Labs/JS Modules

< Labs
Revision as of 21:57, 19 June 2008 by Varmaa (talk | contribs) (Typo fix)

Back to Labs.

JavaScript Modules

Here you'll find a collection of modules which you can import into your extension. The goal is to make extension development easier by implementing common functionality as reusable libraries.

The modules here are designed to be imported with Components.utils.import(). To avoid namespace collisions with core code and other extensions, use the two-parameter form of import() to import the module's symbols into a namespace defined by your extension, i.e.:

let MyExtension = {};
Components.utils.import("resource://myextension/modules/SomeModule.js", MyExtension);

If you would like to contribute a new module, get in touch with us at #labs!

Logging

This is a partial implementation of the Log4* interfaces (for example, see log4j or log4net). The original implementation came from Michael Johnston, but it was heavily modified by Dan Mills to get it into Weave.

To use it, create one or more appenders while initializing your add-on, and create loggers in the places where you have logging messages to emit. Both loggers and appenders have minimum log levels to allow you to customize your logging output without changing any code. For example:

function setupLogging() {
  // The basic formatter will output lines like:
  // DATE/TIME	LoggerName	LEVEL	(log message) 
  let formatter = Log4Moz.Service.newFormatter("basic");

  // Loggers are hierarchical, lowering this log level will affect all output
  let root = Log4Moz.Service.rootLogger;
  root.level = Log4Moz.Level["All"];

  // A console appender outputs to the JS Error Console
  let capp = Log4Moz.Service.newAppender("console", formatter);
  capp.level = Log4Moz.Level["Warn"];
  root.addAppender(capp);

  // A dump appender outputs to standard out
  let dapp = Log4Moz.Service.newAppender("dump", formatter);
  dapp.level = Log4Moz.Level["Debug"];
  root.addAppender(dapp);
}

// ... in some other code, but after setupLogging() has been done

// Get a logger, give it a name unique to this chunk of code.
// Use dots to create a hierarchy, this way you can later change
// the log level of sets of loggers under some common root
let logger = Log4Moz.Service.getLogger("MyExtension.MyClass");
logger.level = Log4Moz.Level["Debug"];

// Log some messages
// Given our settings, the error would show up everywhere, but the
// debug one would only show up in stdout
logger.error("Oh noes!! Something bad happened!");
logger.debug("Details about bad thing only useful during debugging");

The implementation is much less comprehensive than what you might see in the log4* projects at Apache, however. For example, support for serializing the logging configuration or reading it in is non-existent. Also, note that this module used to be an XPCOM component and that influenced some of the interface design (e.g., Service.newAppender() instead of making a new Appender() directly). That will change in the future.

Get it here:

Preferences

The Preferences module provides an API for accessing application preferences. Getting and setting prefs is easy:

let foo = Preferences.get("extensions.test.foo");
Preferences.set("extensions.test.foo", foo);

As with FUEL's preferences API, datatypes are auto-detected, and you can pass a default value that the API will return if the pref doesn't have a value:

let foo = Preferences.get("extensions.test.nonexistent", "default value");
// foo == "default value"

Unlike FUEL, which returns null in the same situation, the module doesn't return a value when you get a nonexistent pref without specifying a default value:

let foo = Preferences.get("extensions.test.nonexistent");
// typeof foo == "undefined"

(Although the preferences service doesn't currently store null values, other interfaces like nsIVariant and nsIContentPrefService and embedded storage engines like SQLite distinguish between the null value and "doesn't have a value," as does JavaScript, so it seems more consistent and robust to do so here as well.)

Because we aren't using XPCOM, we can include some interesting API features. First, as you may have noticed already, the interface doesn't require you to create a branch just to get a pref, but you can create one if you want to via an intuitive syntax:

let testBranch = new Preferences("extensions.test.");
// Preferences.get("extensions.test.foo") == testBranch.get("foo")

The get method uses polymorphism to enable you to retrieve multiple values in a single call, and, with JS 1.7's destructuring assignment, you can assign those values to individual variables:

let [foo, bar, baz] = testBranch.get(["foo", "bar", "baz"]);

And set lets you update multiple prefs in one call (although they still get updated individually on the backend, so each change results in a separate notification):

testBranch.set({ foo: 1, bar: "awesome", baz: true });