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

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:

Related

Remember array after the popup has closed in a Chrome extension

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.

where is showing conversation value

THis topic is abouton google add word (conversation)
Below is my conversation setup screenshot
http://nimb.ws/alycTQ
Below is my code that was putted on body tag
<script type="text/javascript">
/* <![CDATA[ */
function GoogleFormTracker()
{
goog_snippet_vars = function() {
var w = window;
w.google_conversion_id = 949468534;
w.google_conversion_label = "9xLwCK7rm3IQ9vrexAM";
w.google_conversion_value = 1;
w.google_remarketing_only = false;
}
// DO NOT CHANGE THE CODE BELOW.
goog_report_conversion = function(url) {
goog_snippet_vars();
window.google_conversion_format = "3";
var opt = new Object();
opt.onload_callback = function() {
if (typeof(url) != 'undefined') {
window.location = url;
}
}
var conv_handler = window['google_trackConversion'];
if (typeof(conv_handler) == 'function') {
conv_handler(opt);
}
}
}
/* ]]> */
</script>
<script type="text/javascript"
src="//www.googleadservices.com/pagead/conversion_async.js">
</script>
GoogleFormTracker() fired on footer when site is load.
And also i verified my code on tag manager chrome addons(No error showing there).
but i don't know where to showing me how many time this function is fired ?
let me know any mistake in my code or where is showing tracking value in add word (with screenshot and step by step).
Thanks
In google add word account follow below step
Tool->Attribution
In Attribution available you conversation value.
I hope u need like above
"but i don't know where to showing me how many time this function is fired". Not entirely sure I understand, but perhaps you just need to put a console.log('marco'); inside the function and view the browser console (ctrl + shift + i) to see how many times the function is called?

Reassigning external JavaScript global variable by replacing it with another script in html

