You need to be logged in to post in the forums. If you do not have an account, please sign up first.
LocalStore -> injectscript
Hi,I create a option page (with localStore HTML5) and this work OK.
But now i want to get the settings inside my inject script. I try this in my injectscript.js. But it doesn't work.
if(!localStorage['test'])localStorage['test'] = 'true'; // found nothing then it is true
var test = localStorage[test];
if(test== 'true'){opera.postError('TEST: OK');}
What do i wrong, any idea?
Kind Regards,
Stefan
I have now this, but it doesn't work. It not show the postError (when my option is true).
inside the inject.js file:
if(!localStorage['test'])localStorage['test'] = 'true'; // found nothing then it is true
var test = localStorage['test']; // add the ' quotes
if(test== 'true'){opera.postError('TEST: OK');}
Kind Regards,
Stefan
it get my localstore information from "test". But it doesn't update when i change it in my option page.
Must i place inside my option page also the word "window" ?
Current got this for save and reading of my localstore files.
//options.html
// my save button
function save_options(){
if($('test').checked)localStorage['test'] = 'true';
else localStorage['test'] = 'false';
}
//read by open windows onload
function read_options(){
if(localStorage['test'] == 'true')$('test').checked = true;
}
Default previous post, the "test" is true. But when i change the setting in my option page to false (check off the box). Click save button.
It still show the old value.
Even though you can use localStorage you should preferably use widget.preferences though. From the option.html article (http://dev.opera.com/articles/view/opera-extensions-options-page/):
The widget.preferences interface is exactly the same as that of the localStorage object, except that it doesn't have the same size limitations as localStorage, and widget.preferences default values can be declared in the <preference> elements of your config.xml file, as seen in The preference Element and its Attributes section of the Widget Packaging and Configuration specification. Therefore, when the script in our options.html template sets a preference on the widget.preferences object, a storage event is fired on the active documents of the extension (especially the background process). By listening to the storage events, the background process of your extension can react immediately to any change in the widget.preferences.
But regardless of (window.)localStorage or widget.preferences, you must add a "storage" event handler in your index.html/background.js:
window.addEventListener('storage', onStorage, false);
...
...
// then either: var storage = widget.preferences;
// or: var storage = window.localStorage;
function onStorage(e){
if (e.storageArea !== storage) return;
if (e.key == 'test'){
// 'test' has been updated, so update variables and/or call the functions/methods you need to.
opera.postError('Preference "test" updated: ' + storage['test']);
}
}
Maybe you already have that code, but since I'm not sure I added it in case you don't.
However, you can start using the template given to us by Opera themselves: http://dev.opera.com/articles/view/opera-extensions-options-page/nonsense.oex
Modify it for your needs, no need to touch the javascript in options.html, just modify the html to reflect your preferences (options). The javascript will intelligently take care of setting the preferences that result in "storage" events being sent to the extension background (index.html/background.js/whatever_name), which is handled by the above test code.
2. December 2010, 05:44:30 (edited)
// BACKGROUND SCRIPT
// Singleton object that can store and retrieve arbitrary objects from widget.preferences
var storage = function() {
// This is a list of the names of all settings
this.settings = [
'foo',
'bar',
];
// Gets an object from widget.preferences
this.get = function(name) {
var data = widget.preferences[name];
if (typeof data === 'undefined')
return null;
return JSON.parse(data);
}
// Sets an object in widget.preferences
this.set = function(name, value) {
widget.preferences[name] = JSON.stringify(value);
}
this.isSetting = function(name) {
return settings.indexOf(name) != -1;
}
// Gets all objects listed as settings and returns them as an object
// i.e. var settings = storage.getSettings();
// var foo = settings.foo; OR var foo = settings['foo'];
this.getSettings = function() {
var data = {};
for (var i = 0; i < this.settings.length; i++) {
data[this.settings[i]] = this.get(this.settings[i]);
}
return data;
}
}
// When a script connects, send it the settings object
opera.extension.addEventListener('connect', function(e) {
e.source.postMessage({
action: 'settings',
settings: storage.getSettings(),
});
}, false);
// OPTIONAL: When a setting changes, update all the injected scripts with the new setting
window.addEventListener('storage', function(e) {
// If the update item is not a setting, don't send an update
if (!storage.isSetting(e.key))
return;
opera.extension.broadcastMessage({
action: 'update setting',
name: e.key;
value: storage.get(e.key),
});
}, false);
// INJECTED SCRIPT
// This is where we'll store settings in the injected script
var settings = null;
// Listen to messages from the background script
opera.extension.addEventListener('message', function(e) {
switch (e.data.action) {
// Update the entire settings object
case 'settings':
settings = e.data.settings;
break;
// Update one setting
case 'update setting':
settings[e.data.name] = e.data.value;
break;
}
}, false);
// You can now do stuff with the settings object as long as you check that it's been initialized first.
// i.e. if (settings) {
// do stuff
// }
2. December 2010, 21:20:07 (edited)
You have to provide more information, like fragments from your code, or even the extension itself. It's hard to say what is causing problems without any knowledge of details in your code. For example, I don't even know how you are actually implementing the options page; 1) inside an injected tab, 2) inside the popup, or 3) in options.html. I now, again, think it might be 1 (inside an injected script, somewhat like Lex1's NoAds, more or less) even though I thought it was 3 before (inside options.html, given the fact that you mentioned "options page"). But I'm not sure, so please provide this information:
1. Where does the code that writes to window.localStorage (or preferably, widget.preferences) reside: injected script, popup, or options.html ?
2. Copy and paste the piece of code that writes to the aforementioned storage/preferences object.
Originally posted by eternal1:
@stefanvd
You have to provide more information, like fragments from your code, or even the extension itself. It's hard to say what is causing problems without any knowledge of details in your code. For example, I don't even know how you are actually implementing the options page; 1) inside an injected tab, 2) inside the popup, or 3) in options.html. I now, again, think it might be 1 (inside an injected script, somewhat like Lex1's NoAds, more or less) even though I thought it was 3 before (inside options.html, given the fact that you mentioned "options page"). But I'm not sure, so please provide this information:
1. Where does the code that writes to window.localStorage (or preferably, widget.preferences) reside: injected script, popup, or options.html ?
2. Copy and paste the piece of code that writes to the aforementioned storage/preferences object.
Here the extension (with the above change i make)
TOTL http://turnoffthelights.googlecode.com/files/Turn%20Off%20the%20Lights.oex
Only the autoplay option i am working. When the option is on -> it inject a green div (20px x 20px) on the current webpage. If the option is Off -> a red div.
Kind Regards,
Stefan
In order to get settings from your extension's localStorage (or widget.preferences), you need to pass them from the background script to the injected script in a message. The giant block of code in my last post is the method I use to do that. When an injected script loads, it fires the "connect" event in the background script. The background script now has a way of messaging the injected script, so it collects all the settings it needs to send to the injected script into one object and passes it to the injected script with postMessage. The injected script then gets the message and saves all the settings it was sent.
It looks like you will have to alter your injected script's message handling code so that it can understand different types of messages. postMessage can send JSON objects, so I send an object with the "action" field to say what kind of message it is.
3. December 2010, 02:29:35 (edited)
lol, I'm in hurry so I'm myself gonna gloss over your latest post for now

@stefanvd
You have a save button that re-writes the preferences in one go, instead of immediate update to each individual preference (note the singular). Nothing wrong with this of course. The problems however are enumerated below:
1. You accidentally had prefixed window.localStorage in your save_options() function, with yet another "window.", making: window.window.localStorage. Fix that.
2. You took my sample "storage" listener/handler and check for the key (preference) "test". Thing is you do not set any "test" on the localStorage object from inside options.html. Therefore the check for key "test" inside "storage" listener in index.html will never be true. So for verifying that it works check for "autoplay" instead of "test" in the "storage" listener:
if (e.key == 'autoplay'){
// 'autoplay' has been updated, so update variables and/or call the functions/methods you need to.
opera.postError('Preference "autoplay" updated: ' + storage['autoplay']);
}
3. Since you write all preferences to localStorage object in one go, it's not particularly nice to listen for indicidual storage events. You can of course listen and check for the key matching the storage variable that you set last in options.html. But better is, after you set the last storage variable in options.html then simply send a message to the background script.
3.1. Before "window.close();" in your save_options() in options.html add:
opera.extension.postMessage({action: 'options-updated'});
3.2. In index.html, replace my previous sample "storage" listener with:
function onMessage(e){
switch (e.data.action){
case 'options-updated':
// options have been updated, so update variables and/or call the functions/methods you need to.
opera.postError('options updated !');
break;
}
}
3.3. And lastly, in index.html, change:
window.addEventListener('storage', onStorage, false);
to:
opera.extension.onmessage = onMessage;
sorry, i still got the same problem, (or do i forgot something else?)
see here the new version:
http://turnoffthelights.googlecode.com/files/Turn%20Off%20the%20Lights%20alpha2.oex
Kind Regards,
Stefan
3. December 2010, 17:24:38 (edited)
I assume you are not confident in English. Therefore translations of the dev articles to German and other languages would be welcomed by all. Any volunteers reading this ?

You have embedded "function onMessage(e){...}" inside "function onStorage(e){...}" so now it looks like this:
function onStorage(e){
if (e.storageArea !== storage) return;
function onMessage(e){
switch (e.data.action){
case 'options-updated':
// options have been updated, so update variables and/or call the functions/methods you need to.
opera.postError('options updated !');
break;
}
}
}
Remove above code and put this instead:
function onMessage(e){
switch (e.data.action){
case 'options-updated':
// options have been updated, so:
// * Call any function(s)/method(s) that rely on the options.
// * Post message(s) to injected scripts giving the options that injected scripts rely on.
opera.postError('options updated !');
break;
}
}
Originally posted by eternal1:
I know you are not confident in English, so translations of the dev articles to German and other languages would be welcomed by all. Any volunteers reading this ?
I'm not confident enough in my German to translate something like that, but I can translate my previous posts if you want.
We're starting to get on the right track here. My first post was a giant block of generic code, so here it is again, but simplified and more specific to your extension. The ". . ."'s are where some of your original code goes.
// INDEX.HTML
window.addEventListener('load', function(){
var theButton, ToolbarUIItemProperties = . . .
//You can use this for your button's click event
theButton.addEventListener("click", function(){
// Send the message as a JSON object instead of a string. Normally, I would suggest using focused.postMessage, but there's a bug with iframes that makes that fail sometimes.
var focused = opera.extension.tabs.getFocused();
if (focused)
opera.broadcastMessage({
action: 'lightsoff',
url: focused.url,
});
}, false);
opera.contexts.toolbar.addItem(theButton);
// show welcome page
. . .
// When a script loads, it calls the "connect" event here Then, we send the new script all the extension's settings. Use JSON.parse because a boolean value gets saved as a string
opera.extension.addEventListener('connect', function(e) {
e.source.postMessage({
action: 'settings',
settings: {
autoplay: JSON.parse(localStorage['autoplay'] || 'false')
}
});
}, false);
// When a setting changes, update all the injected scripts with the new setting
window.addEventListener('storage', function(e) {
if (e.storageArea !== localStorage)
return;
// Send a message to all scripts saying which setting changed and what its new value is
opera.extension.broadcastMessage({
action: 'update setting',
name: e.key;
value: localStorage[e.key],
});
}, false);
}, false);
// LIGHT.JS
// In my experience, opera.extension.tabs.getFocused().url strips everything after the #, so I do the same here.
var url = window.location.href.replace(/#.*$/, '');
// This is where we'll store settings in the injected script. localStorage in light.js and localStorage in index.html are NOT the same object.
var settings = null;
// Listen to messages from the background script
opera.extension.onmessage = function(e) {
switch (e.data.action) {
// Update the entire settings object
case 'settings':
settings = e.data.settings;
break;
// Update one setting
case 'update setting':
settings[e.data.name] = e.data.value;
break;
case 'lightsoff'
if (!settings || if (url == e.data.url) {)
return;
var autoplay = settings['autoplay'];
if(autoplay == 'true'){
. . .
} else {
. . .
}
break;
}
}
. . .
where i can start to translate the dev article for the Dutch users.
@spadija
I try to you code, look easy now

but... now i see not my lamp button.
see here new version:
http://turnoffthelights.googlecode.com/files/Turn%20Off%20the%20Lights%20alpha3.oex
3. December 2010, 21:01:08 (edited)
Originally posted by spadija:
I'm not confident enough in my German to translate something like that, but I can translate my previous posts if you want.
Not for me. I thought stefanvd was German so that was the reason I asked anyone reading if he/she wants to volunteer for translation of extension related articles. There are always developers who cannot follow articles (proprely) due to them not being confident with English.
Originally posted by stefanvd:
@eternal1
where i can start to translate the dev article for the Dutch users.
I'm not sure what you mean, and though I'm presuming you already know where the articles are, here's the link just in case: http://dev.opera.com/articles/extensions/
If I misunderstood you let me know.
Edit:
Oh I think you meant to translate and upload to dev.opera.com. In that case I have no idea. Opera Software can enlighten us if it is possible and in that case how. But regardless, there's always the possiblity to translate and post in your my-opera blog, and add links in your signature. :|
Originally posted by eternal1:
I'm not sure what you mean, and though I'm presuming you already know where the articles are, here's the link just in case: http://dev.opera.com/articles/extensions/
If I misunderstood you let me know.
Edit:
Oh I think you meant to translate and upload to dev.opera.com. In that case I have no idea. Opera Software can enlighten us if it is possible and in that case how. But regardless, there's always the possiblity to translate and post in your my-opera blog, and add links in your signature. :|
I want to translate few english article (http://dev.opera.com/articles/extensions/) to the language -> Dutch.
Thanks!! i found the link how to start translate the extension article.
@spadija
waiting...
Originally posted by stefanvd:
Thanks!! i found the link how to start translate the extension article.
It's here for anyone else interested: http://dev.opera.com/articles/info/
There were a few syntax errors keeping things from working (like that semicolon after e.key that I put instead of a comma). I also moved the code that happens when the script gets a "lightsoff" message into another function to make the code easier to read.
Try replacing the current "connect" handler with this.
function postSettings(listener) {
listener.postMessage({
action: 'settings',
settings: {
autoplay: ...
}
});
}
opera.extension.addEventListener('connect', function(e) {
postSettings(e.source);
}, false);
opera.extension.tabs.addEventListener('focus', function(e) {
var focused = opera.extension.tabs.getFocused();
if (focused)
postSettings(focused);
}, false);
4. December 2010, 14:31:48 (edited)
This doesn't necessarily relate to your problem. Anyway, since I remember it I thought mentioning: in your options.html (in the html markup) I remember that when you called save_options, you immediately also called _gaq(){} (some google function I think ?):
<SOME_ELEMENT_I_DONT_REMEMBER_WITCH onclick="save_options();_gaq(...);">
There exists however no _gaq(){} in the context of options.html.
Anyhow, if you always get the red rectangle, then something is wrong in your injeced script, just like there were wrongs in your options.html and index.html. In my humble opinion you really need to have a basic understanding of:
1. Messaging.
2. localStorage / widget.preferences.
When you know what you are doing then you won't have all these problems.
Originally posted by eternal1:
@stefanvd
<SOME_ELEMENT_I_DONT_REMEMBER_WITCH onclick="save_options();_gaq(...);">
that is the Google analytic code.
Opera support localstore, so i use my Chrome option page (that use localstore for save the settings).
It work on Chrome so normal no change must make on the options.html page, only the problem is that it use back the default value when i refresh the page.
In light.js, you have "if(autoplay == 'true')", and the code I gave you for index.html used "autoplay: JSON.parse(localStorage['autoplay'] || 'false')". JSON.parse('true') returns a boolean true instead of the string 'true', and true != 'true'. Either change light.js to use "if(autoplay == true)", or change index.html to "autoplay: localStorage['autoplay'] || 'false'".
There's also a bug in messaging at the moment where opera.extension.tabs.getFocused().postMessage() will post to an iframe in a page instead of the main page itself. I forgot about that with that last bit of code. If you use this instead, it will work around the bug, but it's less efficient.
function getSettings() {
return {
autoplay: JSON.parse(localStorage['autoplay'] || 'false'),
};
}
opera.extension.addEventListener('connect', function(e) {
e.source.postMessage({
action: 'settings',
settings: getSettings()
});
}, false);
opera.extension.tabs.addEventListener('focus', function(e) {
// Avoid the messaging bug by updating all scripts' settings
opera.extension.broadcastMessage({
action: 'settings',
settings: getSettings()
});
}, false);
Now that I think about it, you could pass the settings with the "click" message to avoid updating every script's settings all the time, but I don't have time right now to test that.
Forums » Dev.Opera » Opera Extensions Development Discussions