Teach Yourself D3.js

D3 is a JavaScript framework used for data visualization.
Learning how to work with D3.js is very easy. If you begin with the basics, you’ll see how easy it is to use D3, because:

  • the DOM function names are much shorter than those in the legacy DOM library.
  • the same functions are used for both setting and getting values.
  • setters return a value, so you can keep working with the same element without repeating its name.

If you already know some framework, you probably feel that there’s nothing new under the sun, which is good because it makes learning the new stuff easier.

So, What’s New?

In D3, you can:
* add and remove SVG and HTML elements using the setters and getter.
* set and get the element’s properties, attributes, styles, text and… data

Getting Started

First, download the framework from http://d3js.org
After you include it in your script, you can use selections, that is wrappers for elements.
To get a selection, use one of the functions:

  • d3.select(selector) – to select the first element matching selector.
  • d3.selectAll(selector) – to select the all elements matching selector.

Like the d3 object, selections has their select and selectAll methods to select descendant.

If selection is a selection, it’s easy to understand what selection.text(), selection.attribute(), selection.style(), selection.insert(), and selection.append() do. But, what does selection.data() do?

Data And Update Selection

The selection’s method data() helps you use the HTML DOM as a database.
selection.data() returns an array of all data that belongs to the elements of the selectionץ The data is held in the property __data__ of the element.
selection.data(arr, func) – defines an update selection and distribute the elements of array arr to the elements of selection.
func(d,i)is a function that:

  • receives d an element of arr and its index i.
  • returns a record key.

If func is not passed, the key is the element’s index.

A recommended way to create an update selection is:
var updSelection = selection1.selectAll(selector).data(arr)

Now, updSelection is a selection of existing elements whose data has been changed.
In addition, updSelection has to methods:

  • updSelection.enter() – returns place holders for elements to be created under selection1.
  • updSelection.exit()– returns elements that have lost their data, and are usually to be removed.

Using the data

The methods attr, style, property, text and data can accept a function as a value to set. The function will be called for each element of the selection’s data. And its arguments will be the array element and its index.

Now, let us see it in an example.
The following code arranges data in a donut layout::

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Testing Pie Chart</title>
<script type="text/javascript" src="d3.min.js"></script>

<style type="text/css">
.slice text {
font-size: 12pt;
font-family: Arial;
}
</style>
</head>
<body>
<script type="text/javascript">
var width = 300, 
    height = 300, 
    radius = 100, 
    color = d3.scale.category20c(); //builtin range of colors


// Step 1: creating a container for the donut chart, and attach raw data to it.
var vis = d3.select("body")
            .append("svg")     //create the SVG element inside the <body>
            .datum([{"label":"first", "value":20},
                    {"label":"second", "value":10},
                    {"label":"third", "value":30},
                    {"label":"fourth", "value":25}])       
            .attr({"width": width, "height": height})  
            .append("g")
            .attr("transform", "translate(" + radius + "," + radius + ")") //move the center of the pie chart from 0, 0 to r, r



// Step two: defining the accessors of the layour function. 
//           The function 'pie' will use the 'value' accessor to compute the arc's length.
var pie = d3.layout.pie() 
            .value(function(d) { return d.value; })
            .sort(function(a,b){return a.label<b.label?-1:a.label==b.label?0:1;});

// Step 3: Create the update selection. 
var updateSelection = vis.selectAll("g") 
                         .data(pie);    // This will use the function 'pie' to create arc data from the row data attached to the visualisation element.


// Step 4: Using the data in the update selection to create shape elements.
var arc = d3.svg.arc() 
            .outerRadius(radius)
            .innerRadius(radius / 2)
