Eli Grey

RetargetMouseScroll

Yesterday I made a simple JavaScript micro-library named RetargetMouseScroll that provides a simple API to retarget/redirect mouse scroll events. I named it this because RedirectMouseScroll sounded weird. At first glance it seems pretty useless but there are some cool things you can do with the library which are listed in the examples section of this post.

Demo

The test suite functions as a great demo.

Usage

RetargetMouseScroll(
    element         :Node,    // default: document
    targetWindow    :Window,  // default: window
    preventDefault  :Boolean, // default: true
    scrollMultiplier:Number   // default: 1.0
)

RetargetMouseScroll returns an object containing a restore method. Calling the method restores the default scrolling.

Examples

  • RetargetMouseScroll(myElement, myFrame) – Per-element mouse scroll retargetting to a frame
  • RetargetMouseScroll(document, window, true, 0.5) – Slow down scrolling
  • RetargetMouseScroll(document, window, true, -1) – Invert scrolling
  • RetargetMouseScroll(document, window, true, 2) – Speed up scrolling

More advanced example using a popup:

var win = window.open("/", "example", "w=" + (screen.availWidth / 3.5)
                                      + ",h=" + (screen.availHeight / 3.5)
                                      + ",scrollbars=yes,resize=yes");
win.addEventListener("DOMContentLoaded", function() {
    var retargetting = RetargetMouseScroll(document, win);
    win.onunload = function () {
        retargetting.restore();
    };
}, false);

In the previous example, all mouse scrolling on the main document is retargetted to the popup until it is closed.

GTranslatifier

I have created a very simple Jetpack feature called GTranslatifier that adds a Google Translate icon to Firefox’s status bar for easy access to Google Translate services. Upon clicking the icon, one of the following things happen. If no text is selected in the tab currently being viewed, the page in the browser tab currently being viewed is translated into your native language. If the icon was left-clicked, the translated page will be loaded in the same tab. If the icon was middle-clicked or right-clicked the translated page is loaded in a new tab. If there is text selected in the current tab being viewed, a new tab is opened which has a translation of the selected text. If you are on a page translated using Google Translate, clicking the button will return you to the original page.

Array methods for XML lists

I have created an open source library that implements every array method for E4X XML lists in JavaScript named e4x-array-methods.js. The methods output XML as opposed to arrays to make the output directly usable with other XML. To get the array representation of an XML list, use slice. This returns XML when used to slice out ranges from XML but if no arguments are passed or a third argument is specified which is equivalent to true, it will convert an XML list to an array instead. For example, xmllist.slice(3, 5) returns an XML list and xmllist.slice(3, 5, true) returns an array.

Download

You can download e4x-array-methods.js at it’s github repository. If you wish to minify the library yourself, make sure that your minification tool supports the E4X used in this library. Rhino-based minifiers fail, throwing syntax errors, and /packer/ errantly minifies all of the public methods names preceded by .function::.

Example

The following example demonstrate the usefulness of having array methods for XML lists. If you want to try out the example in a JavaScript shell, make sure XML.prettyPrinting is set to false, which removes any unnecessary whitespace from xml.toString() and xml.toXMLString().

XML.prettyPrinting = false; // for simplifying the comparisons below
 
