javascript – Firefox SDK附加组件,同时在右侧和左侧都有侧边栏
作者:互联网
我正在编写一个基于Firefox Add-on SDK的扩展.我需要同时使用左侧边栏和右侧边栏.默认情况下,我可以在左侧显示一个.我已经阅读过使用CSS在左侧和右侧的ui/sidebar之间进行更改:
@namespace url(http://www.mozilla.org/keymaster/gat...re.is.only.xul);
hbox#browser {
direction: rtl !important;
}
hbox#browser > vbox {
direction: ltr !important;
}
但是,这似乎是旧学校的一部分,因为我没有定义.xul文件.
你知道在浏览器的左侧和右侧同时有侧边栏吗?
解决方法:
你的问题并不清楚你到底想要什么.因此,我做了一些假设.
最终,没有官方支持的方式一次显示多个“侧边栏”.默认情况下,Firefox浏览器窗口中只存在一个“侧边栏”.虽然没有支持的API为您提供多个侧边栏,但可以通过修改Firefox浏览器窗口的XUL DOM来创建多个侧边栏(界面面板).
下面的代码创建了用户界面面板(侧栏,顶栏,底栏和窗口),它们位于浏览器窗口的顶部,底部,左侧,右侧或单独的窗口中.可以创建界面面板,使它们相对于窗口(当您切换选项卡时它们保持可见;如书签/历史侧边栏),或者相对于选项卡(它们仅在选项卡所在的选项卡上可见)显示已创建;与开发人员工具一样).
createInterfacePanelIframe()方法创建一个包含< iframe>的接口面板. (或< browser>),您可以为其提供网址和所有属性. < iframe>的元素和<分离器>返回给调用者,以便您可以对它们执行其他操作,或者从Firefox窗口的DOM中删除它们以删除/隐藏它们.此方法创建< iframe>并调用createInterfacePanel()将其插入到指定位置的Firefox浏览器中.
createInterfacePanel()方法将把传递给它的XUL元素(使用Document Fragment传递多个元素)放入Firefox浏览器窗口的DOM,以及< splitter>在指定的位置(左侧,右侧,顶部,底部,窗口以及相对于窗口或选项卡).您可以指定希望界面面板位于的窗口和/或选项卡,或者,默认情况下,界面面板将插入当前窗口/选项卡中.如果已指定要插入的接口面板,则将在现有接口面板旁边创建另一个面板.您可以创建的数量没有固有的限制(例如,如果您愿意,您可以在右侧有10个面板).
此外,下面还有一个演示Firefox Add-on SDK扩展,它添加了6个不同的按钮.前5个按钮创建或销毁界面面板.徽章指示按钮将影响哪个面板(左,右,顶部,底部和窗口).绿色徽章表示将创建界面面板.红色徽章表示单击将破坏现有面板.第六个按钮切换Tab相对界面面板和Window相对界面面板之间的其他按钮.如果您创建所有面板,您将有8个面板(4个用于窗口,4个用于当前选项卡)以及两个单独的窗口(每个浏览器窗口).
以下是演示附加组件的外观.下图中显示的面板很窄和/或很短,以便在此页面上显示.下面的代码允许您根据需要制作任何尺寸的面板,并使用户能够调整它们的大小.
这是创建侧边栏(界面面板)的代码:
sidebars.js:
/**
* createInterfacePanelIframe(location,options)
* Creates an <iframe> based panel within the current tab, within the
* current window, or opens a window, for use as an user interface
* box. If it is not a window, it is associated with the current
* browser tab, or the current browser window depending on the
* byWindow option.
* @param location
* Placement of the panel [right|left|top|bottom|window]
* The default location is 'right'.
* @param options
* An Object containing optional parameters.
* height
* The height of a top or bottom sidebar
* Default is 200.
* width
* The width of a left or right sidebar
* Default is 200.
* size
* Width if on left or right. Height if top or bottom.
* Both width and height if location='window' unless
* features is a string.
* Default is 200.
* id
* The ID to assign to the iframe. Default is
* 'makyen-interface-panel'
* The <splitter> will be assigned the
* ID = id + '-splitter'
* url
* This is the chrome:// URL to use for the contents
* of the iframe or the window.
* the default is:
* 'chrome://devtools/content/framework/toolbox.xul'
* iframeAttributes
* An Object
* Contains key/value pairs which are applies as attributes
* of the iframe. These will override any defaults or other
* attributes derived from other options (e.g. id, height,
* width, type, etc.). If the value of the property is null
* then that attribute will be removed.
* useBrowser
* If true, a <browser> element is used instead of an <iframe>.
* byWindow
* If true then the created sidebar is for the window
* not the tab.
* tab
* The tab for which to create the sidebar. If not
* specified, the current tab is used.
* window
* The window for which to create the sidebar. If not
* specified, the current window is used.
* features
* The features string for the window. See:
* https://developer.mozilla.org/en-US/docs/Web/API/Window.open
*
* returns [splitterEl, iframeEl]
* The elements for the <splitter> and <iframe>.
*
* Copyright 2014-2016 by Makyen.
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/.
**/
function createInterfacePanelIframe(location,options){
//options
let size,width,height,id,chromeUrl;
if(typeof options === 'object'){
size = options.size;
width = options.width;
height = options.height;
id = options.id;
chromeUrl = options.url;
}
if(!width && !height && size){
width = size;
height = size;
}
[width,height] = getSizeWithDefaults(location,width,height);
//defaults
id = typeof id !== 'string' ? 'makyen-interface-panel' : id;
chromeUrl = typeof chromeUrl !== 'string'
? 'chrome://devtools/content/framework/toolbox.xul'
: chromeUrl;
//Create some common variables if they do not exist.
//This gets the currently active Firefox XUL window.
// Add/remove a '/' to comment/un-comment the code appropriate for your add-on type.
//* Add-on SDK:
let activeWindow = options.window ?
options.window : require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
Components.utils.import('resource://gre/modules/Services.jsm');//Services
let activeWindow = options.window ?
options.window : Services.wm.getMostRecentWindow('navigator:browser');
//*/
let mainDocument = activeWindow.document;
//Create the <iframe> use
//mainDocument for the XUL namespace.
let iframeEl;
if(options.useBrowser){
iframeEl = mainDocument.createElement('browser');
} else {
iframeEl = mainDocument.createElement('iframe');
}
iframeEl.id = id;
iframeEl.setAttribute('src',chromeUrl);
iframeEl.setAttribute("tooltip", "aHTMLTooltip");
iframeEl.setAttribute("autocompleteenabled", true);
iframeEl.setAttribute("autocompletepopup", "PopupAutoComplete");
iframeEl.setAttribute("disablehistory",true);
iframeEl.setAttribute('type', 'content');
if(typeof height === 'number'){
iframeEl.setAttribute('height', height.toString());
}
if(typeof width === 'number'){
iframeEl.setAttribute('width', width.toString());
}
if(typeof options.iframeAttributes === 'object'){
let attrs = options.iframeAttributes;
for(let attr in attrs){
if(attrs.hasOwnProperty(attr)) {
if(attrs[attr]===null){
iframeEl.removeAttribute(attr);
}else{
iframeEl.setAttribute(attr, attrs[attr]);
}
}
}
}
//Call createInterfacePanel
let splitterEl;
let newOptions = {};
if(height) {
newOptions.height = height;
}
if(width) {
newOptions.width = width;
}
newOptions.url = chromeUrl;
if(options.tab){
newOptions.tab = options.tab;
}
if(options.window){
newOptions.window = options.window;
}
if(options.features){
newOptions.features = options.features;
}
if(options.byWindow){
newOptions.byWindow = options.byWindow;
}
newOptions.id = id + '-splitter';
splitterEl = createInterfacePanel(location, iframeEl, newOptions)
return [splitterEl, iframeEl];
}
/**
* createInterfacePanel(location,objectEl,options)
* Creates a panel within the current tab, or opens a window, for use as a
* user interface box. If not a window, it is associated with the current
* browser tab.
* @param location
* Placement of the panel [right|left|top|bottom|window]
* The default location is 'right'.
* @param objectEl
* The element of an XUL object that will be inserted into
* the DOM such that it is within the current tab.
* Some examples of possible objects are <iframe>,
* <browser>, <box>, <hbox>, <vbox>, etc.
* If the location='window' and features is not a string
* and this is a number then it is used as the width of the
* window.
* @param options
* An Object containing optional parameters.
* height
* The height of a top or bottom sidebar
* Default is 200.
* width
* The width of a left or right sidebar
* Default is 200.
* size
* Width if on left or right. Height if top or bottom.
* Both width and height if location='window' unless
* features is a string.
* Default is 200.
* If none of height, width or size is specified, then the
* size of the sidebar should be specified within the XUL
* elements referenced by objectEl.
* sizeEl
* The element that is to contain attributes of 'width' and
* 'height'. If location=='left'|'right' then the
* 'height' attribute is removed prior to the objectEl
* being inserted into the DOM.
* This is an optional spearate reference for the size element
* in case the objectEl is a documentFragment containing
* multiple elements. However, normal usage is for
* objectEl === sizeEl (which is default if unspecified)
* when location != 'window'.
* When location == 'window' and features is not a string,
* and sizeEl is a number then it is used as the height
* of the window.
* If features is a string, it is assumed the height is
* set in that, or elsewhere (e.g. in the XUL).
* id
* The ID to assign to the <splitter>. The default is:
* 'makyen-interface-panel-splitter'.
* url
* This is the chrome:// URL to use for the contents
* of the window.
* the default is:
* 'chrome://devtools/content/framework/toolbox.xul'
* byWindow
* If true then the created sidebar is for the window
* not the tab.
* tab
* The tab for which to create the sidebar. If not
* specified, the current tab is used.
* window
* The window for which to create the sidebar. If not
* specified, the current window is used.
* features
* The features string for the window. See:
* https://developer.mozilla.org/en-US/docs/Web/API/Window.open
* If features is a string, it is assumed the width is
* set in that, or elsewhere (e.g. in the XUL).
*
* returns
* if location != 'window':
* splitterEl, The element for the <splitter>.
* if location == 'window':
* The windowObjectReference returned by window.open().
*
* Copyright 2014-2016 by Makyen.
* Released under the MPL 2.0. http://mozilla.org/MPL/2.0/.
**/
function createInterfacePanel(location,objectEl,options) {
//function createInterfacePanel(location,objectEl,sizeEl,id,chromeUrl,features) {
//options
let size,width,height,sizeEl,id,chromeUrl,byWindow,features;
if(typeof options === 'object'){
size = options.size;
width = options.width;
height = options.height;
//If a separate sizeEl is not specified, then use the ObjectEl for sizeEl.
// This is so we could pass a document fragment with multiple elements,
// But only one which should have a specified size.
sizeEl = options.sizeEl? options.sizeEl:objectEl;
id = options.id;
chromeUrl = options.url;
byWindow = options.byWindow;
features = options.features;
}
if(!width && !height && size){
width = size;
height = size;
}
[width,height] = getSizeWithDefaults(location,width,height);
//Set location default:
location = typeof location !== 'string' ? 'right' : location;
if(location == 'window') {
if(typeof features !== 'string') {
let widthText = 'width=' + width.toString() + ',';
let heightText = 'height=' + height.toString() + ',';
features = widthText + heightText
+ 'menubar=no,toolbar=no,location=no,personalbar=no'
+ ',status=no,chrome=yes,resizable,centerscreen';
}
}
id = typeof id !== 'string' ? 'makyen-interface-panel-splitter' : id;
chromeUrl = typeof chromeUrl !== 'string'
? 'chrome://devtools/content/framework/toolbox.xul'
: chromeUrl;
//Create some common variables if they do not exist.
//This gets the currently active Firefox XUL window.
// Add/remove a '/' to comment/un-comment the code appropriate for your add-on type.
//* Add-on SDK:
let activeWindow = options.window ?
options.window : require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
Components.utils.import('resource://gre/modules/Services.jsm');//Services
let activeWindow = options.window ?
options.window : Services.wm.getMostRecentWindow('navigator:browser');
//*/
if (typeof gBrowser === 'undefined') {
//If there is no gBrowser defined, get it
var gBrowser = activeWindow.gBrowser;
}
//Get the tab & notification box (container for tab UI).
let tab = options.tab?options.tab:gBrowser.selectedTab;
let browserForTab = gBrowser.getBrowserForTab( tab );
let notificationBox = gBrowser.getNotificationBox( browserForTab );
let ownerDocument = gBrowser.ownerDocument;
//Create a Document Fragment.
//If doing multiple DOM additions, we should be in the habit
// of doing things in a way which causes the least number of reflows.
// We know that we are going to add more than one thing, so use a
// document fragment.
let docFrag = ownerDocument.createDocumentFragment();
//ownerDocument must be used here in order to have the XUL namespace
// or the splitter causes problems.
// createElementNS() does not work here.
let splitterEl = ownerDocument.createElement('splitter');
splitterEl.id = id ;
//Look for the child element with class='browserSidebarContainer'.
//It is the element in procimity to which the <splitter>
//and objectEl will be placed.
let theChild = notificationBox.firstChild;
while (!theChild.hasAttribute('class')
|| (theChild.getAttribute('class').indexOf('browserSidebarContainer') === -1)
) {
theChild = theChild.nextSibling;
if(!theChild) {
//We failed to find the correct node.
//This implies that the structure Firefox
// uses has changed significantly and it should
// be assumed that the extension is no longer compatible.
return null;
}
}
let tabBrowser = ownerDocument.getElementById('content');
let heightAttr='height';
let widthAttr='width';
if(byWindow) {
notificationBox = ownerDocument.getElementById('browser');
theChild = ownerDocument.getElementById('appcontent');
//When Window referenced, where we need to put the elements is
// slightly different, but works out to just being a swapping
// of 'location' values.
//Swap the width and height attributes and options.
heightAttr='width';
widthAttr='height';
let foo = width;
width = height;
height = foo;
foo = options.width;
options.width = options.height;
options.height = foo;
switch(location) {
case 'window' :
//no change
break;
case 'top' :
location = 'left'
break;
case 'bottom' :
location = 'right'
break;
case 'left' :
location = 'top'
break;
case 'right' :
default :
location = 'bottom'
break;
}
}
switch(location) {
case 'window' :
return activeWindow.open(chromeUrl,'_blank',features);
break;
case 'top' :
if(options.size || options.height) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(widthAttr);
sizeEl.setAttribute(heightAttr,height);
}
docFrag.appendChild(objectEl);
docFrag.appendChild(splitterEl);
//Inserting the document fragment results in the same
// DOM structure as if you Inserted each child of the
// fragment separately. (i.e. the document fragment
// is just a temporary container).
//Insert the interface prior to theChild.
notificationBox.insertBefore(docFrag,theChild);
break;
case 'bottom' :
if(options.size || options.height) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(widthAttr);
sizeEl.setAttribute(heightAttr,height);
}
docFrag.appendChild(splitterEl);
docFrag.appendChild(objectEl);
//Insert the interface just after theChild.
notificationBox.insertBefore(docFrag,theChild.nextSibling);
break;
case 'left' :
if(options.size || options.width) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(heightAttr);
sizeEl.setAttribute(widthAttr,width);
}
docFrag.appendChild(objectEl);
//Splitter is second in this orientaiton.
docFrag.appendChild(splitterEl);
if(byWindow) {
//Insert the interface prior to the tabbrowser to put
// global notifications above the top sidebar.
theChild.insertBefore(docFrag,tabBrowser);
}else{
//Insert the interface as the first child of theChild.
theChild.insertBefore(docFrag,theChild.firstChild);
}
break;
case 'right' :
default :
//Right orientaiton, the default.
if(options.size || options.width) {
//Don't mess with the height/size unless it was specified
sizeEl.removeAttribute(heightAttr);
sizeEl.setAttribute(widthAttr,width);
}
docFrag.appendChild(splitterEl);
docFrag.appendChild(objectEl);
//Insert the interface as the last child of theChild.
theChild.appendChild(docFrag);
break;
}
return splitterEl;
}
function getSizeWithDefaults(location,width,height){
let defaultSize = 200;
switch(location) {
case 'window' :
width = ( (typeof width !== 'number') || width<1) ? defaultSize : width;
height = ( (typeof height !== 'number') || height<1) ? defaultSize : height;
break;
case 'top' :
case 'bottom' :
width = null;
height = ( (typeof height !== 'number') || height<1) ? defaultSize : height;
break;
case 'left' :
case 'right' :
default :
width = ( (typeof width !== 'number') || width<1) ? defaultSize : width;
height = null;
break;
}
return [width,height];
}
exports.createInterfacePanel=createInterfacePanel;
exports.createInterfacePanelIframe=createInterfacePanelIframe;
Firefox Add-on SDK扩展示例是:
的package.json:
{
"title": "Demo Sidebars",
"name": "demo-sidebars",
"version": "0.0.1",
"description": "Demo creating Window related sidebars",
"main": "index.js",
"author": "Makyen",
"permissions": {"private-browsing": true},
"engines": {
"firefox": ">=38.0a1",
"fennec": ">=38.0a1"
},
"license": "MIT",
"keywords": [
"jetpack"
]
}
数据/ sidebar.html:
<html>
<head>
<meta charset="utf-8">
</head>
<body style="background-color:white;">
This is a Window.
</body>
</html>
index.js:
var utils = require('sdk/window/utils');
var tabs = require('sdk/tabs');
var tabsUtils = require('sdk/tabs/utils');
var self = require('sdk/self');
//For testing: Open the Browser Console
var activeWin = utils.getMostRecentBrowserWindow();
activeWin.document.getElementById('menu_browserConsole').doCommand();
var mySidebars = require('./sidebars.js');
var sidebarSize = 100; //Width & height to use
var sidebarByWindow = false;
var sidebars = {};
//The buttons
var buttons = {
'◀': {where:'Left'},
'▶': {where:'Right'},
'▲': {where:'Top'},
'▼': {where:'Bottom'},
'☐': {where:'Window'}
};
//Create Buttons
var sdkActionButtons = require('sdk/ui/button/action');
for(let badge in buttons){
buttons[badge].button = sdkActionButtons.ActionButton({
id: 'openSidebar' + buttons[badge].where,
label: 'Open ' + buttons[badge].where + ' Sidebar',
badge: badge,
badgeColor: 'green',
icon: './icons/Aurora-icon64.png',
onClick: handleButtonClick
});
}
function handleButtonClick(state){
let where = buttons[state.badge].where.toLowerCase();
let stateType = getSidebarByWindowText();
let sidebarId = getSidebarId(state.badge,sidebarByWindow);
//With this state being kept by window and tab, the checked property does
// not accurately track what we need to be doing, so use badgeColor and
// action buttons.
if(sidebars[sidebarId]){
//If we have a sidebar for this combo, then
let elements = sidebars[sidebarId];
if(elements){
if(where==='window'){
try{
elements[0].close();
}catch(e){
//Do nothing. We should be tracking the state of the window so
// users can use the close button. We are not, so trying to
// close an already closed window could throw an error.
}
} else {
elements.forEach(el => {el.remove();});
}
}
delete sidebars[sidebarId];
}else{
//Create the sidebar and keep track of it so it can be removed.
sidebars[sidebarId] = mySidebars.createInterfacePanelIframe(where,{
url:self.data.url('sidebar.html'),
byWindow:sidebarByWindow,
size:sidebarSize,
id:'makyen-interface-panel-' + stateType + '- ' + where
});
//Make the text reflect the sidebar
if(where !== 'window'){
setBodyText(sidebarId, 'This is a ' +stateType + ' ' + where + ' Sidebar.');
sidebars[sidebarId][1].addEventListener('load', setBodyText.bind(null
,sidebarId
,'This is a ' + stateType.toUpperCase() + ' ' + where + ' Sidebar.'),true);
}
}
updateButtonBadgeColors();
}
function setBodyText(sidebarId,text){
let doc = sidebars[sidebarId][1].contentDocument;
doc.body.textContent = text;
}
function getSidebarId(badge,sidebarByWindow,domWin){
let where = buttons[badge].where.toLowerCase();
let stateType = getSidebarByWindowText();
domWin = domWin?domWin:utils.getMostRecentBrowserWindow();
let winId = utils.getOuterId(domWin);
//This should get the tab ID from any window, not just the active window.
let tabId = tabsUtils.getTabId(tabsUtils.getActiveTab(domWin));
let id = sidebarByWindow?winId:tabId;
return stateType+id+where;
}
function getSidebarByWindowText(){
return sidebarByWindow?'window':'tab';
}
function updateButtonBadgeColors(){
//Update the badge colors in all windows based on if there is a sidebar of the
// current type for the window/tab.
let allWindows = utils.windows('navigator:browser',{includePrivate:true});
for(let win of allWindows){
for(let badge in buttons){
let sidebarId = getSidebarId(badge,sidebarByWindow,win);
buttons[badge].button.state(win,{
badgeColor : sidebars[sidebarId]?'red':'green'
});
}
}
}
//update badge colors each time the active tab changes.
tabs.on('activate',updateButtonBadgeColors);
//var sdkToggleButtons = require('sdk/ui/button/toggle');
var windowTabLabelText = 'Sidebars are associated with ';
var windowTabToggleButton = sdkActionButtons.ActionButton({
id: 'windowTabToggleButton',
label: windowTabLabelText + getSidebarByWindowText(),
icon: './icons/Aurora-icon64.png',
onClick: handlewindowTabToggle
});
function handlewindowTabToggle(state){
if(!state.badge){
windowTabToggleButton.badge= '☐';
windowTabToggleButton.badgeColor= 'blue';
sidebarByWindow = true;
} else {
windowTabToggleButton.badge= '';
sidebarByWindow = false;
}
windowTabToggleButton.label = windowTabLabelText + getSidebarByWindowText();
updateButtonBadgeColors();
}
这个答案中的代码经过调整,并从我对“Firefox Extension, Window related sidebar”的回答中进行了大量扩展.这个答案有很多额外的信息,比如在Firefox浏览器中如何构建侧边栏(界面面板).
标签:javascript,firefox-addon,firefox-addon-sdk,firefox-sidebar 来源: https://codeday.me/bug/20191002/1845175.html