;
updateSelection.enter()
               .append('g')
               .attr('class','slice')
               .append("path")
               .style("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
               .attr("d", arc);                                     // Create the arc from the arc data attached to each element.

updateSelection.append("text") 
               .attr("transform", 
                     function(d) { 
                       return "translate(" + arc.centroid(d) + ")"; 
                     })
               .attr("text-anchor", "middle")                  
               .text(function(d, i) { return d.data.label; }); 


</script>
</body>
</html> 

And following is the chart:
enter image description here

For more funcions, see the API Reference.

Advertisements

Static Variables in JavaScript

How do you declare a static variable in a JavaScript function?
Not with the word “static“.
In JavaScript “function” is a variable type similar to “object“.
The difference is that the reference to a function inside itself is not this but arguments.callee.
An example of using arguments.callee is the following function that returns itself:

function func(){
    return arguments.callee;
}

Adding Static Variables

You can add arguments to arguments.callee. To initialize it the first time, check first if it is not defined. For example:

if (typeof(arguments.callee.myStaticVar)!="undefined")
    arguments.callee.myStaticVar=0;

Following is an example function in [rhino(http://rhino.org) JavaScript(run from the command line):

    function _example(){
        var thisFunction = arguments.callee;
        if (!thisFunction.static){
            thisFunction.static=0;
        }
        ++thisFunction.static;
        print("This function has been called: " + thisFunction.static + " times");
    };

In this example, you can change the static variable’s value without calling the function using _example.static = "some value";.

To prevent this, encapsulate your function using a function called once, for example:

(function(){
    function _example(){
        var thisFunction = arguments.callee;
        if (!thisFunction.static){
            thisFunction.static=0;
        }
        ++thisFunction.static;
        print("This function has been called: " + thisFunction.static + " times");
    };

    example=function(){
        _example();
    }
})();

Now, each time example() is called, it will increment the variablestatic, but the variable cannot be incremented without calling example because _example is private.

Written with StackEdit.

HTML5 Canvases & Transforms

Browsers supporting HTML5 allow you to draw on the browser’s screen without preparing an image file before. Drawing on a canvas is done using the wonderful Javascript language. If you want to draw a 2-dimensional image on canvas element ‘cnv’, get the drawing context using:

var ctx = cnv.getContext("2d")

And use that context to draw everything using the standard functions:

moveTo, lineTo, arc, fillRect, etc.

Learn more about drawing here.

You can use the functions to create more complicated shapes easily thanks to transform functions:

Transformations: Linear Algebra Made Easy

The origin(0,0) of the canvas is defined to be the top-left pixel of the canvas element. And the coordinates are given in number of pixels. This can change by using transforms.

The transformation functions are:

  • scale(x,y): this will make every shape x times wider and y time taller.
  • translate(x,y): now coordinate (0,0) will be shifted x units to the right, and y units down.
  • rotate(angle): will rotete the axes by given angle in radians.
  • transform(a,b,c,d,e,f): If you want to use a transformation matrix
  • setTransfrom(a,b,c,d,e,f): reset all transform, and perform transform(a,b,c,d,e,f).

a,b,c,d,e,f are values in the matrix:

Transform Matrix
The values a,b,c,d are used for rotating and scaling. e,f for translating.

Other useful methods of the context are:

  • ctx.save() – to save current transform in a stack (Last In First Out).
  • ctx.restore() – to retrieve the trnasform from the top of the stack.

An Example – The Koch Snowflake

The algorithm for drawing the Koch Snowflake can be found in the post Drawing The Koch Snowflake Fractal With GIMP.

Here’s an example in Javascript:

        function drawSide(ctx, len){
          if (len > 1) {
            var thirdOfLen = len / 3.;

            var rotationAngles = [0, -Math.PI / 3, 2 * Math.PI / 3., -Math.PI / 3];

            rotationAngles.forEach(function(val){
              
              if (val != 0){
                ctx.translate(thirdOfLen, 0);
                ctx.rotate(val);
              }
              ctx.save();
              drawSide(ctx, thirdOfLen);
              ctx.restore();
            });

          } else {
            ctx.moveTo(0,0);
            ctx.lineTo(len,0);
            //ctx.stroke();
          } 
        }

        ctx.translate(startX, startY);
        for (i=0; i<3; i++){
          ctx.save();
          drawSide(ctx, sideLength);
          ctx.restore();
          ctx.translate(sideLength,0);
          ctx.rotate(2*Math.PI/3);
        }
        ctx.stroke();
      }

Warning: using ctx.stroke() after every little line you draw might make your code inefficient, and slow down your browser.

Transformation functions are also part of the Processing language.

Processing: Sketches – Your Multi-Programs

When you start Processing, you get an editor with a sketch name. You will probably want another name for your program, a name that means something. So, to change the name generated by the tool, you choose file->save as. Choose a name, and the tool will create a folder with that name, and a file named <name>.pde.

When you run your sketch – by clicking the button with the triange or selecting ‘sketch->Run …’ (Run In Browser, Run on Device, Run In Emulator, Run, etc. Depending on the mode) – Processing will parse the ‘.pde’ files and the program used for running will create an instance each time you run it.

Setup and Draw

If you want have things to do upon initiation of your program, define a function named ‘void setup()‘.  To size your canvas call size(width, height). It is recommended to use ‘size’ only once in the setup function, and the whole sketch. A setup command is not called in run time. It is recommended to set the frame rate inside the setup function by calling ‘frameRate(rate)‘, the rate is the number of time the function ‘draw()‘ is supposed to be call every second. If you want an action to take place each time a frame changes, write a “draw” function in one of  your ‘.pde’ files.

More Than Just Pure Processing

Sometimes, pure Processing is not enough for your application. For example, sound functions do not exist in pure Processing. There are libraries, such as Maxim,  that extend Processing.

Another reason to use more than pure Processing is will to use native widgets (HTML elements, Android Menus & Dialogs, etc.). In pure Processing, you would have to draw the button and detect in the ‘mouseClicked‘ function if the mouse pointer is inside the button.

Here’s a little video example of using the android menu to change the number of polygon sides:

In addition, I’ve created a Javascript module, and added buttons to the HTML page displayed when running in Javascript mode.

The following sections will explain how to do it.

 The Common Part: Just Draw a Polygon

The sommon part that draws the polygon is found in a ‘.pde’ file in the ‘polygon’ sketch directory. The function ‘draw’ detects changes in the global variable n, the number of sides.

The functions ‘translate’ and ‘rotate’ make drawing rotated shapes and lines easier.

In addition, the module contains a call to a function named ‘alert’. This function exists in Javascript, so I’ve made another in the Android module using Toasts.

Following is the code:

// This program draws a regular polygon with n sides.
int n;
float halfVertexAngle; // Will be used for moving from the center
                      // of the sreen to a vertex of the polygon.
                                          
float rotateAngle;     

void set_no_of_sides(int inputN){
  if (inputN < 3){
    alert("Polygons must have at least 3 sides. Try again");
    return;
  }
  n=inputN;
  halfVertexAngle = radians(180) / n; 
  rotateAngle = radians(360./n);
}

void setup(){
  //size(640,480);
  set_no_of_sides(8);
  background(0xffffff00);
  strokeWeight(1);
}

void draw(){
    background(0xffffff00);

  // Center coordinates of the screen and circumbscribed circle.

  int centerX = width / 2;
  int centerY = height / 2;
  
  
  float radius = min(width / 2, height/2);  // The radius of the circumscribed circle.
 
  // Use the cosine theorem to find the length of a side. 
  float side = sqrt(2 * radius * radius * (1 - cos(2 * halfVertexAngle)));
  
  translate(centerX, centerY);  // Move the origin of axes to the center of the screen.
  rotate (-halfVertexAngle);    // Rotate the axes to find the first vertex without
                                // dealing with trigonometry.
  translate(0, radius);
  rotate(halfVertexAngle);      // Rotate back to make the first side horizontal.
  
  for (int i=0; i<n; i ++){
    line(0,0,-side,0);
    translate(-side,0);
    rotate(rotateAngle);
  }
}

The Android Module

I found that in my Linux system Android files are created in the ‘/tmp’ directory. Processing Android mode generates an extension of the class PApplet from your pde files. The good news is that PApplet extends the android Activity class, which makes adding event handlers easy.

Additional functions to a class extending PApplet should be written in a separate pde file. Following is the extension’s code:

import android.os.Bundle;
import android.widget.Toast;
import android.view.Menu;
import android.view.MenuItem;
import android.app.AlertDialog;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.content.DialogInterface;

AlertDialog.Builder alertDialog=null;

public void alert(String text){
  Toast toast=Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG);
  toast.show();
}

public void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  
}

 public boolean onCreateOptionsMenu (Menu menu){
   super.onCreateOptionsMenu (menu);
   menu.add(Menu.NONE, 37, Menu.NONE, "Change Number of sides");
   return true;
 }
 
 private void createAlertDialog(){
     // Create a dialog to change the number of polygon sides.
  alertDialog = new AlertDialog.Builder(this);
  alertDialog.setTitle("Polygon Settings");
  alertDialog.setMessage("Enter number of polygon sides:");
  final EditText input = new EditText(polygon.this);
  LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                                    LinearLayout.LayoutParams.MATCH_PARENT,
                                    LinearLayout.LayoutParams.MATCH_PARENT);
  input.setLayoutParams(lp);
  alertDialog.setView(input);
  alertDialog.setPositiveButton("Accept",
    new DialogInterface.OnClickListener(){
      public void onClick(DialogInterface dialog, int which){
        int numberParsed;
        try {
          numberParsed = Integer.parseInt(input.getText().toString());
          set_no_of_sides(numberParsed);
        } catch (NumberFormatException ex){
          alert("Not an integer. Try again.");
        }
      }
    });

  alertDialog.setNegativeButton("Cancel",
    new DialogInterface.OnClickListener(){
      public void onClick(DialogInterface dialog, int which){
      }
    });

 }
 
 public boolean onMenuItemSelected(int featureId, MenuItem menuitem){
   super.onMenuItemSelected(featureId, menuitem);
   switch (menuitem.getItemId()){
     case 37:
       createAlertDialog();
       alertDialog.show();
       break;
     default:
       break;
   }
   return true;
 }