I am trying to bypass the error:
Cross origin requests are only supported for protocol schemes: http,
data, chrome, chrome-extension, https.
By including external javascript file containing html data. So what I want to achieve is, to load the html content when a side menu is clicked. In the process, I want the earlier data to be replaced by the new one so that I can replace the old content with the new one.
Below is the main part of the code.
function change_current_doc(clicked_file, doc_src) {
//Change current JavasScript file containing data with the new one.
var prev_doc_src = doc_src;
var prev_doc = doc;
doc_src = html_file.doc_path + '/' + clicked_file.split('.')[0] + '.json';
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.indexOf(prev_doc_src) > 0) {
scripts[i].remove();
var doc_script = document.createElement('script');
doc_script.setAttribute('src', doc_src);
document.head.appendChild(doc_script);
break;
}
}
}
$(document).ready(function () {
$('#st_side_menu').jstree({
"plugins": [
"types",
"unique",
"wholerow",
"changed"
],
"types": {
"default": {
"icon": "images/book.png",
"select_node" : function(e) {
this.toggle_node(e);
return false;
}
},
"moveType": {
"icon": "glyphicon glyphicon-transfer"
}
}
}).on('changed.jstree', function (NODE, REF_NODE) {
//Loads the new file on button click however, it is out of sync, it
//requires to be clicked two times.
var clicked_file = REF_NODE.node.a_attr.href;
if (clicked_file.length < 5){
return
}
console.log(clicked_file);
// Actual changing happens here.
change_current_doc(clicked_file, doc_src);
// data is set here.
$('#doc_container').html(doc[0]);
})
});
My json data in js file.
var doc = ["<!DOCTYPE HTML>\n<html>\n<head>\n <meta content=\"text/html;...];
What I am looking for is, if there is any way to reset the old doc global variable and set the new one without clicking twice.
I didn't need to go through all this long way if I had an option to access the web application from server. I have no option to get away with this as users will access it from a folder.
I found a solution to the problem. What I did was the following.
On the server side, I updated all JavaScript files containing html with a variable name that corresponds to the file name. Then I used a dynamic variable by accessing it through window['document_name']
eg.
var name_of_the_file = ["<!DOCTYPE HTML>\n<html>\n<head>\n <meta content=\"text/html;...];
I created another JavaScript file that contains an array of all files to be included.
eg.
var js_doc_files = ['path/to/name_of_the_file.js', ...];
In the index.html file, I did the following.
function insert_js_docs() {
// Loops through all files and add them in the index.html as a JavaScript
file in the head section of the document.
for (var j = 0; j < js_doc_files.length; j++) {
var doc_script = document.createElement('script');
doc_script.setAttribute('src', js_doc_files[j]);
document.head.appendChild(doc_script);
}
}
// executes the insert here.
insert_js_docs();
$(document).ready(function () {
$('#st_side_menu').jstree({
"plugins": [
"types",
"unique",
"wholerow",
"changed",
"conditionalselect"
],
"types": {
"default": {
"icon": "images/book.png",
"select_node" : function(e) {
this.toggle_node(e);
return false;
}
},
"moveType": {
"icon": "glyphicon glyphicon-transfer"
}
}
}).on('changed.jstree', function (NODE, REF_NODE) {
var clicked_file = REF_NODE.node.a_attr.href;
if (clicked_file.length < 5){
return
}
// get the document name without extension and set it to
// the window object to get the clicked item html data.
var doc_name = clicked_file.split('.')[0].replace(/-/g, '_');
// Set it to the content container div
$('#doc_container').html(window[doc_name]);
})
});
Now it works on single click. Moreover, performance wise it is good. The pages' RAM usage starts around 25MB and reaches a maximum of 75MB, which is good enough.

How do I dynamically change the background.js to reflect a user's setting in chrome?

So I am developing a chrome extension right now in order to get introduced to the world of web development. I have run into a issue which I can't seem to get my head wrapped around. Currently, my background.js gets all the open windows along with the open tabs in each of them using:
function getAllOpenWindows(winData) {
var tabs = [];
for (var i in winData) {
/*if (winData[i].focused === true) {*/
var winTabs = winData[i].tabs;
var totTabs = winTabs.length;
for (var j=0; j<totTabs;j++) {
tabs.push(winTabs[j].url);
}
/*}*/
}
console.log(tabs);
}
which was actually posted by bpatel here. Now as you can see the commented line, it allows me to decide if the tabs are taken from just the current focused window or all the windows. My issue is that how exactly can I change this setting dynamically once the extension has been loaded into chrome and is being used actively? So if a user goes into options and says they want only all the windows I would be able pass a message to my background.js to say "hey use the all windows function and not the current focused window". I understand that 2 seperate functions should ideally be used here, this is just a sample on how it would be done. Thanks a ton!
You can use localstorage to save any user settings.
The options page and the background page share the same localstorage, so you can update the setting from options page and access the value from background page.
options.html
<input type="checkbox" id="windowType" value="All">Check all windows</input>
<input type="button" id="saveSettings" value="Save">Save</input>
options.js
document.getElementById("saveSettings").onclick = save_settings;
function save_settings()
{
var checkAll = document.getElementById("windowType").checked;
localStorage.setItem("check_all_windows",checkAll);
}
You can use a similar function to update the value of the checkbox with the current value, when the options page loads.
background.js
function getAllOpenWindows(winData) {
var tabs = [];
for (var i in winData) {
var checkAll = (localStorage.getItem("check_all_windows") == "true");
if (checkAll || winData[i].focused === true) {
var winTabs = winData[i].tabs;
var totTabs = winTabs.length;
for (var j=0; j<totTabs;j++) {
tabs.push(winTabs[j].url);
}
}
}
console.log(tabs);
}
In background.js, you can get the value of the same key from localStorage and check if it is set to true or false. Remember that the localStorage will store values as strings so you need to compare it with the proper strings - if("false") will evaluate to true.

Javascript from file gives Uncaught ReferenceError

I am trying to dynamically adjust the height of an iFrame on a web page depending on the content within the iFrame via some JavaScript.
My problem is when I have the script directly on the page in a <script> tag it works fine. When I stuff the code in to a separate js file and link to it- it doesn't work!
<iframe id='StatusModule' onload='FrameManager.registerFrame(this)' src='http://randomdomain.dk/StatusModule.aspx'></iframe>
<script type='text/javascript' src='http://randomdomain.dk/FrameManager.js'></script>
It gives me the error:
Uncaught ReferenceError: FrameManager is not defined
Can this really be true? Has it something to do with the page life cycle?
Ps. I guess the JavaScript code is irrelevant, as we not it works.
UPDATE: I think this might have something to do with secure http (https) and the different browsers in some weird way. I noticed that the script actually worked in Firefox. Or rather I'm not sure if its the script, or just Firefox's functionality that resizes iframes automatically depending on the content. It doesn't give me any error though.
If I then add https to the script url reference, the scripts work in IE and Chrome - but not in Firefox. Function reference error! This just got weird!
UPDATE #2: Its not a Firefox function that resizes the iframe. Its the actual script that works (without https).
UPDATE #3: The JavaScript. Works fine if I put it directly into a script tag.
var FrameManager = {
currentFrameId: '',
currentFrameHeight: 0,
lastFrameId: '',
lastFrameHeight: 0,
resizeTimerId: null,
init: function () {
if (FrameManager.resizeTimerId == null) {
FrameManager.resizeTimerId = window.setInterval(FrameManager.resizeFrames, 0);
}
},
resizeFrames: function () {
FrameManager.retrieveFrameIdAndHeight();
if ((FrameManager.currentFrameId != FrameManager.lastFrameId) || (FrameManager.currentFrameHeight != FrameManager.lastFrameHeight)) {
var iframe = document.getElementById(FrameManager.currentFrameId.toString());
if (iframe == null) return;
iframe.style.height = FrameManager.currentFrameHeight.toString() + "px";
FrameManager.lastFrameId = FrameManager.currentFrameId;
FrameManager.lastFrameHeight = FrameManager.currentFrameHeight;
window.location.hash = '';
}
},
retrieveFrameIdAndHeight: function () {
if (window.location.hash.length == 0) return;
var hashValue = window.location.hash.substring(1);
if ((hashValue == null) || (hashValue.length == 0)) return;
var pairs = hashValue.split('&');
if ((pairs != null) && (pairs.length > 0)) {
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if ((pair != null) && (pair.length > 0)) {
if (pair[0] == 'frameId') {
if ((pair[1] != null) && (pair[1].length > 0)) {
FrameManager.currentFrameId = pair[1];
}
} else if (pair[0] == 'height') {
var height = parseInt(pair[1]);
if (!isNaN(height)) {
FrameManager.currentFrameHeight = height;
//FrameManager.currentFrameHeight += 5;
}
}
}
}
}
},
registerFrame: function (frame) {
var currentLocation = location.href;
var hashIndex = currentLocation.indexOf('#');
if (hashIndex > -1) {
currentLocation = currentLocation.substring(0, hashIndex);
}
frame.contentWindow.location = frame.src + '&frameId=' + frame.id + '#' + currentLocation;
}
};
window.setTimeout(FrameManager.init, 0);
UPDATE #4: Alright I did as ShadowWizard and TheZuck suggested:
<script type="text/javascript">
var iframe = document.createElement("iframe");
iframe.src = "http://www.randomdomain.dk/StatusWebModule.aspx";
iframe.width = '100%';
iframe.id = 'StatusModule';
iframe.scrolling = 'no';
if (iframe.attachEvent) {
iframe.attachEvent("onload", function () {
FrameManager.registerFrame(iframe);
});
} else {
iframe.onload = function () {
FrameManager.registerFrame(iframe);
};
}
document.getElementById('framecontainer').appendChild(iframe);
</script>
With HTTP as URL its work on IE and Firefox - not Chrome. If I set it to HTTPS it works on Chrome and IE - Not Firefox. Same error:
"ReferenceError: FrameManager is not defined".
What is going on here?
a couple of things:
I would bet on a race condition when you have two independent
resources which are supposed to be loaded concurrently. You can
easily check this by writing to log (or to document, whichever works
for you) when both finish loading (i.e. add a little script in the
iframe to dynamically add the time to the content or write to log if
you're using chrome, do that in the external script file as well,
and see if they post the time in a specific order when this fails). In your case, if the script appears before the iframe, and you don't mark it as async, it should be loaded before the iframe is fetched, so it would seem strange for the iframe not to find it due to a race condition. I would bet on (3) in that case.
Assuming there is such an issue (and if there isn't now, when you go
out into the real world it will be), a better way to do this is to
make sure both behave well in case the other loads first. In your
case, I would tell the iframe to add itself to a local variable
independent of the script, and would tell the script to check if the
iframe registered when it loads, and after that in recurring
intervals until it finds the iframe.
If the page the script is loaded into is not in the same domain
as the iframe (note that it doesn't matter where the script comes
from, it only matters what the page's domain is), (or even the same
protocol as someone mentioned here), you will not be able to access
the content so you won't be able to resize according to what the
content is. I'm not sure about the onload method, if it's considered part of the wrapping page or part of the internal iframe.
Check out this question, it sounds relevant to your case:
There's also an interesting article here about this.
I think that your frame is loaded before the script, so "FrameManager" does not exist yet when the iframe has finished loading.

Categories