Dactyloidae/toolkit/jetpack/sdk/ui/button/view.js

243 lines
6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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';
module.metadata = {
'stability': 'experimental',
'engines': {
'Firefox': '> 28'
}
};
const { Cu } = require('chrome');
const { on, off, emit } = require('../../event/core');
const { data } = require('sdk/self');
const { isObject, isNil } = require('../../lang/type');
const { getMostRecentBrowserWindow } = require('../../window/utils');
const { ignoreWindow } = require('../../private-browsing/utils');
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
const { AREA_PANEL, AREA_NAVBAR } = CustomizableUI;
const { events: viewEvents } = require('./view/events');
const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
const views = new Map();
const customizedWindows = new WeakMap();
const buttonListener = {
onCustomizeStart: window => {
for (let [id, view] of views) {
setIcon(id, window, view.icon);
setLabel(id, window, view.label);
}
customizedWindows.set(window, true);
},
onCustomizeEnd: window => {
customizedWindows.delete(window);
for (let [id, ] of views) {
let placement = CustomizableUI.getPlacementOfWidget(id);
if (placement)
emit(viewEvents, 'data', { type: 'update', target: id, window: window });
}
},
onWidgetAfterDOMChange: (node, nextNode, container) => {
let { id } = node;
let view = views.get(id);
let window = node.ownerDocument.defaultView;
if (view) {
emit(viewEvents, 'data', { type: 'update', target: id, window: window });
}
}
};
CustomizableUI.addListener(buttonListener);
require('../../system/unload').when( _ =>
CustomizableUI.removeListener(buttonListener)
);
function getNode(id, window) {
return !views.has(id) || ignoreWindow(window)
? null
: CustomizableUI.getWidget(id).forWindow(window).node
};
function isInToolbar(id) {
let placement = CustomizableUI.getPlacementOfWidget(id);
return placement && CustomizableUI.getAreaType(placement.area) === 'toolbar';
}
function getImage(icon, isInToolbar, pixelRatio) {
let targetSize = (isInToolbar ? 18 : 32) * pixelRatio;
let bestSize = 0;
let image = icon;
if (isObject(icon)) {
for (let size of Object.keys(icon)) {
size = +size;
let offset = targetSize - size;
if (offset === 0) {
bestSize = size;
break;
}
let delta = Math.abs(offset) - Math.abs(targetSize - bestSize);
if (delta < 0)
bestSize = size;
}
image = icon[bestSize];
}
if (image.indexOf('./') === 0)
return data.url(image.substr(2));
return image;
}
function nodeFor(id, window=getMostRecentBrowserWindow()) {
return customizedWindows.has(window) ? null : getNode(id, window);
};
exports.nodeFor = nodeFor;
function create(options) {
let { id, label, icon, type, badge } = options;
if (views.has(id))
throw new Error('The ID "' + id + '" seems already used.');
CustomizableUI.createWidget({
id: id,
type: 'custom',
removable: true,
defaultArea: AREA_NAVBAR,
allowedAreas: [ AREA_PANEL, AREA_NAVBAR ],
onBuild: function(document) {
let window = document.defaultView;
let node = document.createElementNS(XUL_NS, 'toolbarbutton');
let image = getImage(icon, true, window.devicePixelRatio);
if (ignoreWindow(window))
node.style.display = 'none';
node.setAttribute('id', this.id);
node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional badged-button');
node.setAttribute('type', type);
node.setAttribute('label', label);
node.setAttribute('tooltiptext', label);
node.setAttribute('image', image);
node.setAttribute('constrain-size', 'true');
views.set(id, {
area: this.currentArea,
icon: icon,
label: label
});
node.addEventListener('command', function(event) {
if (views.has(id)) {
emit(viewEvents, 'data', {
type: 'click',
target: id,
window: event.view,
checked: node.checked
});
}
});
return node;
}
});
};
exports.create = create;
function dispose(id) {
if (!views.has(id)) return;
views.delete(id);
CustomizableUI.destroyWidget(id);
}
exports.dispose = dispose;
function setIcon(id, window, icon) {
let node = getNode(id, window);
if (node) {
icon = customizedWindows.has(window) ? views.get(id).icon : icon;
let image = getImage(icon, isInToolbar(id), window.devicePixelRatio);
node.setAttribute('image', image);
}
}
exports.setIcon = setIcon;
function setLabel(id, window, label) {
let node = nodeFor(id, window);
if (node) {
node.setAttribute('label', label);
node.setAttribute('tooltiptext', label);
}
}
exports.setLabel = setLabel;
function setDisabled(id, window, disabled) {
let node = nodeFor(id, window);
if (node)
node.disabled = disabled;
}
exports.setDisabled = setDisabled;
function setChecked(id, window, checked) {
let node = nodeFor(id, window);
if (node)
node.checked = checked;
}
exports.setChecked = setChecked;
function setBadge(id, window, badge, color) {
let node = nodeFor(id, window);
if (node) {
// `Array.from` is needed to handle unicode symbol properly:
// '𝐀𝐁'.length is 4 where Array.from('𝐀𝐁').length is 2
let text = isNil(badge)
? ''
: Array.from(String(badge)).slice(0, 4).join('');
node.setAttribute('badge', text);
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
'class', 'toolbarbutton-badge');
if (badgeNode)
badgeNode.style.backgroundColor = isNil(color) ? '' : color;
}
}
exports.setBadge = setBadge;
function click(id) {
let node = nodeFor(id);
if (node)
node.click();
}
exports.click = click;