var foo = <></>; // XMLList literal
foo.push(<n>0</n>, 1, 5, 3, 2, 6, 4);
foo.toXMLString() === "<n>0</n><n>1</n><n>5</n><n>3</n><n>2</n><n>6</n><n>4</n>";
foo.sort(function(a, b) {
    return a - b;
}).toXMLString() === "<n>0</n><n>1</n><n>2</n><n>3</n><n>4</n><n>5</n><n>6</n>";
foo.filter(function(x) { // filter out integers less then 3
    if (!(x < 3))
        return true;
}).toXMLString() === "<n>3</n><n>4</n><n>5</n><n>6</n>";
foo.slice(2, 5).toXMLString() === "<n>2</n><n>3</n><n>4</n>";
foo.splice(2, 1, <n>9</n>, <n>8</n>).toXMLString() === "<n>2</n>";
foo.slice(2, 5).toXMLString() === "<n>3</n><n>9</n><n>8</n>";
foo.pop().toXMLString() === "<n>6</n>";
foo.shift().toXMLString() === "<n>0</n>";
foo.shift().toXMLString() === "<n>1</n>";
foo.unshift(<bar/>);
foo.toXMLString() === "<bar/><n>3</n><n>9</n><n>8</n><n>4</n><n>5</n>";
foo.map(parseFloat).forEach(function(n){ print(n) }) // prints NaN, 3, 9, 8, 4, 5
foo.some(function(x) { // some are greater than 5
    if (x > 5) return true;
}) === true;
foo.every(function(x) { // all are numbers
    if (typeof x == "number") return true
}) === false;

Edit Page Jetpack Feature

I have created a Jetpack feature called Edit Page to see what developing a Jetpack feature would be like. As the name implies, it lets you edit any web page in one click. Right clicking on the Edit Page button added to the status bar toggles spellcheck. To change the default spellcheck preference for the Jetpack feature, go to about:config and search for the jetpacks.editpage.spellcheck boolean preference to toggle it. The following is the code I used to implement version 0.1 of the Jetpack feature:

While creating this Jetpack feature, I ran into various quirks in the Jetpack platform:

  • Jetpack features are handled as JavaScript 1.6 and not 1.8 even though Firefox supports JavaScript 1.8. Update: This is due to a Firefox 3.0.x bug that is fixed in Firefox 3.5.
  • An HTML status bar widget added with jetpack.statusBar.append() is a complete HTMLDocument but it doesn’t support any features that involve the id attribute, like document.getElementById() and CSS’s #id syntax. This may be fixable by including a doctype in the HTML (I didn’t because it’s invalid E4X) but the point is that this is an HTML snippet, which should default to some HTML or XHTML doctype.
  • jetpack.notifications.show() doesn’t allow skipping options (passing null or undefined) without falling back on defaults (Prism’s API, platform.showNotification(), allows it). The following code should fix the function to allow this kind of use:
jetpack.notifications.show = function (message) {
    var body = message,
    title = "Jetpack Notification",
    icon = null; // Mozilla favicon is http://www.mozilla.org/favicon.ico
    if (typeof message == "object") {
        body = message.body;
        if ("title" in message) {
            title = message.title;
        }
        if ("icon" in message) {
            icon = message.icon;
        }
    }
    try {
        Components.classes['@mozilla.org/alerts-service;1']
          .getService(Components.interfaces.nsIAlertsService)
          .showAlertNotification(icon, title, body);
        return true;
    } catch (e) {
        console.log("Unable to display notification:", message);
        return false;
    }
};

Jetpack API

Mozilla Labs’ latest creation, Jetpack, is a great way to extend Firefox. It currently has a poorly documented API that doesn’t mention all of the public methods and fields. Due to Mozilla Labs’ decision of not putting the API documentation on MDC or the MozillaWiki, I cannot update the documentation myself. Therefore, I will list all of Jetpack’s documented and undocumented features as of version 0.1.2 in this blog post.

(Not) Assigning Properties

You can’t do things like jetpack.tabs.focused.contentWindow.foo = "bar". I find this much too restrictive, as it hinders a developer’s ability to make an API for a webpage to communicate with a jetpack to do things that need more privileges. Ironically, it seems that jetpacks have full access to Firefox’s XPCOM (Components.*, ect.) which means, with a little hacking, it may be possible to bypass this restriction. The follow code shows an example of getting an nsIAlertsService and using it instead of jetpack.notifications.show.

var body = "Some text",
title = "Notification",
icon = "http://code.eligrey.com/favicon.ico",
 
classObj = Components.classes['@mozilla.org/alerts-service;1'],
alertService = classObj.getService(Components.interfaces.nsIAlertsService);
 