Notes:

  • If you have an Android module, but you want to run in another mode, renaming the Android mode’s pde file with a ‘.bak’ extension is recommended.
  • If the Android module is not installed, you can install it from a Processing editor window, by selecting <mode button> -> Add more…

Javascript Functions & Changes to the HTML Page

You can change the appearance of your output HTML page by editing the file <sketch-name>/template/template.html . If the file and directory do not exist yet, you can create them by choosing “Javascript->Start Custom Template”. If they do exist use “Javascript-.Show Custom Template”.

For example, I’ve added a button in the HTML template as follows:

<input type="button" value="Change Number of Sides" onclick="get_no_of_sides(this);" />

In addition, I’ve added an ‘onload’ event to change the size of the canvas after the polygon object is created. This function works fine in my favorite browser, Firefox. Fortunately, from ‘.js’ module one can call ‘size’ to resize the canvas:

    <body onload="new_size(640, 480);">

From this page you can learn about accessing Processing from Javascript.

Following is my javascript code defined in ‘js_functions.js’:

function new_size(w, h){
  var divElement=document.getElementById('content');
  divElement.addEventListener("DOMSubtreeModified", function(event, func){
    if (window.Processing.getInstanceById("polygon")){
      divElement.removeEventListener("DOMSubtreeModified", arguments.callee);
      divElement.style.width = w + "px";
      window.Processing.getInstanceById("polygon").size(w,h);
    }
  });

function get_no_of_sides(obj){

  var k=prompt("How many sides?");
  var num=Number(k);
  if (isNaN(num) || num != parseInt(k)){
    alert("Please type an integer");
    return;
  }

  this.Processing.getInstanceById('polygon').set_no_of_sides(num);
}

Now, when you run your application, the product will be found in ‘<sketch-name>/web-export’.
Learn more about Javascript modules here.

The Processing Language…

Processing is a programming language used mainly for animations and graphical applications. If your program is written in pure Processing, the same code can be run in many tools that use their own language: Windows with Java applets, HTML pages with Javascript, Android applications, etc. Running an application  is done by choosing a mode from the Processing GUI tool.

Here’s a screen shot of a Processing Window containing a program to draw a regular polygon:

processingGUI
Drawing a Polygon

Now, to run the program , push the “Play” button, or choose Sketch->Run, and you’ll see the result:

regularPolygon
The polygon in a Java applet.

Now, to run this on Android, I commented out the call to the function ‘size’, following is the result on in an emulator window:

polygonInAndroid
Polygon in Android

You can add to your programs command specific to the mode in which you run. For example you can  add an ‘alert’ command in Javascript mode. Better do it in separate files.

You can download Processing here.

To learn more about pure Processing function go to the function reference.

 

Adding Java Classes to Rhino JavaScript

In the post LibreOffice Javascript, I wrote about Rhino Javascript, which is a Javascript interpreter written in Java. This tool has been developed by Mozilla. With this tool you can instantiate Java classes and to access them via Javascript commands ‘importClass’ and ‘importPackage’.

How to ass classes and packages you can load ?

This is not too hard: the ‘rhino’ command is a shell script. In linux, you can find it using the command:

which rhino

You’ll see the response:

/usr/bin/rhino

Then you can look into the file with a text editor or the ‘more’ command, and see that this is a script that performs a Java class. Jar files and classes that a Java programs uses are found in the environment variable ‘CLASSPATH” or after the directive ‘-classpath’. In this script you’ll find that the class path the content of the variable ‘JAVA_CLASSPATH’.

I decided to add the ‘Tidy’ package. In my computer the path of this package is ‘/usr/share/maven-repo/net/sf/jtidy/jtidy/debian/jtidy-debian.jar’, so the script looks like:

#!/bin/sh

JAVA_CMD=”/usr/bin/java”
JAVA_OPTS=””
JAVA_CLASSPATH=”/usr/share/java/js.jar:/usr/share/java/jline.jar:/usr/share/maven-repo/net/sf/jtidy/jtidy/debian/jtidy-debian.jar
JAVA_MAIN=”org.mozilla.javascript.tools.shell.Main”
export LD_LIBRARY_PATH=/home/amity/myWs
##
## Remove bootclasspath overriding for OpenJDK since
## it now use a mangled version of Rhino (in sun.org.mozilla.rhino package)
##
## References:
## <https://bugs.launchpad.net/ubuntu/+source/openjdk-6/+bug/255149&gt;
## <http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=179&gt;
## <http://www.openoffice.org/issues/show_bug.cgi?id=91641&gt;
##

$JAVA_CMD $JAVA_OPTS -classpath $JAVA_CLASSPATH $JAVA_MAIN “$@”

BTW, in Window, environment variables are enclosed by ‘%’ signes. For example: ‘%JAVA_CLASSPATH%’.

How To Develop Firefox Extensions – Files And Directories (Actually Coding It)

In the previous post, How To Develop Firefox Extensions – Intro, we discussed the Chrome document’s DOM. In this post, we’ll learn how to create an add-on that can be installed.  In this post, we’ll learn how to access the Chrome document’s nodes, and to organize an XPI (Cross Platform Installer) file.

XPI files are ZIP files

A file having an ‘.xpi’ suffix, is a compressed file in ‘.zip’ format. If we use Firefox as the opening program for those files, it will install an extension. We can refer to a zipped file as a root directory, containing files and sub-directories.

Following is a recommended directory structure:

  • /
    • install.rdf
    • chrome.manifest
    • content/
      • <filename>.xul
      • <filename>.js
    • locale/
      • <language code>/
        • <filename>.dtd
      • <language code>/
        • <filename>.dtd
      • .
      • .
      • .
    • skin/
      • <filename>.css

The XUL File

The XUL file is located in the content directory. XUL is an acronym for *XML User-interface Language’. The XUL file is a data file that makes the development of Mozilla extensions easier. The following example is an Overlay XUL file, i.e. an XUL used for adding a component to the browser’s Chrome, the great UI container.

<?xml version="1.0"?>
<!DOCTYPE overlay SYSTEM
  "chrome://imageinnewtab_extension/locale/browserOverlay.dtd">

<overlay id="imageinnewtabextensionOverlay"
         xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

 <script src="imageinnewtab_extension.js">
 </script>

    <menupopup id="contentAreaContextMenu"  >
        <menuitem id="context-imageinnewtab"
                  label="&phpandmoreimageinnewtab.image-new-tab.label;"
                  oncommand="ImageInNewTab.BrowserOverlay.openInNewTab(event)"
                  insertafter="context-viewimage"
                  hidden="true"
                 />
    </menupopup>

</overlay>

This overlay contains the required data for adding an “Open Image In New Tab” option to the context-menu that pops up when you right-click in the HTML document area. This file contains a link to a script and instructions to add a new menu item to the popup menu with the unique id ‘contentAreaContextMenu’.

Note that the value of the label attributes begins with an ampersand(‘&’) and ends with a seni-colons; this means that the value is variable. The actual value is translated according to the definitions found in the DTD file specified in the ‘!DOCTYPE’ tag.

The ‘oncommand’ attribute defines how Firefox should response to command events, i.e. the function called when the menu item is selected either by a mouse click or by a keyboard shortcut.

Learn more about XUL here.

The Javascript file

In this example, ‘content/imageinnewtab_extension.js’, the Javascript file, defines an object that response to the ‘command’ event. The object is initialized as a response to the “load” event, i.e. after the UI components has completed loading. When the Chrome document has completed loading, the new menu item is defined. The ‘init’ member is a function that defines what callback function to call when the context menu pops up.

Following is the code:

if ("undefined" == typeof(ImageInNewTab)) {
  var ImageInNewTab = {};
};

ImageInNewTab.BrowserOverlay = {
    hideIfNoImage: function(event){
      var element=document.popupNode;
      var whatWasClicked = document.getElementById("context-imageinnewtab");
      whatWasClicked.hidden = !(element instanceof Components.interfaces.nsIImageLoadingContent &&
                 element.currentURI);
    },

    init: function (event){
        var contextMenu = document.getElementById("contentAreaContextMenu");
        contextMenu.addEventListener("popupshowing", ImageInNewTab.BrowserOverlay.hideIfNoImage, false);
    },

    openInNewTab: function (event){
      gBrowser.addTab(document.popupNode.src);
    }

}

addEventListener("load",
                 ImageInNewTab.BrowserOverlay.init,false);

The Install RDF file

The ‘install.rdf’ file includes information for the browser. The information includes information shown to the installing end-uder, and for the browser to know if the extension supports the current browser version.

Following is an example:

<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
    <Description about="urn:mozilla:install-manifest">
        <em:id>imageinnewtab_extension@phpandmore.net</em:id>
        <em:name>Image in New Tab</em:name>
        <em:version>0.1</em:version>
        <em:description>Add a new Menu Item in the context menu to view an image in a new tab.</em:description>
        <em:creator>Amit Yaron</em:creator>
        <em:homepageURL>https://phpandmore.net/</em:homepageURL>
        <em:type>2</em:type> <!-- type=extension --> 

        <em:targetApplication>
           <!-- Firefox -->
           <Description>
                <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
                <em:minVersion>3.5</em:minVersion>
                <!-- Since Firefox's numbering scheme is unpredictable, users
                are encouraged to edit this field to the current max value.
                -->
                <em:maxVersion>999.0.*</em:maxVersion>
           </Description>
        </em:targetApplication>
    </Description>
</RDF>

Note: don’t change the ’em:id’ tag value.

Learn more about the install.rdf here.

The Chrome Manifest

The ‘chrrome.manifest’ file is a simple text files that contains instructions on how to access Chrome URL addresses (beginning with ‘chrome://’.

Following is an example manifest:

content imageinnewtab_extension content/
skin imageinnewtab_extension classic/1.0 skin/
locale imageinnewtab_extension en-US locale/en-US/
locale imageinnewtab_extension he-IL locale/he-IL/

overlay chrome://browser/content/browser.xul chrome://imageinnewtab_extension/content/imageinnewtab_extension_overlay.xul
  • The ‘content ‘ instruction tells Firefox where to find ‘chrome://imageinnewtab_extension/content’, where the XUL and code are found.
  • The ‘skin ‘ instruction tells Firefox where to find ‘chrome://imageinnewtab_extension/skin’, where the CSS files are found.
  • The ‘locale ‘ instruction tells Firefox where to find ‘chrome://imageinnewtab_extension/locale’ according to the ‘LANG’ environment variable, This is where the DTD file, used for label translation, is found.
  • The ‘overlay’ instruction tells Firefox the role of the overlay XUL file, which extends the great UI container.

Learn more about the manifest here.

The Example Add-on

The example add-on, that opens an image in a new tab, can be downloaded here.