menuitem
Version:
[](https://travis-ci.org/jetpack-labs/menuitem)
219 lines (182 loc) • 6.45 kB
JavaScript
/* 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/. */
;
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 menuitemNS = require("sdk/core/namespace").ns();
const { unload } = require('pathfinder/lib/addon/unload');
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', 'number'] },
label: { is: ["string"] },
include: { is: ['string', 'undefined'] },
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'] },
useChrome: { map: function(v) !!v }
});
}
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] = overwrites[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 (menuitemNS(self).destroyed) return;
if (options.include) {
if (options.include != window.location) return;
}
else if (!isBrowser(window)) {
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
let added = updateMenuitemParent(menuitem, options, function(id) window.document.getElementById(id));
menuitem.addEventListener("command", function() {
if (!self.disabled)
emit(self, 'command', options.useChrome ? window : null);
}, 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) {
if (!before) {
parent.appendChild(menuitem);
return true;
}
parent.insertBefore(menuitem, insertBefore(parent, before));
return true;
}
return false;
}
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;