alertService.showAlertNotification(icon, title, body);

(more…)

ECMAScript 5 accessors

A while ago, I created a JavaScript library named Xccessors, which implemented the legacy non-standard accessor (getter and setter) methods in IE8. I initially created two different libraries and decided that the library that implemented the legacy methods would be more useful at the time due to no changes needed in a JavaScript program’s code to add accessor support for IE. I never released the second one that implements the ECMAScript 5 (formerly 3.1) standard accessor methods so I’m releasing it now. There can’t be two different libraries with the same “Xccessors” name, so I am also renaming the libraries accordingly to what they implement. The new names are Xccessors Legacy and Xccessors Standard.

Here are two examples of using Object.defineProperty and Object.getOwnPropertyDescriptor:

Using accessors

(function() {
// this creates a document.foo accessor
    var foo = 0;
    Object.defineProperty(document, "foo", {
        get: function() { return foo },
        set: function(x) { return foo += x }
    });
})();
 
document.foo = 5;
(document.foo = 4) === 9;
//Object.getOwnPropertyDescriptor(document, "foo") is {set:[...], get:[...]}

Setting a property

// this is the equivalent of window.foo = "bar";
Object.defineProperty(window, "foo", {value: "bar"});
window.foo === "bar";
Object.getOwnPropertyDescriptor(window, "foo").value === "bar";
window.x = 0;
Object.getOwnPropertyDescriptor(window, "x").value === 0;

Object.getPrototypeOf

The addition of Object.getPrototypeOf in JavaScript 1.8.1 reminded me of John Resig’s Object.getPrototypeOf implementation that uses constructor property even if it has been modified. If the constructor property has been modified, it can be reset by deleting the property. An easy way to detect if a property like constructor has been modified is check if the object’s hasOwnProperty method returns true when passed "constructor". This could have been modified too, so the safest bet it to call Object.prototype.hasOwnProperty. Though the thing is, you can’t even trust that, so you just have to assume that it isn’t function hasOwnProperty() false;.
Now I bet you’re wondering, why I should go through all this trouble to check if the constructor property is modified. This is because just storing the constructor, then deleting, checking, and restoring it will set the constructor property if it wasn’t already set, which can mess with iterators. Of course you could just delete it and hope that if it was set, it wasn’t important, but that won’t always end up well.

You can download my implementation at gist.github.com/154398.

Namespacing properties in JavaScript

Namespacing properties is a great way to make a JavaScript library produce extendible objects. Namespacing in JavaScript can be done by prefixing namespace:: before object and property names. Unfortunately, namespacing is only supported in JavaScript 1.6 and higher, which is currently only implemented in SpiderMonkey (Firefox’s JavaScript engine) and Rhino with E4X enabled. Namespaces should have a toString method that returns a string of the same name (Foo.toString() == "Foo"). In Firefox, the @mozilla.org/js/function namespace (“function”) seems to be the default at which everything is under. For example, function::document == this.document and x = "@mozilla.org/js/function"; x::document == function::document. Also, anything with a blank string representation behaves the same way as long as it’s being used in a property, for example:

var foo = "@mozilla.org/js/function", bar = "";
try { // using try..catch because the error still fires in a typeof statement
    foo::document; // works fine
    bar::document; // error
} catch(err if err instanceof ReferenceError) {
    // err.message == "reference to undefined XML name document";
}
this.bar::document == this.document;

The following example shows a naming conflict that can be solved using namespaced properties. In the examples, the fictional libraries, libSpaceTime and libDimensions are loaded. libDimensions adds a Size global and creates a ‘dimensions’ setter on Object.prototype that accepts various inputs to change the width/height/depth/ect. of something. libSpaceTime introduces the SpaceTime global. When a universe is created, the dimensions setter changes the number of dimensions the universe has. The width/height/ect. of a universe are automatically set (but can be changed) from the matter property.
(more…)