2013-03-26 14:23:23 -07:00
|
|
|
/* 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";
|
|
|
|
|
|
|
|
this.EXPORTED_SYMBOLS = ["CustomizeMode"];
|
|
|
|
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
|
|
|
|
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
|
|
|
|
const kPaletteId = "customization-palette";
|
2013-04-10 10:37:25 -07:00
|
|
|
const kAboutURI = "about:customizing";
|
2013-05-29 13:41:44 -07:00
|
|
|
const kDragDataTypePrefix = "text/toolbarwrapper-id/";
|
2013-06-03 15:35:30 -07:00
|
|
|
const kPlaceholderClass = "panel-customization-placeholder";
|
2013-06-20 16:13:01 -07:00
|
|
|
// TODO(bug 885574): Merge this constant with the one in CustomizableWidgets.jsm,
|
|
|
|
// maybe just use a pref for this.
|
|
|
|
const kColumnsInMenuPanel = 3;
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-04-30 08:00:41 -07:00
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
Cu.import("resource:///modules/CustomizableUI.jsm");
|
2013-06-07 20:29:47 -07:00
|
|
|
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
2013-06-03 21:02:30 -07:00
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
2013-06-17 07:37:41 -07:00
|
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Promise.jsm");
|
2013-04-30 08:00:41 -07:00
|
|
|
|
2013-06-03 21:02:30 -07:00
|
|
|
let gModuleName = "[CustomizeMode]";
|
|
|
|
#include logging.js
|
2013-04-04 08:07:09 -07:00
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
function CustomizeMode(aWindow) {
|
|
|
|
this.window = aWindow;
|
|
|
|
this.document = aWindow.document;
|
2013-04-10 10:37:25 -07:00
|
|
|
this.browser = aWindow.gBrowser;
|
2013-06-29 01:25:56 -07:00
|
|
|
|
|
|
|
// There are two palettes - there's the palette that can be overlayed with
|
|
|
|
// toolbar items in browser.xul. This is invisible, and never seen by the
|
|
|
|
// user. Then there's the visible palette, which gets populated and displayed
|
|
|
|
// to the user when in customizing mode.
|
|
|
|
this.visiblePalette = this.document.getElementById(kPaletteId);
|
2013-03-26 14:23:23 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
CustomizeMode.prototype = {
|
|
|
|
_changed: false,
|
2013-06-17 07:37:41 -07:00
|
|
|
_transitioning: false,
|
2013-03-26 14:23:23 -07:00
|
|
|
window: null,
|
|
|
|
document: null,
|
|
|
|
// areas is used to cache the customizable areas when in customization mode.
|
|
|
|
areas: null,
|
|
|
|
// When in customizing mode, we swap out the reference to the invisible
|
|
|
|
// palette in gNavToolbox.palette for our visiblePalette. This way, for the
|
|
|
|
// customizing browser window, when widgets are removed from customizable
|
|
|
|
// areas and added to the palette, they're added to the visible palette.
|
|
|
|
// _stowedPalette is a reference to the old invisible palette so we can
|
|
|
|
// restore gNavToolbox.palette to its original state after exiting
|
|
|
|
// customization mode.
|
|
|
|
_stowedPalette: null,
|
2013-03-22 12:26:26 -07:00
|
|
|
_dragOverItem: null,
|
2013-04-10 10:37:25 -07:00
|
|
|
_customizing: false,
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
get panelUIContents() {
|
|
|
|
return this.document.getElementById("PanelUI-contents");
|
|
|
|
},
|
|
|
|
|
2013-06-25 08:05:24 -07:00
|
|
|
toggle: function() {
|
|
|
|
if (this._transitioning) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this._customizing) {
|
|
|
|
this.exit();
|
|
|
|
} else {
|
|
|
|
this.enter();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
enter: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
if (this._customizing || this._transitioning) {
|
2013-04-30 08:00:41 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-28 14:49:11 -07:00
|
|
|
// We don't need to switch to kAboutURI, or open a new tab at
|
|
|
|
// kAboutURI if we're already on it.
|
|
|
|
if (this.browser.selectedBrowser.currentURI.spec != kAboutURI) {
|
|
|
|
this.window.switchToTabHavingURI(kAboutURI, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-07 20:29:47 -07:00
|
|
|
// Disable lightweight themes while in customization mode since
|
|
|
|
// they don't have large enough images to pad the full browser window.
|
|
|
|
LightweightThemeManager.temporarilyToggleTheme(false);
|
|
|
|
|
2013-05-17 12:29:05 -07:00
|
|
|
this.dispatchToolboxEvent("beforecustomization");
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
let window = this.window;
|
|
|
|
let document = this.document;
|
|
|
|
|
2013-03-22 12:15:58 -07:00
|
|
|
CustomizableUI.addListener(this);
|
|
|
|
|
2013-04-10 13:24:45 -07:00
|
|
|
// Add a keypress listener and click listener to the tab-view-deck so that
|
|
|
|
// we can quickly exit customization mode when pressing ESC or clicking on
|
|
|
|
// the blueprint outside the customization container.
|
|
|
|
let deck = document.getElementById("tab-view-deck");
|
|
|
|
deck.addEventListener("keypress", this, false);
|
|
|
|
deck.addEventListener("click", this, false);
|
|
|
|
|
2013-03-22 12:15:58 -07:00
|
|
|
// Same goes for the menu button - if we're customizing, a click to the
|
|
|
|
// menu button means a quick exit from customization mode.
|
2013-06-12 11:28:21 -07:00
|
|
|
window.PanelUI.hide();
|
2013-03-22 12:15:58 -07:00
|
|
|
window.PanelUI.menuButton.addEventListener("click", this, false);
|
2013-06-12 11:28:21 -07:00
|
|
|
window.PanelUI.menuButton.open = true;
|
2013-06-17 07:37:41 -07:00
|
|
|
window.PanelUI.beginBatchUpdate();
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-04-09 18:50:05 -07:00
|
|
|
// Move the mainView in the panel to the holder so that we can see it
|
|
|
|
// while customizing.
|
|
|
|
let panelHolder = document.getElementById("customization-panelHolder");
|
|
|
|
panelHolder.appendChild(window.PanelUI.mainView);
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._transitioning = true;
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
let customizer = document.getElementById("customization-container");
|
|
|
|
customizer.parentNode.selectedPanel = customizer;
|
|
|
|
customizer.hidden = false;
|
2013-05-28 06:07:10 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
Task.spawn(function() {
|
|
|
|
yield this._doTransition(true);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
// Let everybody in this window know that we're about to customize.
|
|
|
|
this.dispatchToolboxEvent("customizationstarting");
|
|
|
|
|
|
|
|
// The menu panel is lazy, and registers itself when the popup shows. We
|
|
|
|
// need to force the menu panel to register itself, or else customization
|
|
|
|
// is really not going to work. We pass "true" to ensureRegistered to
|
|
|
|
// indicate that we're handling calling startBatchUpdate and
|
|
|
|
// endBatchUpdate.
|
2013-07-27 11:58:36 -07:00
|
|
|
yield window.PanelUI.ensureReady(true);
|
2013-06-17 07:37:41 -07:00
|
|
|
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
|
|
|
|
yield this._wrapToolbarItems();
|
|
|
|
yield this.populatePalette();
|
|
|
|
|
|
|
|
window.PanelUI.mainView.addEventListener("contextmenu", this, true);
|
|
|
|
this.visiblePalette.addEventListener("dragstart", this, true);
|
|
|
|
this.visiblePalette.addEventListener("dragover", this, true);
|
|
|
|
this.visiblePalette.addEventListener("dragexit", this, true);
|
|
|
|
this.visiblePalette.addEventListener("drop", this, true);
|
|
|
|
this.visiblePalette.addEventListener("dragend", this, true);
|
2013-05-20 10:23:50 -07:00
|
|
|
|
2013-07-10 05:05:35 -07:00
|
|
|
window.gNavToolbox.addEventListener("toolbarvisibilitychange", this);
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
// Same goes for the menu button - if we're customizing, a click to the
|
|
|
|
// menu button means a quick exit from customization mode.
|
|
|
|
window.PanelUI.menuButton.addEventListener("click", this, false);
|
|
|
|
window.PanelUI.menuButton.disabled = true;
|
2013-05-23 06:40:55 -07:00
|
|
|
|
2013-08-15 10:54:10 -07:00
|
|
|
window.document.getElementById("PanelUI-help").disabled = true;
|
2013-08-20 13:36:41 -07:00
|
|
|
window.document.getElementById("PanelUI-quit").disabled = true;
|
2013-06-25 08:05:24 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._updateResetButton();
|
|
|
|
|
|
|
|
let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])");
|
|
|
|
for (let toolbar of customizableToolbars)
|
|
|
|
toolbar.setAttribute("customizing", true);
|
|
|
|
|
|
|
|
window.PanelUI.endBatchUpdate();
|
|
|
|
this._customizing = true;
|
|
|
|
this._transitioning = false;
|
|
|
|
this.dispatchToolboxEvent("customizationready");
|
|
|
|
}.bind(this));
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-04-10 10:37:25 -07:00
|
|
|
exit: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
if (!this._customizing || this._transitioning) {
|
2013-04-30 08:00:41 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
CustomizableUI.removeListener(this);
|
|
|
|
|
2013-04-10 13:24:45 -07:00
|
|
|
let deck = this.document.getElementById("tab-view-deck");
|
|
|
|
deck.removeEventListener("keypress", this, false);
|
|
|
|
deck.removeEventListener("click", this, false);
|
2013-03-22 12:15:58 -07:00
|
|
|
this.window.PanelUI.menuButton.removeEventListener("click", this, false);
|
2013-06-12 11:28:21 -07:00
|
|
|
this.window.PanelUI.menuButton.open = false;
|
2013-03-22 12:15:58 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this.window.PanelUI.beginBatchUpdate();
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
this._removePanelCustomizationPlaceholders();
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._transitioning = true;
|
2013-03-26 14:23:23 -07:00
|
|
|
|
|
|
|
let window = this.window;
|
|
|
|
let document = this.document;
|
2013-05-10 13:24:18 -07:00
|
|
|
let documentElement = document.documentElement;
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
Task.spawn(function() {
|
|
|
|
yield this._doTransition(false);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
let customizer = document.getElementById("customization-container");
|
|
|
|
customizer.hidden = true;
|
|
|
|
let browser = document.getElementById("browser");
|
|
|
|
browser.parentNode.selectedPanel = browser;
|
2013-05-09 09:00:31 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
yield this.depopulatePalette();
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-07-10 05:05:35 -07:00
|
|
|
window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
window.PanelUI.mainView.removeEventListener("contextmenu", this, true);
|
|
|
|
this.visiblePalette.removeEventListener("dragstart", this, true);
|
|
|
|
this.visiblePalette.removeEventListener("dragover", this, true);
|
|
|
|
this.visiblePalette.removeEventListener("dragexit", this, true);
|
|
|
|
this.visiblePalette.removeEventListener("drop", this, true);
|
|
|
|
this.visiblePalette.removeEventListener("dragend", this, true);
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
yield this._unwrapToolbarItems();
|
2013-04-10 10:37:25 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
if (this._changed) {
|
|
|
|
// XXXmconley: At first, it seems strange to also persist the old way with
|
|
|
|
// currentset - but this might actually be useful for switching
|
|
|
|
// to old builds. We might want to keep this around for a little
|
|
|
|
// bit.
|
|
|
|
this.persistCurrentSets();
|
|
|
|
}
|
2013-05-07 13:08:36 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
// And drop all area references.
|
|
|
|
this.areas = [];
|
|
|
|
|
|
|
|
// Let everybody in this window know that we're starting to
|
|
|
|
// exit customization mode.
|
|
|
|
this.dispatchToolboxEvent("customizationending");
|
|
|
|
window.PanelUI.setMainView(window.PanelUI.mainView);
|
|
|
|
window.PanelUI.menuButton.disabled = false;
|
|
|
|
|
2013-08-15 10:54:10 -07:00
|
|
|
window.document.getElementById("PanelUI-help").disabled = false;
|
2013-08-20 13:36:41 -07:00
|
|
|
window.document.getElementById("PanelUI-quit").disabled = false;
|
2013-06-25 08:05:24 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
// We need to set self._customizing to false before removing the tab
|
|
|
|
// or the TabSelect event handler will think that we are exiting
|
|
|
|
// customization mode for a second time.
|
|
|
|
this._customizing = false;
|
|
|
|
|
|
|
|
if (this.browser.selectedBrowser.currentURI.spec == kAboutURI) {
|
|
|
|
let custBrowser = this.browser.selectedBrowser;
|
|
|
|
if (custBrowser.canGoBack) {
|
|
|
|
// If there's history to this tab, just go back.
|
|
|
|
custBrowser.goBack();
|
|
|
|
} else {
|
|
|
|
// If we can't go back, we're removing the about:customization tab.
|
|
|
|
// We only do this if we're the top window for this window (so not
|
|
|
|
// a dialog window, for example).
|
|
|
|
if (window.getTopWin(true) == window) {
|
|
|
|
let customizationTab = this.browser.selectedTab;
|
|
|
|
if (this.browser.browsers.length == 1) {
|
|
|
|
window.BrowserOpenTab();
|
|
|
|
}
|
|
|
|
this.browser.removeTab(customizationTab);
|
2013-05-17 12:29:05 -07:00
|
|
|
}
|
2013-04-10 10:37:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-07 20:29:47 -07:00
|
|
|
LightweightThemeManager.temporarilyToggleTheme(true);
|
2013-05-17 12:29:05 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
|
|
|
|
for (let toolbar of customizableToolbars)
|
|
|
|
toolbar.removeAttribute("customizing");
|
2013-05-23 06:40:55 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this.window.PanelUI.endBatchUpdate();
|
|
|
|
this._changed = false;
|
|
|
|
this._transitioning = false;
|
|
|
|
this.dispatchToolboxEvent("aftercustomization");
|
|
|
|
}.bind(this));
|
|
|
|
},
|
|
|
|
|
|
|
|
_doTransition: function(aEntering) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
|
|
|
|
let deck = this.document.getElementById("tab-view-deck");
|
2013-08-20 04:39:04 -07:00
|
|
|
let customizeTransitionEnd = function(aEvent) {
|
2013-06-17 07:37:41 -07:00
|
|
|
if (aEvent.originalTarget != deck || aEvent.propertyName != "padding-top") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
deck.removeEventListener("transitionend", customizeTransitionEnd);
|
2013-05-16 03:00:22 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
if (!aEntering) {
|
|
|
|
this.document.documentElement.removeAttribute("customize-exiting");
|
|
|
|
}
|
|
|
|
|
|
|
|
deferred.resolve();
|
2013-08-20 04:39:04 -07:00
|
|
|
}.bind(this);
|
|
|
|
deck.addEventListener("transitionend", customizeTransitionEnd);
|
2013-06-17 07:37:41 -07:00
|
|
|
|
|
|
|
if (aEntering) {
|
|
|
|
this.document.documentElement.setAttribute("customizing", true);
|
|
|
|
} else {
|
|
|
|
this.document.documentElement.setAttribute("customize-exiting", true);
|
|
|
|
this.document.documentElement.removeAttribute("customizing");
|
|
|
|
}
|
|
|
|
return deferred.promise;
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-05-17 12:29:05 -07:00
|
|
|
dispatchToolboxEvent: function(aEventType, aDetails={}) {
|
|
|
|
let evt = this.document.createEvent("CustomEvent");
|
|
|
|
evt.initCustomEvent(aEventType, true, true, {changed: this._changed});
|
|
|
|
let result = this.window.gNavToolbox.dispatchEvent(evt);
|
|
|
|
},
|
|
|
|
|
2013-06-05 02:31:37 -07:00
|
|
|
addToToolbar: function(aNode) {
|
|
|
|
CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_NAVBAR);
|
|
|
|
},
|
|
|
|
|
|
|
|
removeFromPanel: function(aNode) {
|
|
|
|
CustomizableUI.removeWidgetFromArea(aNode.id);
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
populatePalette: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
let fragment = this.document.createDocumentFragment();
|
2013-03-26 14:23:23 -07:00
|
|
|
let toolboxPalette = this.window.gNavToolbox.palette;
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
let unusedWidgets = CustomizableUI.getUnusedWidgets(toolboxPalette);
|
|
|
|
for (let widget of unusedWidgets) {
|
|
|
|
let paletteItem = this.makePaletteItem(widget, "palette");
|
|
|
|
fragment.appendChild(paletteItem);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this.visiblePalette.appendChild(fragment);
|
|
|
|
this._stowedPalette = this.window.gNavToolbox.palette;
|
|
|
|
this.window.gNavToolbox.palette = this.visiblePalette;
|
|
|
|
}.bind(this));
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
//XXXunf Maybe this should use -moz-element instead of wrapping the node?
|
|
|
|
// Would ensure no weird interactions/event handling from original node,
|
|
|
|
// and makes it possible to put this in a lazy-loaded iframe/real tab
|
|
|
|
// while still getting rid of the need for overlays.
|
|
|
|
makePaletteItem: function(aWidget, aPlace) {
|
|
|
|
let widgetNode = aWidget.forWindow(this.window).node;
|
|
|
|
let wrapper = this.createWrapper(widgetNode, aPlace);
|
|
|
|
wrapper.appendChild(widgetNode);
|
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
|
|
|
|
depopulatePalette: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
this.visiblePalette.hidden = true;
|
|
|
|
let paletteChild = this.visiblePalette.firstChild;
|
|
|
|
let nextChild;
|
|
|
|
while (paletteChild) {
|
|
|
|
nextChild = paletteChild.nextElementSibling;
|
|
|
|
let provider = CustomizableUI.getWidget(paletteChild.id).provider;
|
|
|
|
if (provider == CustomizableUI.PROVIDER_XUL) {
|
|
|
|
let unwrappedPaletteItem =
|
|
|
|
yield this.deferredUnwrapToolbarItem(paletteChild);
|
|
|
|
this._stowedPalette.appendChild(unwrappedPaletteItem);
|
|
|
|
} else if (provider == CustomizableUI.PROVIDER_API) {
|
|
|
|
//XXXunf Currently this doesn't destroy the (now unused) node. It would
|
|
|
|
// be good to do so, but we need to keep strong refs to it in
|
|
|
|
// CustomizableUI (can't iterate of WeakMaps), and there's the
|
|
|
|
// question of what behavior wrappers should have if consumers
|
|
|
|
// keep hold of them.
|
|
|
|
//widget.destroyInstance(widgetNode);
|
|
|
|
} else if (provider == CustomizableUI.PROVIDER_SPECIAL) {
|
|
|
|
this.visiblePalette.removeChild(paletteChild);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
paletteChild = nextChild;
|
|
|
|
}
|
|
|
|
this.visiblePalette.hidden = false;
|
|
|
|
this.window.gNavToolbox.palette = this._stowedPalette;
|
|
|
|
}.bind(this));
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-04-30 08:00:41 -07:00
|
|
|
isCustomizableItem: function(aNode) {
|
|
|
|
return aNode.localName == "toolbarbutton" ||
|
|
|
|
aNode.localName == "toolbaritem" ||
|
|
|
|
aNode.localName == "toolbarseparator" ||
|
|
|
|
aNode.localName == "toolbarspring" ||
|
|
|
|
aNode.localName == "toolbarspacer";
|
|
|
|
},
|
|
|
|
|
|
|
|
isWrappedToolbarItem: function(aNode) {
|
|
|
|
return aNode.localName == "toolbarpaletteitem";
|
|
|
|
},
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
deferredWrapToolbarItem: function(aNode, aPlace) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
|
|
|
|
dispatchFunction(function() {
|
|
|
|
let wrapper = this.wrapToolbarItem(aNode, aPlace);
|
|
|
|
deferred.resolve(wrapper);
|
|
|
|
}.bind(this))
|
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapToolbarItem: function(aNode, aPlace) {
|
2013-05-30 01:07:43 -07:00
|
|
|
if (!this.isCustomizableItem(aNode)) {
|
|
|
|
return aNode;
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
let wrapper = this.createWrapper(aNode, aPlace);
|
2013-03-22 12:15:58 -07:00
|
|
|
// It's possible that this toolbar node is "mid-flight" and doesn't have
|
|
|
|
// a parent, in which case we skip replacing it. This can happen if a
|
|
|
|
// toolbar item has been dragged into the palette. In that case, we tell
|
|
|
|
// CustomizableUI to remove the widget from its area before putting the
|
|
|
|
// widget in the palette - so the node will have no parent.
|
|
|
|
if (aNode.parentNode) {
|
|
|
|
aNode = aNode.parentNode.replaceChild(wrapper, aNode);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
wrapper.appendChild(aNode);
|
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
|
|
|
|
createWrapper: function(aNode, aPlace) {
|
|
|
|
let wrapper = this.document.createElement("toolbarpaletteitem");
|
|
|
|
|
|
|
|
// "place" is used by toolkit to add the toolbarpaletteitem-palette
|
|
|
|
// binding to a toolbarpaletteitem, which gives it a label node for when
|
|
|
|
// it's sitting in the palette.
|
|
|
|
wrapper.setAttribute("place", aPlace);
|
|
|
|
|
|
|
|
// Ensure the wrapped item doesn't look like it's in any special state, and
|
|
|
|
// can't be interactved with when in the customization palette.
|
|
|
|
if (aNode.hasAttribute("command")) {
|
|
|
|
wrapper.setAttribute("itemcommand", aNode.getAttribute("command"));
|
|
|
|
aNode.removeAttribute("command");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode.checked) {
|
|
|
|
wrapper.setAttribute("itemchecked", "true");
|
|
|
|
aNode.checked = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode.hasAttribute("id")) {
|
|
|
|
wrapper.setAttribute("id", "wrapper-" + aNode.getAttribute("id"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode.hasAttribute("title")) {
|
|
|
|
wrapper.setAttribute("title", aNode.getAttribute("title"));
|
|
|
|
} else if (aNode.hasAttribute("label")) {
|
|
|
|
wrapper.setAttribute("title", aNode.getAttribute("label"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode.hasAttribute("flex")) {
|
|
|
|
wrapper.setAttribute("flex", aNode.getAttribute("flex"));
|
|
|
|
}
|
|
|
|
|
2013-03-22 14:28:34 -07:00
|
|
|
wrapper.addEventListener("mousedown", this);
|
|
|
|
wrapper.addEventListener("mouseup", this);
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
return wrapper;
|
|
|
|
},
|
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
deferredUnwrapToolbarItem: function(aWrapper) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
dispatchFunction(function() {
|
|
|
|
deferred.resolve(this.unwrapToolbarItem(aWrapper));
|
|
|
|
}.bind(this));
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
unwrapToolbarItem: function(aWrapper) {
|
2013-05-30 01:07:43 -07:00
|
|
|
if (aWrapper.nodeName != "toolbarpaletteitem") {
|
|
|
|
return aWrapper;
|
|
|
|
}
|
2013-03-22 14:28:34 -07:00
|
|
|
aWrapper.removeEventListener("mousedown", this);
|
|
|
|
aWrapper.removeEventListener("mouseup", this);
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
let toolbarItem = aWrapper.firstChild;
|
2013-06-20 16:13:01 -07:00
|
|
|
if (!toolbarItem) {
|
|
|
|
ERROR("no toolbarItem child for " + aWrapper.tagName + "#" + aWrapper.id);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
|
|
|
|
if (aWrapper.hasAttribute("itemchecked")) {
|
|
|
|
toolbarItem.checked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aWrapper.hasAttribute("itemcommand")) {
|
|
|
|
let commandID = aWrapper.getAttribute("itemcommand");
|
|
|
|
toolbarItem.setAttribute("command", commandID);
|
|
|
|
|
|
|
|
//XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
|
|
|
|
let command = this.document.getElementById(commandID);
|
|
|
|
if (command && command.hasAttribute("disabled")) {
|
|
|
|
toolbarItem.setAttribute("disabled", command.getAttribute("disabled"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-22 12:15:58 -07:00
|
|
|
if (aWrapper.parentNode) {
|
|
|
|
aWrapper.parentNode.replaceChild(toolbarItem, aWrapper);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
return toolbarItem;
|
|
|
|
},
|
|
|
|
|
2013-05-24 08:43:39 -07:00
|
|
|
_wrapToolbarItems: function() {
|
|
|
|
let window = this.window;
|
|
|
|
// Add drag-and-drop event handlers to all of the customizable areas.
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
this.areas = [];
|
|
|
|
for (let area of CustomizableUI.areas) {
|
|
|
|
let target = CustomizableUI.getCustomizeTargetForArea(area, window);
|
|
|
|
target.addEventListener("dragstart", this, true);
|
|
|
|
target.addEventListener("dragover", this, true);
|
|
|
|
target.addEventListener("dragexit", this, true);
|
|
|
|
target.addEventListener("drop", this, true);
|
|
|
|
target.addEventListener("dragend", this, true);
|
|
|
|
for (let child of target.children) {
|
|
|
|
if (this.isCustomizableItem(child)) {
|
|
|
|
yield this.deferredWrapToolbarItem(child, getPlaceForItem(child));
|
|
|
|
}
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
this.areas.push(target);
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
}.bind(this));
|
2013-05-24 08:43:39 -07:00
|
|
|
},
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
// TODO(bug 885575): Remove once CustomizeUI can handle moving wrapped widgets.
|
|
|
|
_wrapToolbarItemsSync: function() {
|
|
|
|
let window = this.window;
|
|
|
|
// Add drag-and-drop event handlers to all of the customizable areas.
|
|
|
|
this.areas = [];
|
|
|
|
for (let area of CustomizableUI.areas) {
|
|
|
|
let target = CustomizableUI.getCustomizeTargetForArea(area, window);
|
|
|
|
target.addEventListener("dragstart", this, true);
|
|
|
|
target.addEventListener("dragover", this, true);
|
|
|
|
target.addEventListener("dragexit", this, true);
|
|
|
|
target.addEventListener("drop", this, true);
|
|
|
|
target.addEventListener("dragend", this, true);
|
|
|
|
for (let child of target.children) {
|
|
|
|
if (this.isCustomizableItem(child)) {
|
|
|
|
this.wrapToolbarItem(child, getPlaceForItem(child));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.areas.push(target);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-05-24 08:43:39 -07:00
|
|
|
_unwrapToolbarItems: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
2013-06-20 16:13:01 -07:00
|
|
|
this._unwrapToolbarItemsSync();
|
|
|
|
}.bind(this));
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO(bug 885575): Merge into _unwrapToolbarItems.
|
|
|
|
_unwrapToolbarItemsSync: function() {
|
|
|
|
for (let target of this.areas) {
|
|
|
|
for (let toolbarItem of target.children) {
|
|
|
|
if (this.isWrappedToolbarItem(toolbarItem)) {
|
|
|
|
this.unwrapToolbarItem(toolbarItem);
|
2013-05-24 08:43:39 -07:00
|
|
|
}
|
|
|
|
}
|
2013-06-20 16:13:01 -07:00
|
|
|
target.removeEventListener("dragstart", this, true);
|
|
|
|
target.removeEventListener("dragover", this, true);
|
|
|
|
target.removeEventListener("dragexit", this, true);
|
|
|
|
target.removeEventListener("drop", this, true);
|
|
|
|
target.removeEventListener("dragend", this, true);
|
|
|
|
}
|
2013-05-24 08:43:39 -07:00
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
persistCurrentSets: function() {
|
|
|
|
let document = this.document;
|
2013-07-16 06:36:03 -07:00
|
|
|
let toolbars = document.querySelectorAll("toolbar[customizable='true'][currentset]");
|
2013-04-29 21:25:08 -07:00
|
|
|
for (let toolbar of toolbars) {
|
|
|
|
// Persist the currentset attribute directly on hardcoded toolbars.
|
|
|
|
document.persist(toolbar.id, "currentset");
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
reset: function() {
|
2013-06-17 07:37:41 -07:00
|
|
|
return Task.spawn(function() {
|
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
yield this.depopulatePalette();
|
|
|
|
yield this._unwrapToolbarItems();
|
|
|
|
|
|
|
|
CustomizableUI.reset();
|
|
|
|
|
|
|
|
yield this._wrapToolbarItems();
|
|
|
|
yield this.populatePalette();
|
|
|
|
|
|
|
|
let document = this.document;
|
|
|
|
let toolbars = document.querySelectorAll("toolbar[customizable='true']");
|
|
|
|
for (let toolbar of toolbars) {
|
|
|
|
let set = toolbar.currentSet;
|
|
|
|
toolbar.removeAttribute("currentset");
|
|
|
|
LOG("[RESET] Removing currentset of " + toolbar.id);
|
|
|
|
// Persist the currentset attribute directly on hardcoded toolbars.
|
|
|
|
document.persist(toolbar.id, "currentset");
|
|
|
|
}
|
2013-05-24 08:43:39 -07:00
|
|
|
|
2013-06-17 07:37:41 -07:00
|
|
|
this._updateResetButton();
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
}.bind(this));
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-07-10 05:05:35 -07:00
|
|
|
_onToolbarVisibilityChange: function(aEvent) {
|
|
|
|
let toolbar = aEvent.target;
|
|
|
|
if (aEvent.detail.visible) {
|
|
|
|
toolbar.setAttribute("customizing", "true");
|
|
|
|
} else {
|
|
|
|
toolbar.removeAttribute("customizing");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
|
2013-05-17 12:29:05 -07:00
|
|
|
this._onUIChange();
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
2013-05-17 12:29:05 -07:00
|
|
|
this._onUIChange();
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetRemoved: function(aWidgetId, aArea) {
|
2013-05-17 12:29:05 -07:00
|
|
|
this._onUIChange();
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetCreated: function(aWidgetId) {
|
|
|
|
},
|
|
|
|
|
|
|
|
onWidgetDestroyed: function(aWidgetId) {
|
|
|
|
},
|
|
|
|
|
2013-05-17 12:29:05 -07:00
|
|
|
_onUIChange: function() {
|
|
|
|
this._changed = true;
|
2013-06-06 12:55:19 -07:00
|
|
|
this._updateResetButton();
|
2013-05-17 12:29:05 -07:00
|
|
|
this.dispatchToolboxEvent("customizationchange");
|
|
|
|
},
|
|
|
|
|
2013-06-06 12:55:19 -07:00
|
|
|
_updateResetButton: function() {
|
|
|
|
let btn = this.document.getElementById("customization-reset-button");
|
|
|
|
btn.disabled = CustomizableUI.inDefaultState;
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
handleEvent: function(aEvent) {
|
|
|
|
switch(aEvent.type) {
|
2013-07-10 05:05:35 -07:00
|
|
|
case "toolbarvisibilitychange":
|
|
|
|
this._onToolbarVisibilityChange(aEvent);
|
|
|
|
break;
|
2013-06-05 02:31:37 -07:00
|
|
|
case "contextmenu":
|
|
|
|
aEvent.preventDefault();
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
break;
|
2013-03-26 14:23:23 -07:00
|
|
|
case "dragstart":
|
|
|
|
this._onDragStart(aEvent);
|
|
|
|
break;
|
|
|
|
case "dragover":
|
|
|
|
this._onDragOver(aEvent);
|
|
|
|
break;
|
|
|
|
case "drop":
|
|
|
|
this._onDragDrop(aEvent);
|
|
|
|
break;
|
2013-03-22 12:26:26 -07:00
|
|
|
case "dragexit":
|
|
|
|
this._onDragExit(aEvent);
|
|
|
|
break;
|
2013-05-16 21:25:40 -07:00
|
|
|
case "dragend":
|
|
|
|
this._onDragEnd(aEvent);
|
|
|
|
break;
|
2013-03-22 14:28:34 -07:00
|
|
|
case "mousedown":
|
|
|
|
this._onMouseDown(aEvent);
|
|
|
|
break;
|
|
|
|
case "mouseup":
|
|
|
|
this._onMouseUp(aEvent);
|
|
|
|
break;
|
2013-03-22 12:15:58 -07:00
|
|
|
case "keypress":
|
2013-04-10 13:24:45 -07:00
|
|
|
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
|
2013-03-22 12:15:58 -07:00
|
|
|
this.exit();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "click":
|
|
|
|
if (aEvent.button == 0 &&
|
2013-04-10 13:24:45 -07:00
|
|
|
(aEvent.originalTarget == this.window.PanelUI.menuButton) ||
|
|
|
|
(aEvent.originalTarget == this.document.getElementById("tab-view-deck"))) {
|
2013-03-22 12:15:58 -07:00
|
|
|
this.exit();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
}
|
|
|
|
break;
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onDragStart: function(aEvent) {
|
|
|
|
__dumpDragData(aEvent);
|
|
|
|
let item = aEvent.target;
|
|
|
|
while (item && item.localName != "toolbarpaletteitem") {
|
2013-06-03 15:35:30 -07:00
|
|
|
if (item.localName == "toolbar" ||
|
|
|
|
item.classList.contains(kPlaceholderClass)) {
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
item = item.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
let dt = aEvent.dataTransfer;
|
|
|
|
let documentId = aEvent.target.ownerDocument.documentElement.id;
|
2013-05-29 13:41:44 -07:00
|
|
|
let draggedItem = item.firstChild;
|
|
|
|
let draggedItemWidth = draggedItem.getBoundingClientRect().width + "px";
|
|
|
|
|
|
|
|
let data = {
|
|
|
|
id: draggedItem.id,
|
|
|
|
width: draggedItemWidth,
|
|
|
|
};
|
|
|
|
|
|
|
|
dt.mozSetDataAt(kDragDataTypePrefix + documentId, data, 0);
|
2013-03-26 14:23:23 -07:00
|
|
|
dt.effectAllowed = "move";
|
2013-06-03 15:35:30 -07:00
|
|
|
|
|
|
|
// Hack needed so that the dragimage will still show the
|
|
|
|
// item as it appeared before it was hidden.
|
|
|
|
let win = aEvent.target.ownerDocument.defaultView;
|
|
|
|
win.setTimeout(function() {
|
2013-07-23 08:11:09 -07:00
|
|
|
// For automated tests, we sometimes start exiting customization mode
|
|
|
|
// before this fires, which leaves us with placeholders inserted after
|
|
|
|
// we've exited. So we need to check that we are indeed customizing.
|
|
|
|
if (this._customizing && !this._transitioning) {
|
|
|
|
item.hidden = true;
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
}
|
2013-06-03 15:35:30 -07:00
|
|
|
}.bind(this), 0);
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_onDragOver: function(aEvent) {
|
|
|
|
__dumpDragData(aEvent);
|
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
let documentId = document.documentElement.id;
|
2013-05-29 13:41:44 -07:00
|
|
|
if (!aEvent.dataTransfer.mozTypesAt(0)) {
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-29 13:41:44 -07:00
|
|
|
let {id: draggedItemId, width: draggedItemWidth} =
|
|
|
|
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
2013-03-22 12:26:26 -07:00
|
|
|
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
2013-05-30 01:07:43 -07:00
|
|
|
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
|
2013-03-22 12:26:26 -07:00
|
|
|
let originArea = this._getCustomizableParent(draggedWrapper);
|
|
|
|
|
|
|
|
// Do nothing if the target or origin are not customizable.
|
|
|
|
if (!targetArea || !originArea) {
|
2013-05-21 21:49:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
// Do nothing if the widget is not allowed to be removed.
|
2013-05-21 21:49:37 -07:00
|
|
|
if (targetArea.id == kPaletteId &&
|
|
|
|
!CustomizableUI.isWidgetRemovable(draggedItemId)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do nothing if the widget is not allowed to move to the target area.
|
|
|
|
if (targetArea.id != kPaletteId &&
|
|
|
|
!CustomizableUI.canWidgetMoveToArea(draggedItemId, targetArea.id)) {
|
2013-03-22 12:26:26 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-30 05:32:13 -07:00
|
|
|
let targetNode = this._getDragOverNode(aEvent.target, targetArea);
|
2013-05-30 01:07:43 -07:00
|
|
|
let targetParent = targetNode.parentNode;
|
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
// We need to determine the place that the widget is being dropped in
|
|
|
|
// the target.
|
2013-05-29 13:41:44 -07:00
|
|
|
let dragOverItem;
|
|
|
|
let atEnd = false;
|
|
|
|
if (targetNode == targetArea.customizationTarget) {
|
|
|
|
dragOverItem = targetNode.lastChild;
|
|
|
|
atEnd = true;
|
|
|
|
} else {
|
|
|
|
let position = Array.indexOf(targetParent.children, targetNode);
|
|
|
|
dragOverItem = position == -1 ? targetParent.lastChild : targetParent.children[position];
|
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
|
|
|
|
if (this._dragOverItem && dragOverItem != this._dragOverItem) {
|
|
|
|
this._setDragActive(this._dragOverItem, false);
|
|
|
|
}
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
if (dragOverItem != this._dragOverItem) {
|
2013-05-29 13:41:44 -07:00
|
|
|
this._setDragActive(dragOverItem, true, draggedItemWidth, atEnd);
|
2013-06-03 15:35:30 -07:00
|
|
|
this._dragOverItem = dragOverItem;
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
aEvent.preventDefault();
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
},
|
|
|
|
|
|
|
|
_onDragDrop: function(aEvent) {
|
|
|
|
__dumpDragData(aEvent);
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
|
2013-03-26 14:23:23 -07:00
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
let documentId = document.documentElement.id;
|
2013-05-29 13:41:44 -07:00
|
|
|
let {id: draggedItemId} =
|
|
|
|
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
2013-03-26 14:23:23 -07:00
|
|
|
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
|
|
|
let originArea = this._getCustomizableParent(draggedWrapper);
|
|
|
|
// Do nothing if the target area or origin area are not customizable.
|
|
|
|
if (!targetArea || !originArea) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-20 23:56:36 -07:00
|
|
|
let targetNode = this._getDragOverNode(aEvent.target, targetArea);
|
2013-06-20 16:13:01 -07:00
|
|
|
if (targetNode.tagName == "toolbarpaletteitem") {
|
|
|
|
targetNode = targetNode.firstChild;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._setDragActive(this._dragOverItem, false);
|
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
|
|
|
|
// TODO(bug 885575): Remove once CustomizeUI can handle moving wrapped widgets.
|
|
|
|
this._unwrapToolbarItemsSync();
|
|
|
|
let paletteChild = this.visiblePalette.firstChild;
|
|
|
|
let nextChild;
|
|
|
|
while (paletteChild) {
|
|
|
|
nextChild = paletteChild.nextElementSibling;
|
|
|
|
this.unwrapToolbarItem(paletteChild);
|
|
|
|
paletteChild = nextChild;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this._applyDrop(aEvent, targetArea, originArea, draggedItemId, targetNode);
|
|
|
|
} catch (ex) {
|
|
|
|
ERROR(ex, ex.stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(bug 885575): Remove once CustomizeUI can handle moving wrapped widgets.
|
|
|
|
this._wrapToolbarItemsSync();
|
|
|
|
// Re-wrap palette items.
|
|
|
|
let paletteChild = this.visiblePalette.firstChild;
|
|
|
|
let nextChild;
|
|
|
|
while (paletteChild) {
|
|
|
|
nextChild = paletteChild.nextElementSibling;
|
|
|
|
this.wrapToolbarItem(paletteChild, "palette");
|
|
|
|
paletteChild = nextChild;
|
|
|
|
}
|
|
|
|
this._showPanelCustomizationPlaceholders();
|
|
|
|
},
|
|
|
|
|
|
|
|
_applyDrop: function(aEvent, aTargetArea, aOriginArea, aDraggedItemId, aTargetNode) {
|
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
let draggedItem = document.getElementById(aDraggedItemId);
|
|
|
|
draggedItem.hidden = false;
|
|
|
|
draggedItem.removeAttribute("mousedown");
|
2013-05-30 01:07:43 -07:00
|
|
|
|
2013-04-14 15:52:24 -07:00
|
|
|
// Do nothing if the target was dropped onto itself (ie, no change in area
|
|
|
|
// or position).
|
2013-06-20 16:13:01 -07:00
|
|
|
if (draggedItem == aTargetNode) {
|
2013-04-14 15:52:24 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
// Is the target area the customization palette? If so, we have two cases -
|
2013-06-20 16:13:01 -07:00
|
|
|
// either the origin area was the palette, or a customizable area.
|
|
|
|
if (aTargetArea.id == kPaletteId) {
|
|
|
|
if (aOriginArea.id !== kPaletteId) {
|
|
|
|
if (!CustomizableUI.isWidgetRemovable(aDraggedItemId)) {
|
2013-05-21 21:49:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
CustomizableUI.removeWidgetFromArea(aDraggedItemId);
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
2013-03-22 12:15:58 -07:00
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
// If the target node is the palette itself, just append
|
|
|
|
if (aTargetNode == this.visiblePalette) {
|
|
|
|
this.visiblePalette.appendChild(draggedItem);
|
2013-03-22 12:15:58 -07:00
|
|
|
} else {
|
2013-06-20 16:13:01 -07:00
|
|
|
this.visiblePalette.insertBefore(draggedItem, aTargetNode);
|
2013-03-22 12:15:58 -07:00
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
if (!CustomizableUI.canWidgetMoveToArea(aDraggedItemId, aTargetArea.id)) {
|
2013-05-21 21:49:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-11 12:50:45 -07:00
|
|
|
// Is the target the customization area itself? If so, we just add the
|
|
|
|
// widget to the end of the area.
|
2013-06-20 16:13:01 -07:00
|
|
|
if (aTargetNode == aTargetArea.customizationTarget) {
|
|
|
|
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id);
|
2013-04-11 12:50:45 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-05 06:58:31 -07:00
|
|
|
// We need to determine the place that the widget is being dropped in
|
|
|
|
// the target.
|
2013-06-03 15:35:30 -07:00
|
|
|
let placement;
|
2013-06-20 16:13:01 -07:00
|
|
|
if (!aTargetNode.classList.contains(kPlaceholderClass)) {
|
|
|
|
let targetNodeId = (aTargetNode.nodeName == "toolbarpaletteitem") ?
|
|
|
|
aTargetNode.firstChild && aTargetNode.firstChild.id :
|
|
|
|
aTargetNode.id;
|
2013-06-03 15:35:30 -07:00
|
|
|
placement = CustomizableUI.getPlacementOfWidget(targetNodeId);
|
|
|
|
}
|
2013-04-05 06:58:31 -07:00
|
|
|
if (!placement) {
|
2013-06-20 16:13:01 -07:00
|
|
|
LOG("Could not get a position for " + aTargetNode + "#" + aTargetNode.id + "." + aTargetNode.className);
|
2013-04-05 06:58:31 -07:00
|
|
|
}
|
2013-07-24 07:58:15 -07:00
|
|
|
let position = placement ? placement.position : null;
|
2013-04-05 06:58:31 -07:00
|
|
|
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
// Is the target area the same as the origin? Since we've already handled
|
|
|
|
// the possibility that the target is the customization palette, we know
|
|
|
|
// that the widget is moving within a customizable area.
|
2013-06-20 16:13:01 -07:00
|
|
|
if (aTargetArea == aOriginArea) {
|
|
|
|
CustomizableUI.moveWidgetWithinArea(aDraggedItemId, position);
|
2013-03-26 14:23:23 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-20 16:13:01 -07:00
|
|
|
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id, position);
|
2013-03-26 14:23:23 -07:00
|
|
|
},
|
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
_onDragExit: function(aEvent) {
|
2013-05-16 21:25:40 -07:00
|
|
|
__dumpDragData(aEvent);
|
2013-03-22 12:26:26 -07:00
|
|
|
if (this._dragOverItem) {
|
|
|
|
this._setDragActive(this._dragOverItem, false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-05-16 21:25:40 -07:00
|
|
|
_onDragEnd: function(aEvent) {
|
|
|
|
__dumpDragData(aEvent);
|
|
|
|
let document = aEvent.target.ownerDocument;
|
|
|
|
document.documentElement.removeAttribute("customizing-movingItem");
|
|
|
|
|
|
|
|
let documentId = document.documentElement.id;
|
2013-05-29 13:41:44 -07:00
|
|
|
if (!aEvent.dataTransfer.mozTypesAt(0)) {
|
2013-05-16 21:25:40 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-29 13:41:44 -07:00
|
|
|
let {id: draggedItemId} =
|
|
|
|
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
2013-05-16 21:25:40 -07:00
|
|
|
|
2013-05-29 13:41:44 -07:00
|
|
|
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
2013-06-03 15:35:30 -07:00
|
|
|
draggedWrapper.hidden = false;
|
2013-05-16 21:25:40 -07:00
|
|
|
draggedWrapper.removeAttribute("mousedown");
|
2013-06-03 15:35:30 -07:00
|
|
|
this._showPanelCustomizationPlaceholders();
|
2013-05-16 21:25:40 -07:00
|
|
|
},
|
|
|
|
|
2013-05-29 13:41:44 -07:00
|
|
|
_setDragActive: function(aItem, aValue, aWidth, aAtEnd) {
|
2013-06-03 13:13:34 -07:00
|
|
|
if (!aItem) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
let node = aItem;
|
|
|
|
let window = aItem.ownerDocument.defaultView;
|
|
|
|
let direction = window.getComputedStyle(aItem, null).direction;
|
2013-05-29 13:41:44 -07:00
|
|
|
let value = direction == "ltr" ? "left" : "right";
|
|
|
|
if (aItem.localName == "toolbar" || aAtEnd) {
|
2013-03-22 12:26:26 -07:00
|
|
|
value = direction == "ltr"? "right" : "left";
|
2013-05-29 13:41:44 -07:00
|
|
|
if (aItem.localName == "toolbar") {
|
|
|
|
node = aItem.lastChild;
|
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!node) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aValue) {
|
|
|
|
if (!node.hasAttribute("dragover")) {
|
|
|
|
node.setAttribute("dragover", value);
|
2013-05-29 13:41:44 -07:00
|
|
|
|
|
|
|
if (aWidth) {
|
|
|
|
if (value == "left") {
|
|
|
|
node.style.borderLeftWidth = aWidth;
|
|
|
|
} else {
|
|
|
|
node.style.borderRightWidth = aWidth;
|
|
|
|
}
|
|
|
|
}
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
node.removeAttribute("dragover");
|
2013-05-29 13:41:44 -07:00
|
|
|
// Remove both property values in the case that the end padding
|
|
|
|
// had been set.
|
|
|
|
node.style.removeProperty("border-left-width");
|
|
|
|
node.style.removeProperty("border-right-width");
|
2013-03-22 12:26:26 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
_getCustomizableParent: function(aElement) {
|
|
|
|
let areas = CustomizableUI.areas;
|
|
|
|
areas.push(kPaletteId);
|
|
|
|
while (aElement) {
|
|
|
|
if (areas.indexOf(aElement.id) != -1) {
|
|
|
|
return aElement;
|
|
|
|
}
|
|
|
|
aElement = aElement.parentNode;
|
|
|
|
}
|
|
|
|
return null;
|
2013-03-22 14:28:34 -07:00
|
|
|
},
|
|
|
|
|
2013-05-30 01:07:43 -07:00
|
|
|
_getDragOverNode: function(aElement, aAreaElement) {
|
|
|
|
let expectedParent = aAreaElement.customizationTarget || aAreaElement;
|
|
|
|
let targetNode = aElement;
|
|
|
|
while (targetNode && targetNode.parentNode != expectedParent) {
|
|
|
|
targetNode = targetNode.parentNode;
|
|
|
|
}
|
|
|
|
return targetNode || aElement;
|
|
|
|
},
|
|
|
|
|
2013-03-22 14:28:34 -07:00
|
|
|
_onMouseDown: function(aEvent) {
|
2013-05-16 21:25:40 -07:00
|
|
|
LOG("_onMouseDown");
|
|
|
|
let doc = aEvent.target.ownerDocument;
|
|
|
|
doc.documentElement.setAttribute("customizing-movingItem", true);
|
2013-03-22 14:28:34 -07:00
|
|
|
let item = this._getWrapper(aEvent.target);
|
|
|
|
if (item) {
|
|
|
|
item.setAttribute("mousedown", "true");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onMouseUp: function(aEvent) {
|
2013-05-16 21:25:40 -07:00
|
|
|
LOG("_onMouseUp");
|
|
|
|
let doc = aEvent.target.ownerDocument;
|
|
|
|
doc.documentElement.removeAttribute("customizing-movingItem");
|
2013-03-22 14:28:34 -07:00
|
|
|
let item = this._getWrapper(aEvent.target);
|
|
|
|
if (item) {
|
|
|
|
item.removeAttribute("mousedown");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_getWrapper: function(aElement) {
|
|
|
|
while (aElement && aElement.localName != "toolbarpaletteitem") {
|
|
|
|
if (aElement.localName == "toolbar")
|
|
|
|
return null;
|
|
|
|
aElement = aElement.parentNode;
|
|
|
|
}
|
|
|
|
return aElement;
|
2013-04-10 10:37:25 -07:00
|
|
|
},
|
|
|
|
|
2013-06-03 15:35:30 -07:00
|
|
|
_showPanelCustomizationPlaceholders: function() {
|
|
|
|
this._removePanelCustomizationPlaceholders();
|
|
|
|
let doc = this.document;
|
|
|
|
let contents = this.panelUIContents;
|
2013-06-20 16:13:01 -07:00
|
|
|
let visibleCombinedButtons = contents.querySelectorAll("toolbarpaletteitem:not([hidden]) > .panel-combined-item");
|
2013-06-03 15:35:30 -07:00
|
|
|
let visibleChildren = contents.querySelectorAll("toolbarpaletteitem:not([hidden])");
|
2013-06-20 16:13:01 -07:00
|
|
|
// TODO(bug 885578): Still doesn't handle a hole when there is a wide
|
|
|
|
// widget located at the bottom of the panel.
|
|
|
|
let hangingItems = (visibleChildren.length - visibleCombinedButtons.length) % kColumnsInMenuPanel;
|
|
|
|
let newPlaceholders = kColumnsInMenuPanel - hangingItems;
|
2013-06-03 15:35:30 -07:00
|
|
|
while (newPlaceholders--) {
|
|
|
|
let placeholder = doc.createElement("toolbarpaletteitem");
|
|
|
|
placeholder.classList.add(kPlaceholderClass);
|
|
|
|
//XXXjaws The toolbarbutton child here is only necessary to get
|
|
|
|
// the styling right here.
|
|
|
|
let placeholderChild = doc.createElement("toolbarbutton");
|
|
|
|
placeholderChild.classList.add(kPlaceholderClass + "-child");
|
|
|
|
placeholder.appendChild(placeholderChild);
|
|
|
|
contents.appendChild(placeholder);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_removePanelCustomizationPlaceholders: function() {
|
|
|
|
let contents = this.panelUIContents;
|
|
|
|
let oldPlaceholders = contents.getElementsByClassName(kPlaceholderClass);
|
|
|
|
while (oldPlaceholders.length) {
|
|
|
|
contents.removeChild(oldPlaceholders[0]);
|
|
|
|
}
|
2013-03-26 14:23:23 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-03-22 12:26:26 -07:00
|
|
|
function getPlaceForItem(aElement) {
|
|
|
|
let place;
|
|
|
|
let node = aElement;
|
|
|
|
while (node && !place) {
|
|
|
|
if (node.localName == "toolbar")
|
|
|
|
place = "toolbar";
|
|
|
|
else if (node.id == CustomizableUI.AREA_PANEL)
|
|
|
|
place = "panel";
|
|
|
|
else if (node.id == kPaletteId)
|
|
|
|
place = "palette";
|
|
|
|
|
|
|
|
node = node.parentNode;
|
|
|
|
}
|
|
|
|
return place;
|
|
|
|
}
|
|
|
|
|
2013-03-26 14:23:23 -07:00
|
|
|
function __dumpDragData(aEvent, caller) {
|
|
|
|
let str = "Dumping drag data (CustomizeMode.jsm) {\n";
|
|
|
|
str += " type: " + aEvent["type"] + "\n";
|
|
|
|
for (let el of ["target", "currentTarget", "relatedTarget"]) {
|
|
|
|
if (aEvent[el]) {
|
|
|
|
str += " " + el + ": " + aEvent[el] + "(localName=" + aEvent[el].localName + "; id=" + aEvent[el].id + ")\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let prop in aEvent.dataTransfer) {
|
|
|
|
if (typeof aEvent.dataTransfer[prop] != "function") {
|
|
|
|
str += " dataTransfer[" + prop + "]: " + aEvent.dataTransfer[prop] + "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str += "}";
|
|
|
|
LOG(str);
|
|
|
|
}
|
2013-06-17 07:37:41 -07:00
|
|
|
|
|
|
|
function dispatchFunction(aFunc) {
|
|
|
|
Services.tm.currentThread.dispatch(aFunc, Ci.nsIThread.DISPATCH_NORMAL);
|
|
|
|
}
|