aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/menuitems/README.md3
-rw-r--r--packages/menuitems/docs/menuitems.md66
-rw-r--r--packages/menuitems/lib/menuitems.js195
-rw-r--r--packages/menuitems/package.json9
-rw-r--r--packages/menuitems/tests/test-menuitems.js171
-rw-r--r--packages/notificationbox/LICENSE373
-rw-r--r--packages/notificationbox/README.md5
-rw-r--r--packages/notificationbox/data/gnu-icon.pngbin0 -> 2344 bytes
-rw-r--r--packages/notificationbox/doc/images/critical-notification.pngbin0 -> 25113 bytes
-rw-r--r--packages/notificationbox/doc/images/info-low-priority.pngbin0 -> 23498 bytes
-rw-r--r--packages/notificationbox/doc/main.md46
-rw-r--r--packages/notificationbox/lib/main.js16
-rw-r--r--packages/notificationbox/lib/notification-box.js129
-rw-r--r--packages/notificationbox/package.json9
-rw-r--r--packages/notificationbox/test/test-main.js12
-rw-r--r--packages/vold-utils/README.md5
-rw-r--r--packages/vold-utils/docs/listen.md23
-rw-r--r--packages/vold-utils/docs/unload+.md20
-rw-r--r--packages/vold-utils/lib/array.js6
-rw-r--r--packages/vold-utils/lib/instances.js57
-rw-r--r--packages/vold-utils/lib/listen.js29
-rw-r--r--packages/vold-utils/lib/memoize.js131
-rw-r--r--packages/vold-utils/lib/net-utils.js3
-rw-r--r--packages/vold-utils/lib/services.js84
-rw-r--r--packages/vold-utils/lib/unload+.js80
-rw-r--r--packages/vold-utils/lib/window-watcher.js91
-rw-r--r--packages/vold-utils/lib/xpcom-utils.js3
-rw-r--r--packages/vold-utils/package.json7
-rw-r--r--packages/vold-utils/tests/test-unload+.js119
29 files changed, 1692 insertions, 0 deletions
diff --git a/packages/menuitems/README.md b/packages/menuitems/README.md
new file mode 100644
index 0000000..12a6954
--- /dev/null
+++ b/packages/menuitems/README.md
@@ -0,0 +1,3 @@
+# Menuitems Package for Jetpack Add-ons
+
+
diff --git a/packages/menuitems/docs/menuitems.md b/packages/menuitems/docs/menuitems.md
new file mode 100644
index 0000000..2017c9c
--- /dev/null
+++ b/packages/menuitems/docs/menuitems.md
@@ -0,0 +1,66 @@
+<!-- contributed by Erik Vold [erikvvold@gmail.com] -->
+
+
+The `menuitems` API is a simple way to create
+[Menuitems](https://developer.mozilla.org/en/XUL/PopupGuide/MenuItems), which
+can perform an action when clicked, and display state.
+
+## Example ##
+
+ exports.main = function(options) {
+ // create menuitem for the File menu,
+ // and insert it before the 'Quit' menuitem
+ require("menuitems").Menuitem({
+ id: "myextprefix-some-mi-id",
+ menuid: "menu_FilePopup",
+ insertbefore: "menu_FileQuitItem",
+ "label": _("label"),
+ "accesskey": _("label.ak"),
+ image: self.data.url("icon.png"),
+ className: 'pizazz',
+ disabled: false,
+ checked: false,
+ onCommand: function() {
+ // do something
+ }
+ });
+ };
+
+<api name="Menuitem">
+@class
+
+Module exports `Menuitem` constructor allowing users to create a
+[`menuitem`](https://developer.mozilla.org/en/XUL/menuitem).
+
+<api name="Menuitem">
+@constructor
+Creates a `menuitem`.
+
+@param options {Object}
+ Options for the `menuitem`, with the following parameters:
+
+@prop id {String}
+A id for the `menuitem`, this should be namespaced.
+
+@prop menuid {String}
+The id of the parent `<menu>` node.
+
+@prop label {String}
+A label for the `menuitem`.
+
+@prop image {String}
+A image url for the `menuitem`.
+
+@prop className {String}
+A default space delimited list of class names for the menuitem.
+
+@prop disabled {Boolean}
+When a menuitem is disabled it cannot be used, but is still displayed.
+
+@prop checked {Boolean}
+Displays a check beside the menuitem.
+
+@prop [onCommand] {Function}
+ A option function that is invoked when the `menuitem` is executed.
+</api>
+</api>
diff --git a/packages/menuitems/lib/menuitems.js b/packages/menuitems/lib/menuitems.js
new file mode 100644
index 0000000..075d690
--- /dev/null
+++ b/packages/menuitems/lib/menuitems.js
@@ -0,0 +1,195 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const windowUtils = require("sdk/deprecated/window-utils");
+const { Class } = require("sdk/core/heritage");
+const { validateOptions } = require("sdk/deprecated/api-utils");
+const { on, emit, once, off } = require("sdk/event/core");
+const { isBrowser } = require("sdk/window/utils");
+const { EventTarget } = require('sdk/event/target');
+const { unload } = require("unload+");
+
+const menuitemNS = require("sdk/core/namespace").ns();
+const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function MenuitemOptions(options) {
+ return validateOptions(options, {
+ id: { is: ['string'] },
+ menuid: { is: ['undefined', 'string'] },
+ insertbefore: { is: ['undefined', 'string', 'object'] },
+ label: { is: ["string"] },
+ disabled: { is: ["undefined", "boolean"], map: function(v) !!v},
+ accesskey: { is: ["undefined", "string"] },
+ key: { is: ["undefined", "string"] },
+ checked: { is: ['undefined', 'boolean'] },
+ className: { is: ["undefined", "string"] },
+ onCommand: { is: ['undefined', 'function'] }
+ });
+}
+
+let Menuitem = Class({
+ extends: EventTarget,
+ initialize: function(options) {
+ options = menuitemNS(this).options = MenuitemOptions(options);
+ EventTarget.prototype.initialize.call(this, options);
+
+ menuitemNS(this).destroyed = false;
+ menuitemNS(this).unloaders = [];
+ menuitemNS(this).menuitems = addMenuitems(this, options).menuitems;
+ },
+ get id() menuitemNS(this).options.id,
+ get label() menuitemNS(this).options.label,
+ set label(val) updateProperty(this, 'label', val),
+ get checked() menuitemNS(this).options.checked,
+ set checked(val) updateProperty(this, 'checked', !!val),
+ get disabled() menuitemNS(this).options.disabled,
+ set disabled(val) updateProperty(this, 'disabled', !!val),
+ get key() menuitemNS(this).options.key,
+ set key(val) updateProperty(this, 'key', val),
+ clone: function (overwrites) {
+ let opts = Object.clone(menuitemNS(this).options);
+ for (let key in overwrites) {
+ opts[key] = ovrewrites[key];
+ }
+ return Menuitem(opts);
+ },
+ get menuid() menuitemNS(this).options.menuid,
+ set menuid(val) {
+ let options = menuitemNS(this).options;
+ options.menuid = val;
+
+ forEachMI(function(menuitem, i, $) {
+ updateMenuitemParent(menuitem, options, $);
+ });
+ },
+ destroy: function() {
+ if (!menuitemNS(this).destroyed) {
+ menuitemNS(this).destroyed = true;
+ menuitemNS(this).unloaders.forEach(function(u) u());
+ menuitemNS(this).unloaders = null;
+ menuitemNS(this).menuitems = null;
+ }
+ return true;
+ }
+});
+
+function addMenuitems(self, options) {
+ let menuitems = [];
+
+ // setup window tracker
+ windowUtils.WindowTracker({
+ onTrack: function (window) {
+ if (!isBrowser(window) || menuitemNS(self).destroyed) return;
+
+ // add the new menuitem to a menu
+ var menuitem = updateMenuitemAttributes(
+ window.document.createElementNS(NS_XUL, "menuitem"), options);
+ var menuitems_i = menuitems.push(menuitem) - 1;
+
+ // add the menutiem to the ui
+ updateMenuitemParent(menuitem, options, function(id) window.document.getElementById(id));
+
+ menuitem.addEventListener("command", function() {
+ if (!self.disabled)
+ emit(self, 'command');
+ }, true);
+
+ // add unloader
+ let unloader = function unloader() {
+ menuitem.parentNode && menuitem.parentNode.removeChild(menuitem);
+ menuitems[menuitems_i] = null;
+ };
+ menuitemNS(self).unloaders.push(function() {
+ remover();
+ unloader();
+ });
+ let remover = unload(unloader, window);
+ }
+ });
+ return {menuitems: menuitems};
+}
+
+function updateMenuitemParent(menuitem, options, $) {
+ // add the menutiem to the ui
+ if (Array.isArray(options.menuid)) {
+ let ids = options.menuid;
+ for (var len = ids.length, i = 0; i < len; i++) {
+ if (tryParent($(ids[i]), menuitem, options.insertbefore))
+ return true;
+ }
+ }
+ else {
+ return tryParent($(options.menuid), menuitem, options.insertbefore);
+ }
+ return false;
+}
+
+function updateMenuitemAttributes(menuitem, options) {
+ menuitem.setAttribute("id", options.id);
+ menuitem.setAttribute("label", options.label);
+
+ if (options.accesskey)
+ menuitem.setAttribute("accesskey", options.accesskey);
+
+ if (options.key)
+ menuitem.setAttribute("key", options.key);
+
+ menuitem.setAttribute("disabled", !!options.disabled);
+
+ if (options.image) {
+ menuitem.classList.add("menuitem-iconic");
+ menuitem.style.listStyleImage = "url('" + options.image + "')";
+ }
+
+ if (options.checked)
+ menuitem.setAttribute('checked', options.checked);
+
+ if (options.className)
+ options.className.split(/\s+/).forEach(function(name) menuitem.classList.add(name));
+
+ return menuitem;
+}
+
+function updateProperty(menuitem, key, val) {
+ menuitemNS(menuitem).options[key] = val;
+
+ forEachMI(function(menuitem) {
+ menuitem.setAttribute(key, val);
+ }, menuitem);
+ return val;
+}
+
+function forEachMI(callback, menuitem) {
+ menuitemNS(menuitem).menuitems.forEach(function(mi, i) {
+ if (!mi) return;
+ callback(mi, i, function(id) mi.ownerDocument.getElementById(id));
+ });
+}
+
+function tryParent(parent, menuitem, before) {
+ if (parent) parent.insertBefore(menuitem, insertBefore(parent, before));
+ return !!parent;
+}
+
+function insertBefore(parent, insertBefore) {
+ if (typeof insertBefore == "number") {
+ switch (insertBefore) {
+ case MenuitemExport.FIRST_CHILD:
+ return parent.firstChild;
+ }
+ return null;
+ }
+ else if (typeof insertBefore == "string") {
+ return parent.querySelector("#" + insertBefore);
+ }
+ return insertBefore;
+}
+
+function MenuitemExport(options) {
+ return Menuitem(options);
+}
+MenuitemExport.FIRST_CHILD = 1;
+
+exports.Menuitem = MenuitemExport;
diff --git a/packages/menuitems/package.json b/packages/menuitems/package.json
new file mode 100644
index 0000000..95b1ede
--- /dev/null
+++ b/packages/menuitems/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "menuitems",
+ "description": "Menuitems for Jetpacks",
+ "keywords": ["menu", "menuitems", "button", "ui"],
+ "author": "Erik Vold (http://erikvold.com/) <erikvvold@gmail.com>",
+ "version": "1.1.1",
+ "license": "MPL 2.0",
+ "dependencies": ["api-utils", "vold-utils"]
+}
diff --git a/packages/menuitems/tests/test-menuitems.js b/packages/menuitems/tests/test-menuitems.js
new file mode 100644
index 0000000..191bb44
--- /dev/null
+++ b/packages/menuitems/tests/test-menuitems.js
@@ -0,0 +1,171 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict'
+
+const windowUtils = require("window-utils");
+const menuitems = require("menuitems");
+
+let window = windowUtils.activeBrowserWindow;
+let document = window.document;
+function $(id) document.getElementById(id);
+
+function createMI(options, test) {
+ test.assertEqual(!$(options.id), true);
+ var mi = new menuitems.Menuitem(options);
+ return mi;
+}
+
+exports.testMIDoesNotExist = function(test) {
+ var options = {
+ id: "test-mi-dne",
+ label: "test"
+ };
+ createMI(options, test);
+ test.assertEqual(!!$(options.id), false, 'menuitem does not exists');
+};
+
+exports.testMIDoesExist = function(test) {
+ var options = {
+ id: "test-mi-exists",
+ label: "test",
+ menuid: 'menu_FilePopup'
+ };
+ let mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ test.assertEqual(menuitem.id, options.id, 'menuitem id is ok');
+ test.assertEqual(menuitem.getAttribute('label'), options.label, 'menuitem label is ok');
+ test.assertEqual(menuitem.parentNode.id, options.menuid, 'in the file menu');
+ test.assertEqual(menuitem.getAttribute('disabled'), 'false', 'menuitem not disabled');
+ test.assertEqual(menuitem.getAttribute('accesskey'), '', 'menuitem accesskey is ok');
+ test.assertEqual(menuitem.getAttribute('class'), '', 'menuitem class is ok');
+ test.assertEqual(menuitem.nextSibling, undefined, 'menuitem is last');
+ test.assertEqual(menuitem.hasAttribute("checked"), false, 'menuitem not checked');
+ mi.destroy();
+ test.assert(!$(options.id), 'menuitem is gone');
+ test.assertEqual(menuitem.parentNode, null, 'menuitem has no parent');
+};
+
+exports.testMIOnClick = function(test) {
+ test.waitUntilDone();
+
+ let options = {
+ id: "test-mi-onclick",
+ label: "test",
+ menuid: 'menu_FilePopup',
+ onCommand: function() {
+ mi.destroy();
+ test.pass('onCommand worked!');
+ test.done();
+ }
+ };
+
+ let e = document.createEvent("UIEvents");
+ e.initUIEvent("command", true, true, window, 1);
+
+ var mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ menuitem.dispatchEvent(e);
+};
+
+exports.testMIDisabled = function(test) {
+ test.waitUntilDone();
+
+ let commandIsOK = false;
+ let count = 0;
+ let options = {
+ id: "test-mi-disabled",
+ label: "test",
+ disabled: true,
+ menuid: 'menu_FilePopup',
+ onCommand: function() {
+ count++;
+ if (!commandIsOK) {
+ test.fail('onCommand was called, that is not ok');
+ return;
+ }
+
+ mi.destroy();
+ test.assertEqual(count, 1, 'onCommand was called the correct number of times!');
+ test.done();
+ }
+ };
+
+ let e = document.createEvent("UIEvents");
+ e.initUIEvent("command", true, true, window, 1);
+
+ var mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ test.assertEqual(menuitem.getAttribute('disabled'), 'true', 'menuitem not disabled');
+ menuitem.dispatchEvent(e);
+ mi.disabled = false;
+ test.assertEqual(menuitem.getAttribute('disabled'), 'false', 'menuitem not disabled');
+ commandIsOK = true;
+ menuitem.dispatchEvent(e);
+};
+
+exports.testMIChecked = function(test) {
+ let options = {
+ id: "test-mi-checked",
+ label: "test",
+ disabled: true,
+ menuid: 'menu_FilePopup',
+ checked: true
+ };
+
+ let mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ test.assertEqual(menuitem.getAttribute("checked"), "true", 'menuitem checked');
+ mi.checked = false;
+ test.assertEqual(menuitem.getAttribute("checked"), "false", 'menuitem checked');
+ mi.destroy();
+};
+
+exports.testMIClass = function(test) {
+ let options = {
+ id: "test-mi-class",
+ label: "pizazz",
+ className: "pizazz",
+ menuid: 'menu_FilePopup',
+ };
+
+ var mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ test.assertEqual(menuitem.getAttribute('class'), 'pizazz', 'menuitem not disabled');
+ mi.destroy();
+};
+
+exports.testInsertBeforeExists = function(test) {
+ let options = {
+ id: 'test-mi-insertbefore',
+ label: 'insertbefore',
+ insertbefore:'menu_FileQuitItem',
+ menuid: 'menu_FilePopup',
+ };
+
+ var mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ test.assertEqual(menuitem.nextSibling, $('menu_FileQuitItem'), 'menuitem not disabled');
+ mi.destroy();
+};
+
+exports.testInsertBeforeDoesNotExist = function(test) {
+ let options = {
+ id: 'test-mi-insertbefore',
+ label: 'insertbefore',
+ insertbefore:'menu_ZZZDNE',
+ menuid: 'menu_FilePopup',
+ };
+
+ var mi = createMI(options, test);
+ let menuitem = $(options.id);
+ test.assertEqual(!!menuitem, true, 'menuitem exists');
+ test.assertEqual(menuitem.nextSibling, null, 'menuitem not disabled');
+ mi.destroy();
+};
diff --git a/packages/notificationbox/LICENSE b/packages/notificationbox/LICENSE
new file mode 100644
index 0000000..14e2f77
--- /dev/null
+++ b/packages/notificationbox/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/packages/notificationbox/README.md b/packages/notificationbox/README.md
new file mode 100644
index 0000000..df41739
--- /dev/null
+++ b/packages/notificationbox/README.md
@@ -0,0 +1,5 @@
+NotificationBox implements some of the notificationbox element functionality in Firefox and Mozilla-based browsers using the Addon SDK. For any question or feedback please email lduros--at--member.fsf.org
+
+It is released under the MPL 2.0.
+
+See [doc/main.md](https://github.com/lduros/notificationbox/blob/master/doc/main.md)for more information.
diff --git a/packages/notificationbox/data/gnu-icon.png b/packages/notificationbox/data/gnu-icon.png
new file mode 100644
index 0000000..2005027
--- /dev/null
+++ b/packages/notificationbox/data/gnu-icon.png
Binary files differ
diff --git a/packages/notificationbox/doc/images/critical-notification.png b/packages/notificationbox/doc/images/critical-notification.png
new file mode 100644
index 0000000..6e69b22
--- /dev/null
+++ b/packages/notificationbox/doc/images/critical-notification.png
Binary files differ
diff --git a/packages/notificationbox/doc/images/info-low-priority.png b/packages/notificationbox/doc/images/info-low-priority.png
new file mode 100644
index 0000000..6fff9f6
--- /dev/null
+++ b/packages/notificationbox/doc/images/info-low-priority.png
Binary files differ
diff --git a/packages/notificationbox/doc/main.md b/packages/notificationbox/doc/main.md
new file mode 100644
index 0000000..2c28c3a
--- /dev/null
+++ b/packages/notificationbox/doc/main.md
@@ -0,0 +1,46 @@
+NotificationBox implements some of the notificationbox element functionality in Firefox and Mozilla-based browsers using the Addon SDK. For any question or feedback please email lduros--at--member.fsf.org
+
+You can create a notification as follows:
+```javascript
+var self = require("self");
+var notification = require("notification-box").NotificationBox({
+ 'value': 'important-message',
+ 'label': 'This is a very important message!',
+ 'priority': 'CRITICAL_BLOCK',
+ 'image': self.data.url("gnu-icon.png"),
+ 'buttons': [{'label': "Do something about it!",
+ 'onClick': function () { console.log("You clicked the important button!"); }}]
+});
+```
+
+It implements the following options:
+
+ - value: value used to identify the notification
+ - label: label to appear on the notification
+ - image: URL of image to appear on the notification. You can also
+ load a local image from the data folder, like in the example
+ - priority: notification priority. Use one of the following:
+ - INFO_LOW
+ - INFO_MEDIUM
+ - INFO_HIGH
+ - WARNING_LOW
+ - WARNING_MEDIUM
+ - WARNING_HIGH
+ - CRITICAL_LOW
+ - CRITICAL_MEDIUM
+ - CRITICAL_HIGH
+ - CRITICAL_BLOCK
+ - buttons: array of buttons to appear on the notification.
+ You can use the following options:
+ - accessKey: the accesskey to appear on the button
+ - onClick: the callback function to trigger when button is activated.
+ - label: the text to display on the button
+
+
+Here is an overview of what the example above with the critical notification looks like:
+![The Critical notification](https://raw.github.com/lduros/notificationbox/master/doc/images/critical-notification.png)
+
+When using 'INFO_LOW' for the priority instead, you would see:
+![Info Low notification](https://raw.github.com/lduros/notificationbox/master/doc/images/info-low-priority.png)
+
+For more information on notificationbox: https://developer.mozilla.org/en-US/docs/XUL/notificationbox
diff --git a/packages/notificationbox/lib/main.js b/packages/notificationbox/lib/main.js
new file mode 100644
index 0000000..0d17404
--- /dev/null
+++ b/packages/notificationbox/lib/main.js
@@ -0,0 +1,16 @@
+/*
+ * Copyrights Loic J. Duros 2012
+ * lduros@member.fsf.org
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var self = require("sdk/self");
+var notification = require("notification-box").NotificationBox({
+ 'value': 'important-message',
+ 'label': 'You have been warned...',
+ 'priority': 'WARNING_HIGH',
+ 'image': self.data.url("gnu-icon.png"),
+ 'buttons': [{'label': "Gotcha",
+ 'onClick': function () { console.log("You clicked the important button!"); }}]
+});
diff --git a/packages/notificationbox/lib/notification-box.js b/packages/notificationbox/lib/notification-box.js
new file mode 100644
index 0000000..94b26cd
--- /dev/null
+++ b/packages/notificationbox/lib/notification-box.js
@@ -0,0 +1,129 @@
+/*
+ * Copyrights Loic J. Duros 2012
+ * lduros@member.fsf.org
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+/* I haven't found this sort of validation functions in the SDK,
+except for the deprecated api-utils module. */
+let isString = function (str) {
+ return typeof(str) == 'string' || str instanceof String;
+};
+
+let isArray = function (obj) {
+ return Object.prototype.toString.call(obj) === '[object Array]';
+};
+
+exports.NotificationBox = function (options) {
+ options = options || {};
+ let mainWindow = getWindow();
+ let nb = mainWindow.gBrowser.getNotificationBox();
+ let notification, priority, label, image, value, buttons = [];
+
+ if (options.value && isString(options.value)) {
+ notification = nb.getNotificationWithValue(options.value);
+ value = options.value;
+ }
+ else {
+ notification = nb.getNotificationWithValue('');
+ value = '';
+ }
+
+ // Add label or create empty notification.
+ if (options.label && isString(options.label))
+ label = options.label;
+ else
+ label = "";
+
+ // Set priority of the notification (from info low, to critical
+ // block.
+ if (options.priority && options.priority in PRIORITY)
+ priority = nb[PRIORITY[options.priority]];
+ else
+ priority = nb[PRIORITY.INFO_LOW];
+
+ // Set a custom icon for the notification or use the regular info
+ // icon.
+ if (options.image && isString(options.image))
+ image = options.image;
+ else
+ image = 'chrome://browser/skin/Info.png';
+
+ // Add buttons.
+ if (isArray(options.buttons)) {
+ for (let i = 0, length = options.buttons.length; i < length; i++) {
+ buttons.push(NotificationButton(options.buttons[i]));
+ }
+ }
+ else if (typeof(options.buttons) === 'object') {
+ // If it's not an array of buttons, then it should be a single button.
+ buttons.push(NotificationButton(options.buttons));
+ }
+ else {
+ buttons = null;
+ }
+
+ // add new notification to notificationbox.
+ nb.appendNotification(label, value,
+ image,
+ priority, buttons);
+
+ return {'notificationbox': nb, 'notification': notification};
+};
+
+
+
+var NotificationButton = function (options) {
+
+ options = options || {};
+ let accessKey, onClick, label, popup;
+
+ if (options.accessKey)
+ accessKey = options.accessKey;
+ else
+ accessKey = '';
+
+ if (options.onClick)
+ onClick = options.onClick;
+ else
+ onClick = function () {};
+
+ if (options.label)
+ label = options.label;
+ else
+ label = "";
+
+ // no popup for now... maybe we can use a panel later.
+ popup = null;
+
+ return {label: label,
+ accessKey: accessKey,
+ callback: onClick,
+ popup: popup};
+
+};
+
+const PRIORITY = {
+ 'INFO_LOW': 'PRIORITY_INFO_LOW',
+ 'INFO_MEDIUM': 'PRIORITY_INFO_MEDIUM',
+ 'INFO_HIGH': 'PRIORITY_INFO_HIGH',
+ 'WARNING_LOW': 'PRIORITY_WARNING_LOW',
+ 'WARNING_MEDIUM': 'PRIORITY_WARNING_MEDIUM',
+ 'WARNING_HIGH': 'PRIORITY_WARNING_HIGH',
+ 'CRITICAL_LOW': 'PRIORITY_CRITICAL_LOW',
+ 'CRITICAL_MEDIUM': 'PRIORITY_CRITICAL_MEDIUM',
+ 'CRITICAL_HIGH': 'PRIORITY_CRITICAL_HIGH',
+ 'CRITICAL_BLOCK': 'PRIORITY_CRITICAL_BLOCK'
+};
+
+let getWindow = function () {
+ return getMostRecentBrowserWindow();
+};
+
+exports.PRIORITY = PRIORITY;
+
diff --git a/packages/notificationbox/package.json b/packages/notificationbox/package.json
new file mode 100644
index 0000000..57ce0bb
--- /dev/null
+++ b/packages/notificationbox/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "notification-box",
+ "license": "MPL 2.0",
+ "author": "",
+ "version": "0.1",
+ "fullName": "notification-box",
+ "id": "jid1-dyFGGJB0CjoDMA",
+ "description": "a basic add-on"
+}
diff --git a/packages/notificationbox/test/test-main.js b/packages/notificationbox/test/test-main.js
new file mode 100644
index 0000000..9e7031a
--- /dev/null
+++ b/packages/notificationbox/test/test-main.js
@@ -0,0 +1,12 @@
+var main = require("main");
+
+exports["test main"] = function(assert) {
+ assert.pass("Unit test running!");
+};
+
+exports["test main async"] = function(assert, done) {
+ assert.pass("async Unit test running!");
+ done();
+};
+
+require("test").run(exports);
diff --git a/packages/vold-utils/README.md b/packages/vold-utils/README.md
new file mode 100644
index 0000000..41773e3
--- /dev/null
+++ b/packages/vold-utils/README.md
@@ -0,0 +1,5 @@
+# Vold Utils for Mozilla's Add-on SDK
+
+A collection of various modules which help build other apis.
+
+
diff --git a/packages/vold-utils/docs/listen.md b/packages/vold-utils/docs/listen.md
new file mode 100644
index 0000000..d19c6c6
--- /dev/null
+++ b/packages/vold-utils/docs/listen.md
@@ -0,0 +1,23 @@
+<!-- contributed by Erik Vold [erikvvold@gmail.com] -->
+
+The `listen` module allows modules to register listeners to elements that are
+automatically removed when the module unloads.
+
+<api name="listen">
+@function
+ Add listeners to run when unloading in a unload queue. Optionally scope the
+ callback to a container, e.g., window. Provide a way to run all the callbacks.
+
+@param container {object}
+ A container for the node, which a "unload" event will be attached to, this is
+ used to cancel the unload magic that would occur, to avoid memory leaks.
+@param node {object}
+ The node to listen to.
+@param event {string}
+ The event type, for example: "load", "click", ...
+@param callback {function}
+ A function to be called when the event occurs on the node.
+@param [capture] {boolean}
+ Indicates if the event should be captured. [See the `useCapture`
+ documentation here](https://developer.mozilla.org/en/DOM/element.addEventListener).
+</api>
diff --git a/packages/vold-utils/docs/unload+.md b/packages/vold-utils/docs/unload+.md
new file mode 100644
index 0000000..78693ef
--- /dev/null
+++ b/packages/vold-utils/docs/unload+.md
@@ -0,0 +1,20 @@
+<!-- contributed by Erik Vold [erikvvold@gmail.com] -->
+
+The `unload+` module allows modules to register callbacks that are called
+when they are unloaded, and associate unload functions to containers to have the
+unloader automatically deleted when the container unloads.
+
+<api name="unload">
+@function
+ Save callbacks to run when unloading in a unload queue. Optionally scope the
+ callback to a container, e.g., window. Provide a way to run all the callbacks.
+
+@param callback {function}
+ A function to be called when the module unloads.
+@param [container] {object}
+ Optional container object; if the container "unloads" before the module
+ unloads, then the associated callback is removed from the unload queue.
+@returns {function}
+ Returns a function which will allow one to remove the callback from the unload
+ queue.
+</api>
diff --git a/packages/vold-utils/lib/array.js b/packages/vold-utils/lib/array.js
new file mode 100644
index 0000000..5ac0577
--- /dev/null
+++ b/packages/vold-utils/lib/array.js
@@ -0,0 +1,6 @@
+'use strict';
+
+function run(array) array.forEach(function(f) f());
+exports.run = run;
+
+
diff --git a/packages/vold-utils/lib/instances.js b/packages/vold-utils/lib/instances.js
new file mode 100644
index 0000000..a33ca40
--- /dev/null
+++ b/packages/vold-utils/lib/instances.js
@@ -0,0 +1,57 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT/X11 License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Contributor(s):
+ * Erik Vold <erikvvold@gmail.com> (Original Author)
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const {Cc, Ci, Cu} = require("chrome");
+
+var Instances = exports.Instances = {
+ get bis() Cc["@mozilla.org/binaryinputstream;1"]
+ .createInstance(Ci.nsIBinaryInputStream),
+ get ch() Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash),
+ get dp() Cc["@mozilla.org/xmlextras/domparser;1"]
+ .createInstance(Ci.nsIDOMParser),
+ get ds() Cc["@mozilla.org/xmlextras/xmlserializer;1"]
+ .createInstance(Ci.nsIDOMSerializer),
+ get fos() Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream),
+ get sfos() Cc["@mozilla.org/network/safe-file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream)
+ .QueryInterface(Ci.nsISafeOutputStream),
+ get fp() Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker),
+ get lf() Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile),
+ get process() Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess),
+ get se() Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError)
+ .QueryInterface(Ci.nsIScriptError2),
+ get ss() Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString),
+ get suc() Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter),
+ get timer() Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
+ get wbp() Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist),
+ get xhr() Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest)
+};
diff --git a/packages/vold-utils/lib/listen.js b/packages/vold-utils/lib/listen.js
new file mode 100644
index 0000000..0b947d1
--- /dev/null
+++ b/packages/vold-utils/lib/listen.js
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { unload, Unload } = require("unload+");
+
+/**
+ * Helper that adds event listeners and remembers to remove on unload
+ */
+function listen(window, node, event, func, capture) {
+ // Default to use capture
+ if (capture == null)
+ capture = true;
+
+ node.addEventListener(event, func, capture);
+ function undoListen() {
+ node.removeEventListener(event, func, capture);
+ }
+
+ // Undo the listener on unload and provide a way to undo everything
+ let undoUnload = unload(undoListen, window);
+ return function() {
+ undoListen();
+ undoUnload();
+ };
+}
+exports.listen = listen;
+
diff --git a/packages/vold-utils/lib/memoize.js b/packages/vold-utils/lib/memoize.js
new file mode 100644
index 0000000..73bb4fc
--- /dev/null
+++ b/packages/vold-utils/lib/memoize.js
@@ -0,0 +1,131 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT/X11 License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Contributor(s):
+ * Nils Maier <MaierMan@web.de>
+ * Erik Vold <erikvvold@gmail.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+/**
+ * Decorate a function with a memoization wrapper, with a limited-size cache
+ * to reduce peak memory utilization.
+ *
+ * The memoized function may have any number of arguments, but they must be
+ * be serializable. It's safest to use this only on functions that accept
+ * primitives.
+ *
+ * A memoized function is not thread-safe, but so is JS, nor re-entrant-safe!
+ *
+ * @usage var foo = Scriptish_memoize(function foo(arg1, arg2) { ... complex operation ... });
+ * @param {Function} func The function to be memoized
+ * @param {Number} limit Optional. Cache size (default: 100)
+ * @param {Number} num_args Options. Number of arguments the function expects (default: func.length)
+ * @return {Function} Memoized function
+ */
+exports.memoize = function memoize(func, limit, num_args) {
+ limit = limit || 100;
+ num_args = num_args || func.length;
+
+ var cache = Object.create(null);
+ var keylist = [];
+ var args = [];
+ var key, result;
+
+ switch (num_args) {
+ case 0:
+ throw new Error("memoize does not support functions without arguments");
+ case 1:
+ return function memoize_one_arg(a) {
+ key = a.toString();
+
+ if (key in cache)
+ return cache[key];
+
+ result = func.call(null, a);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+ case 2:
+ return function memoize_two_args(a, b) {
+ args[0] = a; args[1] = b;
+ key = JSON.stringify(args);
+ args.length = 0;
+
+ if (key in cache)
+ return cache[key];
+
+ var result = func.call(null, a, b);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+ case 3:
+ return function memoize_three_args(a, b, c) {
+ args[0] = a; args[1] = b; args[2] = c;
+ key = JSON.stringify(args);
+ args.length = 0;
+
+ if (key in cache)
+ return cache[key];
+
+ var result = func.call(null, a, b, c);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+
+ case 4:
+ return function memoize_four_args(a, b, c, d) {
+ args[0] = a; args[1] = b; args[2] = c; args[3] = d;
+ key = JSON.stringify(args);
+ args.length = 0;
+
+ if (key in cache)
+ return cache[key];
+
+ var result = func.call(null, a, b, c, d);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+
+ default:
+ return function() {
+ var key = JSON.stringify(arguments);
+ if (key in cache)
+ return cache[key];
+
+ var result = func.apply(null, arguments);
+ cache[key] = result;
+ if (keylist.push(key) > limit)
+ delete cache[keylist.shift()];
+ return result;
+ };
+ }
+}
diff --git a/packages/vold-utils/lib/net-utils.js b/packages/vold-utils/lib/net-utils.js
new file mode 100644
index 0000000..df7f300
--- /dev/null
+++ b/packages/vold-utils/lib/net-utils.js
@@ -0,0 +1,3 @@
+
+require("chrome").Cu.import("resource://gre/modules/NetUtil.jsm", this);
+exports.NetUtil = NetUtil;
diff --git a/packages/vold-utils/lib/services.js b/packages/vold-utils/lib/services.js
new file mode 100644
index 0000000..4854dfa
--- /dev/null
+++ b/packages/vold-utils/lib/services.js
@@ -0,0 +1,84 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT/X11 License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Contributor(s):
+ * Erik Vold <erikvvold@gmail.com> (Original Author)
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const {Cc, Ci, Cu} = require("chrome");
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+const global = this;
+var Services = exports.Services = {};
+(function(inc, tools){
+ inc("resource://gre/modules/XPCOMUtils.jsm", global);
+ inc("resource://gre/modules/Services.jsm", tools);
+ Services.__proto__ = tools.Services;
+})(Cu.import, {});
+
+/*
+XPCOMUtils.defineLazyGetter(Services, "scriptish", function() (
+ Cc["@scriptish.erikvold.com/scriptish-service;1"]
+ .getService().wrappedJSObject));
+*/
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "as", "@mozilla.org/alerts-service;1", "nsIAlertsService");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "ass", "@mozilla.org/appshell/appShellService;1",
+ "nsIAppShellService");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "cb", "@mozilla.org/widget/clipboardhelper;1",
+ "nsIClipboardHelper");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "cs", "@mozilla.org/consoleservice;1", "nsIConsoleService");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "eps", "@mozilla.org/uriloader/external-protocol-service;1",
+ "nsIExternalProtocolService");
+
+if (Cc["@mozilla.org/privatebrowsing;1"]) {
+ XPCOMUtils.defineLazyServiceGetter(
+ Services, "pbs", "@mozilla.org/privatebrowsing;1",
+ "nsIPrivateBrowsingService");
+} else {
+ Services.pbs = {privateBrowsingEnabled: false};
+}
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "sis", "@mozilla.org/scriptableinputstream;1",
+ "nsIScriptableInputStream");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "suhtml", "@mozilla.org/feed-unescapehtml;1",
+ "nsIScriptableUnescapeHTML");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "tld", "@mozilla.org/network/effective-tld-service;1",
+ "nsIEffectiveTLDService");
+
+XPCOMUtils.defineLazyServiceGetter(
+ Services, "uuid", "@mozilla.org/uuid-generator;1",
+ "nsIUUIDGenerator");
diff --git a/packages/vold-utils/lib/unload+.js b/packages/vold-utils/lib/unload+.js
new file mode 100644
index 0000000..285fd46
--- /dev/null
+++ b/packages/vold-utils/lib/unload+.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Class } = require("sdk/core/heritage");
+const unloadNS = require("sdk/core/namespace").ns();
+
+var Unloader = exports.Unloader = Class({
+ initialize: function Unloader() {
+ let unloaders = unloadNS(this).unloaders = [];
+
+ let unloadersUnlaod = unloadNS(this).unloadersUnlaod = function() {
+ unloaders.slice().forEach(function(u) u());
+ unloaders.length = 0;
+ }
+
+ require("sdk/system/unload").when(unloadersUnlaod);
+ },
+ unload: function unload(callback, container) {
+ // Calling with no arguments runs all the unloader callbacks
+ if (callback == null) {
+ unloadNS(this).unloadersUnlaod();
+ return null;
+ }
+
+ var remover = removeUnloader.bind(null, unloader, unloadNS(this).unloaders);
+
+ // The callback is bound to the lifetime of the container if we have one
+ if (container != null) {
+ // Remove the unloader when the container unloads
+ container.addEventListener("unload", remover, false);
+
+ // Wrap the callback to additionally remove the unload listener
+ let origCallback = callback;
+ callback = function() {
+ container.removeEventListener("unload", remover, false);
+ origCallback();
+ }
+ }
+
+ // Wrap the callback in a function that ignores failures
+ function unloader() {
+ try {
+ callback();
+ }
+ catch(e) {
+ console.error(e);
+ }
+ }
+ unloadNS(this).unloaders.push(unloader);
+
+ // Provide a way to remove the unloader
+ return remover;
+ }
+});
+
+function removeUnloader(unloader, unloaders) {
+ let index = unloaders.indexOf(unloader);
+ if (index != -1)
+ unloaders.splice(index, 1);
+}
+
+/**
+ * Save callbacks to run when unloading. Optionally scope the callback to a
+ * container, e.g., window. Provide a way to run all the callbacks.
+ *
+ * @usage unload(): Run all callbacks and release them.
+ *
+ * @usage unload(callback): Add a callback to run on unload.
+ * @param [function] callback: 0-parameter function to call on unload.
+ * @return [function]: A 0-parameter function that undoes adding the callback.
+ *
+ * @usage unload(callback, container) Add a scoped callback to run on unload.
+ * @param [function] callback: 0-parameter function to call on unload.
+ * @param [node] container: Remove the callback when this container unloads.
+ * @return [function]: A 0-parameter function that undoes adding the callback.
+ */
+const gUnload = Unloader();
+exports.unload = gUnload.unload.bind(gUnload);
diff --git a/packages/vold-utils/lib/window-watcher.js b/packages/vold-utils/lib/window-watcher.js
new file mode 100644
index 0000000..ab4f449
--- /dev/null
+++ b/packages/vold-utils/lib/window-watcher.js
@@ -0,0 +1,91 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Twitter Address Bar Search.
+ *
+ * The Initial Developer of the Original Code is The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Edward Lee <edilee@mozilla.com>
+ * Erik Vold <erikvvold@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const {Cu} = require("chrome");
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+var {unload} = require("unload+");
+
+/**
+ * Apply a callback to each open and new browser windows.
+ *
+ * @usage watchWindows(callback): Apply a callback to each browser window.
+ * @param [function] callback: 1-parameter function that gets a browser window.
+ */
+exports.watchWindows = function watchWindows(callback) {
+ // Wrap the callback in a function that ignores failures
+ function watcher(window) {
+ try {
+ // Now that the window has loaded, only handle browser windows
+ let {documentElement} = window.document;
+ if (documentElement.getAttribute("windowtype") == "navigator:browser")
+ callback(window);
+ }
+ catch(ex) {}
+ }
+
+ // Wait for the window to finish loading before running the callback
+ function runOnLoad(window) {
+ // Listen for one load event before checking the window type
+ window.addEventListener("load", function runOnce() {
+ window.removeEventListener("load", runOnce, false);
+ watcher(window);
+ }, false);
+ }
+
+ // Add functionality to existing windows
+ let windows = Services.wm.getEnumerator(null);
+ while (windows.hasMoreElements()) {
+ // Only run the watcher immediately if the window is completely loaded
+ let window = windows.getNext();
+ if (window.document.readyState == "complete")
+ watcher(window);
+ // Wait for the window to load before continuing
+ else
+ runOnLoad(window);
+ }
+
+ // Watch for new browser windows opening then wait for it to load
+ function windowWatcher(subject, topic) {
+ if (topic == "domwindowopened")
+ runOnLoad(subject);
+ }
+ Services.ww.registerNotification(windowWatcher);
+
+ // Make sure to stop watching for windows if we're unloading
+ unload(function() Services.ww.unregisterNotification(windowWatcher));
+};
diff --git a/packages/vold-utils/lib/xpcom-utils.js b/packages/vold-utils/lib/xpcom-utils.js
new file mode 100644
index 0000000..f453b34
--- /dev/null
+++ b/packages/vold-utils/lib/xpcom-utils.js
@@ -0,0 +1,3 @@
+
+require("chrome").Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+exports.XPCOMUtils = XPCOMUtils;
diff --git a/packages/vold-utils/package.json b/packages/vold-utils/package.json
new file mode 100644
index 0000000..ad549b7
--- /dev/null
+++ b/packages/vold-utils/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "vold-utils",
+ "description": "Utilitys for Jetpacks",
+ "author": "Erik Vold (http://erikvold.com/) <erikvvold@gmail.com>",
+ "version": "1.1",
+ "dependencies": ["api-utils"]
+}
diff --git a/packages/vold-utils/tests/test-unload+.js b/packages/vold-utils/tests/test-unload+.js
new file mode 100644
index 0000000..92f3e0c
--- /dev/null
+++ b/packages/vold-utils/tests/test-unload+.js
@@ -0,0 +1,119 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+var timer = require("timer");
+var {Cc,Ci} = require("chrome");
+const windowUtils = require("window-utils");
+const { Loader } = require('test-harness/loader');
+
+function makeEmptyWindow() {
+ var xulNs = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var blankXul = ('<?xml version="1.0"?>' +
+ '<?xml-stylesheet href="chrome://global/skin/" ' +
+ ' type="text/css"?>' +
+ '<window xmlns="' + xulNs + '">' +
+ '</window>');
+ var url = "data:application/vnd.mozilla.xul+xml," + escape(blankXul);
+ var features = ["chrome", "width=10", "height=10"];
+
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
+ .getService(Ci.nsIWindowWatcher);
+ return ww.openWindow(null, url, null, features.join(","), null);
+}
+
+exports.testUnloading = function(test) {
+ var loader = Loader(module);
+ var {unload} = loader.require("unload+");
+ var unloadCalled = 0;
+
+ function unloader() {
+ unloadCalled++;
+ throw "error";
+ }
+ unload(unloader);
+
+ function unloader2() unloadCalled++;
+ var removeUnloader2 = unload(unloader2);
+
+ function unloader3() unloadCalled++;
+ unload(unloader3);
+
+ // remove unloader2
+ removeUnloader2();
+
+ loader.unload();
+ test.assertEqual(
+ unloadCalled, 2, "Unloader functions are called on unload.");
+};
+
+exports.testUnloadingWindow = function(test) {
+ test.waitUntilDone();
+
+ var loader = Loader(module);
+ var {unload} = loader.require("unload+");
+ var unloadCalled = 0;
+ var finished = false;
+ var myWindow;
+
+ var delegate = {
+ onTrack: function(window) {
+ if (window == myWindow) {
+ test.pass("onTrack() called with our test window");
+
+ let unloader = function unloader() {
+ unloadCalled++;
+ }
+ unload(unloader, window);
+ unload(unloader);
+
+ timer.setTimeout(function() {
+ window.close();
+
+ test.assertEqual(
+ unloadCalled, 0, "no unloaders called.");
+
+ if (window.closed) {
+ test.pass("window closed");
+ } else {
+ test.fail("window is not closed!");
+ }
+
+ timer.setTimeout(function() {
+ test.assertEqual(
+ unloadCalled, 0, "zero unloaders called.");
+
+ loader.unload();
+
+ test.assertEqual(
+ unloadCalled, 1, "one unloaders called.");
+
+ if (finished) {
+ test.pass("finished");
+ test.done();
+ } else {
+ test.fail("not finished!");
+ }
+ }, 1);
+ }, 1);
+ }
+ },
+ onUntrack: function(window) {
+ if (window == myWindow) {
+ test.pass("onUntrack() called with our test window");
+
+ if (!finished) {
+ finished = true;
+ myWindow = null;
+ wt.unload();
+ } else {
+ test.fail("finishTest() called multiple times.");
+ }
+ }
+ }
+ };
+
+ var wt = new windowUtils.WindowTracker(delegate);
+ myWindow = makeEmptyWindow();
+};