Martin Kadlec blogging

Modular injected scripts in extensions

Recently in one of my blog posts I mentioned great library Require.JS. It has an API to load JavaScript snippets as modules. It works really great with popup files or with background file in speed dial extension.

Unfortunately, you can't use it in injected scripts for several reasons.
  1. Require.JS loads the snippets with script tags and we don't want to add any script tags to every page when our extensions is running.
  2. Even if it wouldn't be a problem we still can't load scripts from our extension package with script tag.
  3. Script tags also has it's own browser scope without the special Opera Extension APIs.
The conclusion is that Require.JS is useless when it comes to injected scripts. What we can do is to split our script to several injected script files. But such files have to use global scope to communicate and they are executed in alphabetical order.

Luckily, latest snapshot of Opera 12.50 Next has the new Resource Loader API. It is just one function which returns reference to "File" object of any file in our extension oex package.

You can use it like this:

var file = opera.extension.getFile('some-file.txt');

if (file) {

var myReader = new FileReader();

myReader.onload = function() {
// myReader.result contains the content of selected file


It might be just one function, but it is enough to build a Require.JS-like function for injected scripts! The good news is that I already created alpha version of such script :knight:

It is built from two "layers". The first layer is a second function similar to the "getFile" function, but it helps to save a bit of writing. The previous code with the new function will look like this:

opera.extension.getFileData('some-file.txt', function(result) {
[COLOR=grey]The syntax is: getFileData(path[, type], callback); where type can be 'text' (default), 'arraybuffer', 'dataurl' or 'json'.[/COLOR]

The second layer is the Require.JS-like alternative build on top of this function. Even though I tried to have very similar API there are still lots of differences so you can not rely much on Require.JS documentation.

Let's start with a simple example. Firstly, we have a regular injected script (/includes/include.js):
require(['random-color', '!domReady'], function(randomColor, d) { = randomColor();

The require function loads a file with this path: '/modules/random-color.js'. If you don't want to call your folder with modules 'modules' you can easily change the name:
require._base = '/newname/';

The first example of course wouldn't work without the 'random-color' module itself. We have to create the module (/modules/random-color.js):
define(function() {
return '#' + Math.floor(Math.random() * 16777215).toString(16);

This module is loaded in the include.js file and executed. You can also load simple text file:
require('template.txt!text', function(template) {

In case you need only one text file you should rather use the getFileData function. You can also load the content as !dataurl, !arraybuffer or !json. Note that the script adds only the '.js' extension and only if there is no '!type' part in the name. In case of text files you have to specify the extensions.

Often you will need to run the script only when the DOM is loaded. You can do that with !domReady dependency. I use it already in the first example to make sure document.body is available. The "!domReady" will return document object. Placement of !domReady in the array of dependencies is important. All dependencies after the !domReady dependency will wait with their execution until the DOM is loaded.

Same as in Require.JS you can specify dependencies in modules as well:
define(['dependency1', 'dependency2'], function(d1, d2) {
return d1() + d2.num;

With higher amount of dependencies it might be uncomfortable to list all the names as function attributes. The require function caches all the results so once they are loaded you can get them without callback:
require(['a', 'b', 'c', 'd', 'e'], function() {
var a = require('a');

All scripts are executed in given order. When given file doesn't exists the parameter on its place will contain 'null'.

Modules have its own scope so all variables created in any module will not cause global pollution. However, all global variables created in injected scripts are accessible within every module. If you need to create variable in global scope from module use the "global" object:
global.globalVariable = 1;

Arrrr you interested? :pirate: Then you can download it here:
Development version (very bad commenting, I didn't expect I would make this public :P )
Minified version

It is still an alpha version so there might be some bugs. Please report them here in comments :)

Please login to post comments.
01.04.2013 18:04
That looks so complex lol
19.02.2013 08:02
rof.M.,Sorimangaraja Sitanggang writes:

bangsa yang besar adalah bangsa yang tahu akan sejarah dan budaya