I am writing a simple firefox addon using addon-sdk-1.17 . I am having trouble updating the UI of my addon. If I do a cfx run, the addon looks normal, but if I do "cfx xpi" and load it into a profile that already has a previous version of the addon, well thats where I run into problems.
A simple example of this can be seen by an example mozilla has in their toolbar tutorial. It can be found at: https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/ui_toolbar
If I package (cfx xpi) the following code (assuming the icons and html file exist), it works as expected:
var { ActionButton } = require('sdk/ui/button/action');
var { Toolbar } = require("sdk/ui/toolbar");
var { Frame } = require("sdk/ui/frame");
var previous = ActionButton({
id: "previous",
label: "previous",
icon: "./icons/previous.png"
});
var next = ActionButton({
id: "next",
label: "next",
icon: "./icons/next.png"
});
var play = ActionButton({
id: "play",
label: "play",
icon: "./icons/play.png"
});
var frame = new Frame({
url: "./frame-player.html"
});
var toolbar = Toolbar({
title: "Player",
items: [previous, next, play, frame]
});
But if i want to add an additional button to it and I decide to change the url of the frame, they don't update to the toolbar. For example, after loading the above addon into my profile, if I make the following changes to the main.js:
var { ActionButton } = require('sdk/ui/button/action');
var { Toolbar } = require("sdk/ui/toolbar");
var { Frame } = require("sdk/ui/frame");
var previous = ActionButton({
id: "previous",
label: "previous",
icon: "./icons/previous.png"
});
var next = ActionButton({
id: "next",
label: "next",
icon: "./icons/next.png"
});
var play = ActionButton({
id: "play",
label: "play",
icon: "./icons/play.png"
});
var mute = ActionButton({
id: "mute",
label: "mute",
icon: "./icons/mute.png"
});
var frame = new Frame({
url: "./new-frame-player.html"
});
var toolbar = Toolbar({
title: "Player",
items: [previous, next, play, mute, frame]
});
The toolbar will not have either (frame-player.html or new-frame-player.html) loaded on the toolbar, and the mute button will not be located on the toolbar either. Again, this works fine for "cfx run" or if I install the addon to a profile that doesn't have the previous version of the addon.
I assume there is something dumb I am doing or there is an easy solution, but I haven't seen documentation on this anywhere. Not sure if I just overlooked something or what.
The "problem" here is, that Firefox saves the location where buttons had been manually placed, even when the extension is uninstalled. You can reset this data by hitting "Restore Defaults" in the cutomization tab.
Alternatively you can force-move your frame using the CustomizableUI.jsm:
var CustomizableUI = require("resource:///modules/CustomizableUI.jsm");
CustomizableUI.addWidgetToArea(frame.id, "inner-" + toolbar.id);
Or if you want to move a button:
var CutomizableUI = require("resource://modules/CustomizableUI.jsm");
var { getNodeView } = require("sdk/view/core");
CustomizableUI.addWidgetToArea(getNodeView(button).id, "inner-" + toolbar.id);
Related
With the coming of multi-process Firefox, I have decided to revamp my addon. It is a toolbar addon that was built on XUL. Now I want to build it using the Addon SDK.
The old XUL overlay allowed for onMouseOver events for buttons. But the new addon SDK only has the one listener for click.
How can I get an onMouseOver (Hover) event for a toolbar button?
Maybe there is some way to add css (:hover) to the button element?
I found this, and am working on getting it in order, but maybe there's a better way?
Here is what I have so far:
var {Cu, Cc, Ci} = require("chrome");
Cu.import('resource://gre/modules/Services.jsm');
var aDOMWindow = Services.wm.getMostRecentWindow('navigator:browser');
aDOMWindow.addEventListener('mouseover', onSpatMouseover, true);
function onMyMouseover(event){
if (event.target.nodeName == 'toolbarbutton'){
console.log(event.explicitOriginalTarget.nodeName);
if(event.currentTarget.nodeName == '#MyButton'){
console.log("found the button");
}
}
}
But it does not yet find #MyButton.
First of all, error message you're getting already tells you how to make it work.
But it's not necessarily what you need anyway, usually sdk/view/core provides access to the underlying XUL elements through one of its 3 methods.
Here is a complete example of how to do this. There are two functions, actually, one for mouseover and one for mouseout. If you change the icon of a button using mouseover, you need mouseout to change it back to normal.
const { browserWindows } = require("sdk/windows");
const { CustomizableUI } = require('resource:///modules/CustomizableUI.jsm');
const { viewFor } = require("sdk/view/core");
const { ActionButton } = require("sdk/ui/button/action");
var myButton = ActionButton({
id: "mybutton",
label: "My Button",
icon: { "16": "./icon-16.png", "32":"./icon-32.png", "64": "./icon-64.png" },
onClick: function(state) {
console.log("My Button was clicked");
}
});
//create a mouseover effect for a control
exports.MouseOver = (whatbutton, whatwindow, whatfunction) =>{
CustomizableUI.getWidget( viewFor(whatbutton).id ).forWindow(whatwindow).node.addEventListener('mouseover', whatfunction, true);
};
exports.MouseOut = (whatbutton, whatwindow, whatfunction) =>{
CustomizableUI.getWidget( viewFor(whatbutton).id ).forWindow(whatwindow).node.addEventListener('mouseout', whatfunction, true);
};
function myMouseOverFunction(){
console.log("mousing over...");
}
function myMouseOutFunction(){
console.log("mousing out...");
}
//add events to the browser window
for(let w of browserWindows){
exports.MouseOver(mybutton, viewFor(w), myMouseOverFunction);
exports.MouseOut(mybutton, viewFor(w), onMouseOutFunction );
}
I am stuck on where to start here. I want to open popovers from the angular controller when something happens.
For example, let's say the user has selected 30 checkboxes, I want to open a popover with two buttons that allow the user to "Action all Items" or "Cancel" and also have a textarea where the user can put in a comment.
Currently, I am calling external javascript function from controller. The issue here is that the popover is defined outside of the current $scope.
Like I said, I am stuck on where to start. Any help is appreciated.
Current angular code:
$scope.showActionCommentPopover = function () {
var elementId = "#CommentPopoverAnchor";
var popoverTitle = "Action Comment";
var popoverContent = "Enter comment: <br/><textarea cols='50' rows='10' id='processComment' ng-model='actionComment'></textarea>";
var width = "600px";
var placement = "right";
ShowPopover(elementId, popoverTitle, popoverContent, width, placement);
};
External js function:
function ShowPopover(elementId, popoverTitle, popoverContent, width, placement) {
$(elementId).popover({
placement: placement,
title: popoverTitle + '<a class="close" data-toggle="popover" onClick="$(\'' + elementId + '\').popover(\'destroy\');">×</a>',
content: popoverContent,
html: true
});
$(elementId).popover('show');
$(".popover").css("width", width);
$(elementId).popover('show');
}
I'm using Titanium.
I am trying to show web page with navigation bar.
I want to put Back button at the top left position inside navigation bar which works as browser's back.
Below is my current code.
Could anybody help me with this??
Thanks in advance.
var w = Titanium.UI.createWindow({
backgroundColor:'#fff',
leftNavButtonLabel:'back',});
var navGroup = Ti.UI.iPhone.createNavigationGroup({
window:w
});
var backBtn = Titanium.UI.createButton({
title:'Back',
style:Titanium.UI.iPhone.SystemButtonStyle.BORDERED
});
var webView = Titanium.UI.createWebView({
url : 'http://gooogle.com',
canGoBack : true,
});
backBtn.addEventListener( 'click', function() {
webview.goBack();
});
w.add.setLeftNavButton(backBtn);
w.add(webView);
w.add(navGroup);
w.open();
w.navGroup.open();
Try, This... or
var main_win = Ti.UI.createWindow({
});
var w = Titanium.UI.createWindow({
backgroundColor:'#fff',
leftNavButtonLabel:'back',
});
var navGroup = Ti.UI.iPhone.createNavigationGroup({
window:w
});
var backBtn = Titanium.UI.createButton({
title:'Back',
style:Titanium.UI.iPhone.SystemButtonStyle.BORDERED
});
var webView = Titanium.UI.createWebView({
url : 'http://gooogle.com',
canGoBack : true,
});
backBtn.addEventListener( 'click', function() {
webview.goBack();// Goes back one entry in the web view's history list. to the previous page.
});
w.add.setLeftNavButton(backBtn);
w.add(webView);
main_win.add(navGroup);
main_win.open();
for Navigation new window
navGroup.open(Window name);
for Closing last open window;
navGroup.close(Window name);
HTML
<div id="activities"></div>
<div id="activity-edit"></div>
JavaScript
require([
'dojo/ready', 'dojo/dom', 'dijit/registry', 'dojox/mobile/parser', 'dojox/mobile/deviceTheme', 'dojox/mobile/compat', 'dojox/mobile/Icon', 'dojox/mobile/ScrollableView', 'dojox/mobile/Heading', 'dojox/mobile/ToolBarButton', 'dojox/mobile'
],
function(ready, dom, registry, parser, deviceTheme, compat, Icon, ScrollableView, Heading, ToolBarButton, mobile) {
ready(function() {
var view_activities = new ScrollableView(null, 'activities');
view_activities.selected = true;
var heading = new Heading({
label: 'Activities',
fixed: 'top'
});
view_activities.addFixedBar(heading);
var button = new ToolBarButton({
icon: 'mblDomButtonWhitePlus',
style: 'float:right;',
moveTo: 'activity-edit',
onClick: function(e) {
click_activity_edit(e, 0);
}
});
heading.addChild(button);
var view_activity_edit = new ScrollableView(null, 'activity-edit');
view_activities.startup();
});
this.click_activity_edit = function(e, activityid) {
var view_activity_edit = registry.byId('activity-edit');
view_activity_edit.destroyDescendants(false);
heading = new Heading({
id: 'heading-activity-edit',
label: 'Activity',
fixed: 'top'
});
view_activity_edit.addChild(heading);
var button = new ToolBarButton({
label: 'Cancel',
moveTo: 'activities',
transitionDir: -1,
arrow: 'left'
});
heading.addChild(button);
button = new ToolBarButton({
label: 'Save',
style: 'float:right;',
moveTo: 'activities',
transitionDir: -1,
onClick: function(e) {
click_activity_save(e, activityid, function() {
data.get_activities(request, handle_getActivities);
});
}
});
heading.addChild(button);
view_activity_edit.startup();
};
parser.parse();
});
Steps to recreate the behavior:
Click the "+" button, click "Cancel", click the "+" button again, click "Cancel" again and the button no longer works.
If you replace addFixedBar with addChild, the button works as expected every time. I would do this, but I need the Heading to be fixed given that it is on a ScrollableView.
I understand that addFixedBar adds the widget to the domNode and not the containerNode, but I don't understand why that affects the behavior of the button and only on the second pass. My guess is that it has something to do with the destroyDescendants call not actually removing the Heading when using addFixedBar. I tried destroying the Heading manually after calling destroyDescendants, but that didn't work. The heading is undefined/null on the second pass whether I get the Heading by "dom" or "registry".
Any help or explanation is appreciated.
EDIT
Here is the JSFiddle: http://jsfiddle.net/MPUvk/
The key is the startup() calls.
The view_activity_edit.startup() call will work only once (startup() sets an internal _started flag and does nothing when it is already set). The second time the view is created, startup() does nothing.
The different behaviors between addFixedBar and addChild are because addChild calls startup() internally, whereas addFixedBar does not.
So to fix, just add heading.startup() after the addFixedBar call, that should work.
Another possibility would be to reset view_activity_edit._started = false when you destroy the view.
Chrome has something called "Page Actions", and I'm roughly trying to replicate that functionality with the Firefox Addon SDK/Jetpack. There's probably a better approach than what I've tried so far, and I'm open to suggestions.
Using tabs, I'm able to listen for tab ready and activate events, and if the tab's URL matches, the addon widget should be enabled; if not, disabled. I've got to the point where I can change the icon when appropriate, but I'd like to disable the panel as well.
Strategy 1: Steal the click event and only show the panel if we're on the right page; otherwise, ignore. Problem is, according to the docs, manually showing the panel causes it not to be anchored, a bug that's not had much progress on it.
Strategy 2: Set the contentURL to null when disabling. Get an error whining about it not being an URL.
Strategy 3: Use a different HTML document for the disabled state. Setting panel.contentURL to another URL doesn't work after going to a different page?
Here's the code:
const widgets = require("widget");
const Panel = require("panel").Panel;
const tabs = require("tabs");
const data = require("self").data;
const prefs = require("simple-prefs").prefs;
var panel = Panel({
width: 480,
height: 640,
contentURL: data.url("panel.html"),
contentScriptFile: [data.url('jquery.min.js'), data.url('panel.js')],
onMessage: function (msg) { console.log(msg) }
});
var widget = widgets.Widget({
id: "icon",
label: "Export",
contentURL: data.url("icon.png"),
panel: panel
});
function enable() {
widget.contentURL = data.url('icon.png');
panel.contentURL = data.url("panel.html");
}
function disable() {
widget.contentURL = data.url('icon_disabled.png');
panel.contentURL = data.url("panel_disabled.html");
}
function on_change_tab(tab) {
console.log(tab.url);
if (/http:\/\/example.com\/.*/.exec(tab.url)) {
console.log('ENABLE');
enable();
} else {
console.log('DISABLE');
disable();
}
console.log(panel.contentURL);
}
tabs.on('ready', on_change_tab);
tabs.on('activate', on_change_tab);
Related, but should have the anchoring problem? How to reload a widget popup panel with firefox addon sdk?
In case you still haven't solved your issue (and for anyone else having a similar problem):
I had a similar issue and resolved it by using erikvold's ToolbarButton package. Once you've installed that and its dependencies, something like this in your main.js file should work.
var pan = require("panel").Panel({
width: 400,
height: 600,
contentURL: "http://stackoverflow.com"
//maybe some more options here
});
var button = require("toolbarbutton").ToolbarButton({
id: "myButton",
label: "My Button",
image: data.url("someicon.png"),
panel: pan //This binds the panel to this toolbarbutton
});
I hope you can find some way to adapt this to your project.
toolbarbutton.ToolbarButton({
id: "MyAddon",
label: "My Addon",
tooltiptext: "My Addon Tooltip",
image: data.url("logo.png"),
onCommand: function() {
var dictionary_panel = require("panel").Panel({
width:630,
height:600,
contentURL: data.url("HtmlPage.html"),
contentScriptWhen: 'ready',
contentScriptFile: [data.url("style.css"),data.url("jquery-1.7.1.js"),
data.url("javascriptquezz.js"),data.url("create.js")]
});
dictionary_panel.show();
}
});