diff options
Diffstat (limited to 'packages/menuitems/lib/menuitems.js')
-rw-r--r-- | packages/menuitems/lib/menuitems.js | 195 |
1 files changed, 195 insertions, 0 deletions
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; |