Files
palemoon27/toolkit/jetpack/sdk/ui/button/view.js
T
2018-07-24 23:18:01 +08:00

244 lines
6.0 KiB
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/. */
'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('sdk-button', '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;