mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-15 04:00:12 +01:00
1100 lines
38 KiB
JavaScript
1100 lines
38 KiB
JavaScript
# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
# http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
#
|
|
# The Original Code is mozilla.org code.
|
|
#
|
|
# The Initial Developer of the Original Code is
|
|
# Pierre Chanial <chanial@noos.fr>.
|
|
# Portions created by the Initial Developer are Copyright (C) 1998
|
|
# the Initial Developer. All Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
# David Hyatt <hyatt@apple.com>
|
|
#
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this file under
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
#
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
var BookmarksMenu = {
|
|
_selection:null,
|
|
_target:null,
|
|
_orientation:null,
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// prepare the bookmarks menu for display
|
|
onShowMenu: function (aTarget)
|
|
{
|
|
this.showOpenInTabsMenuItem(aTarget);
|
|
this.showEmptyItem(aTarget);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// remove arbitary elements created in this.onShowMenu()
|
|
onHideMenu: function (aTarget)
|
|
{
|
|
this.hideOpenInTabsMenuItem(aTarget);
|
|
this.hideEmptyItem(aTarget);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// shows the 'Open All in Tabs' menu item if validOpenInTabsMenuItem is true -->
|
|
showOpenInTabsMenuItem: function (aTarget)
|
|
{
|
|
if (!this.validOpenInTabsMenuItem(aTarget) ||
|
|
aTarget.lastChild.getAttribute("class") == "openintabs-menuitem")
|
|
return;
|
|
var element = document.createElementNS(gXUL_NS, "menuseparator");
|
|
element.setAttribute("class", "openintabs-menuseparator");
|
|
aTarget.appendChild(element);
|
|
element = document.createElementNS(gXUL_NS, "menuitem");
|
|
element.setAttribute("class", "openintabs-menuitem");
|
|
element.setAttribute("label", BookmarksUtils.getLocaleString("cmd_bm_openfolder"));
|
|
element.setAttribute("accesskey", BookmarksUtils.getLocaleString("cmd_bm_openfolder_accesskey"));
|
|
aTarget.appendChild(element);
|
|
},
|
|
|
|
realHideOpenInTabsMenuItem: function (aParent)
|
|
{
|
|
if (!aParent.hasChildNodes())
|
|
return;
|
|
var child = aParent.lastChild;
|
|
var removed = 0;
|
|
while (child) {
|
|
var cclass = child.getAttribute("class");
|
|
if (cclass == "openintabs-menuitem" || cclass == "openintabs-menuseparator") {
|
|
var prevchild = child.previousSibling;
|
|
aParent.removeChild(child);
|
|
child = prevchild;
|
|
removed++;
|
|
if (removed == 2)
|
|
break;
|
|
} else {
|
|
child = child.previousSibling;
|
|
}
|
|
}
|
|
},
|
|
|
|
#ifdef XP_MACOSX
|
|
// FIXME: Work around a Mac menu bug with onpopuphidden. onpopuphiding/hidden on mac fire
|
|
// before oncommand executes on a menuitem. For now we're applying a XUL workaround
|
|
// since it's too late to fix this in the Mac widget code for 1.5.
|
|
hideOpenInTabsMenuItem: function (aTarget)
|
|
{
|
|
setTimeout(function() { BookmarksMenu.realHideOpenInTabsMenuItem(aTarget); }, 0);
|
|
},
|
|
#else
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// hides the 'Open All in Tabs' on popuphidden so that we won't duplicate it -->
|
|
hideOpenInTabsMenuItem: function (aTarget)
|
|
{
|
|
BookmarksMenu.realHideOpenInTabsMenuItem(aTarget);
|
|
},
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// returns false if...
|
|
// - the parent is the bookmark menu or the chevron
|
|
// - the menupopup contains ony one bookmark
|
|
validOpenInTabsMenuItem: function (aTarget)
|
|
{
|
|
var rParent = RDF.GetResource(aTarget.parentNode.id)
|
|
var type = BookmarksUtils.resolveType(rParent);
|
|
if (type != "Folder" && type != "PersonalToolbarFolder" && type != "Livemark")
|
|
return false;
|
|
var count = 0;
|
|
if (!aTarget.hasChildNodes())
|
|
return false;
|
|
var curr = aTarget.firstChild;
|
|
do {
|
|
type = BookmarksUtils.resolveType(curr.id);
|
|
if (type == "Bookmark" && ++count == 2)
|
|
return true;
|
|
curr = curr.nextSibling;
|
|
} while (curr);
|
|
return false;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// show an empty item if the menu is empty
|
|
showEmptyItem: function (aTarget)
|
|
{
|
|
if(aTarget.hasChildNodes())
|
|
return;
|
|
|
|
var EmptyMsg = BookmarksUtils.getLocaleString("emptyFolder");
|
|
var emptyElement = document.createElementNS(gXUL_NS, "menuitem");
|
|
emptyElement.setAttribute("id", "empty-menuitem");
|
|
emptyElement.setAttribute("label", EmptyMsg);
|
|
emptyElement.setAttribute("disabled", "true");
|
|
|
|
aTarget.appendChild(emptyElement);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// remove the empty element
|
|
hideEmptyItem: function (aTarget)
|
|
{
|
|
if (!aTarget.hasChildNodes())
|
|
return;
|
|
|
|
// if the user drags to the menu while it's open (i.e. on the toolbar),
|
|
// the bookmark gets added either before or after the Empty menu item
|
|
// before the menu is hidden. So we need to test both first and last.
|
|
if (aTarget.firstChild.id == "empty-menuitem")
|
|
aTarget.removeChild(aTarget.firstChild);
|
|
else if (aTarget.lastChild.id == "empty-menuitem")
|
|
aTarget.removeChild(aTarget.lastChild);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Fill a context menu popup with menuitems appropriate for the current
|
|
// selection.
|
|
createContextMenu: function (aEvent)
|
|
{
|
|
var target = document.popupNode;
|
|
|
|
if (!this.isBTBookmark(target.id)) {
|
|
target.removeAttribute("open");
|
|
return false;
|
|
}
|
|
|
|
var targettype = BookmarksUtils.resolveType(target.id);
|
|
|
|
if (targettype == "ImmutableFolder") {
|
|
// no context; see bug#... (popups getting stuck because "open"
|
|
// attribute doesn't get removed)
|
|
target.removeAttribute("open");
|
|
return false;
|
|
}
|
|
|
|
// -moz-user-focus: ignore; is set on toolbars
|
|
document.getElementById("PersonalToolbar").focus();
|
|
|
|
this._selection = this.getBTSelection(target);
|
|
this._orientation = this.getBTOrientation(aEvent, target);
|
|
if (targettype != "ImmutableBookmark")
|
|
this._target = this.getBTTarget(target, this._orientation);
|
|
|
|
// walk up the tree until we find a database node
|
|
var p = target;
|
|
while (p && !p.database)
|
|
p = p.parentNode;
|
|
if (p)
|
|
this._db = p.database;
|
|
|
|
BookmarksCommand.createContextMenu(aEvent, this._selection, this._db);
|
|
this.onCommandUpdate();
|
|
aEvent.target.addEventListener("mousemove", BookmarksMenuController.onMouseMove, false);
|
|
return true;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Clean up after closing the context menu popup
|
|
destroyContextMenu: function (aEvent)
|
|
{
|
|
// note that this method is called after doCommand.
|
|
// let's focus the content and dismiss the popup chain (needed when the user
|
|
// type escape or if he/she clicks outside the context menu)
|
|
if (content)
|
|
content.focus();
|
|
// XXXpch: see bug 210910, it should be done properly in the backend
|
|
BookmarksMenuDNDObserver.mCurrentDragOverTarget = null;
|
|
BookmarksMenuDNDObserver.onDragCloseTarget();
|
|
|
|
// if the user types escape, we need to remove the feedback
|
|
BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
|
|
|
|
aEvent.target.removeEventListener("mousemove", BookmarksMenuController.onMouseMove, false);
|
|
|
|
this._target = null;
|
|
this._selection = null;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// returns the formatted selection from aNode
|
|
getBTSelection: function (aNode)
|
|
{
|
|
var item;
|
|
switch (aNode.id) {
|
|
case "bookmarks-ptf":
|
|
item = BMSVC.getBookmarksToolbarFolder().Value;
|
|
break;
|
|
case "bookmarks-menu":
|
|
item = "NC:BookmarksRoot";
|
|
break;
|
|
default:
|
|
item = aNode.id;
|
|
if (!this.isBTBookmark(item))
|
|
return {length:0};
|
|
}
|
|
var parent = this.getBTContainer(aNode);
|
|
var isExpanded = aNode.hasAttribute("open") && aNode.open;
|
|
var selection = {};
|
|
selection.item = [RDF.GetResource(item)];
|
|
selection.parent = [RDF.GetResource(parent)];
|
|
selection.isExpanded = [isExpanded];
|
|
selection.length = selection.item.length;
|
|
BookmarksUtils.checkSelection(selection);
|
|
return selection;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// returns the insertion target from aNode
|
|
getBTTarget: function (aNode, aOrientation)
|
|
{
|
|
var item, parent, index;
|
|
switch (aNode.id) {
|
|
case "bookmarks-ptf":
|
|
parent = BMSVC.getBookmarksToolbarFolder().Value;
|
|
item = BookmarksToolbar.getLastVisibleBookmark();
|
|
break;
|
|
case "bookmarks-menu":
|
|
parent = "NC:BookmarksRoot";
|
|
break;
|
|
case "bookmarks-chevron":
|
|
parent = BMSVC.getBookmarksToolbarFolder().Value;
|
|
break;
|
|
default:
|
|
if (aOrientation == BookmarksUtils.DROP_ON)
|
|
parent = aNode.id
|
|
else {
|
|
parent = this.getBTContainer(aNode);
|
|
item = aNode;
|
|
}
|
|
}
|
|
|
|
parent = RDF.GetResource(parent);
|
|
if (aOrientation == BookmarksUtils.DROP_ON)
|
|
return BookmarksUtils.getTargetFromFolder(parent);
|
|
|
|
item = RDF.GetResource(item.id);
|
|
RDFC.Init(BMDS, parent);
|
|
index = RDFC.IndexOf(item);
|
|
if (aOrientation == BookmarksUtils.DROP_AFTER)
|
|
++index;
|
|
|
|
return { parent: parent, index: index };
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// returns the parent resource of a node in the personal toolbar.
|
|
// this is determined by inspecting the source element and walking up the
|
|
// DOM tree to find the appropriate containing node.
|
|
getBTContainer: function (aNode)
|
|
{
|
|
var parent;
|
|
var item = aNode.id;
|
|
if (!this.isBTBookmark(item))
|
|
return "NC:BookmarksRoot"
|
|
parent = aNode.parentNode.parentNode;
|
|
parent = parent.id;
|
|
switch (parent) {
|
|
case "bookmarks-chevron":
|
|
case "bookmarks-stack":
|
|
case "bookmarks-toolbar":
|
|
return BMSVC.getBookmarksToolbarFolder().Value;
|
|
case "bookmarks-menu":
|
|
return "NC:BookmarksRoot";
|
|
default:
|
|
return parent;
|
|
}
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// returns true if the node is a bookmark, a folder or a bookmark separator
|
|
isBTBookmark: function (aURI)
|
|
{
|
|
if (!aURI || aURI == "bookmarkAllCmd")
|
|
return false;
|
|
var type = BookmarksUtils.resolveType(aURI);
|
|
return (type == "BookmarkSeparator" ||
|
|
type == "Bookmark" ||
|
|
type == "Folder" ||
|
|
type == "PersonalToolbarFolder" ||
|
|
type == "Livemark" ||
|
|
type == "ImmutableBookmark" ||
|
|
type == "ImmutableFolder" ||
|
|
aURI == "bookmarks-ptf")
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// returns true if the node is a container. -->
|
|
isBTContainer: function (aTarget)
|
|
{
|
|
return aTarget.localName == "menu" || (aTarget.localName == "toolbarbutton" &&
|
|
(aTarget.getAttribute("container") == "true"));
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// returns BookmarksUtils.DROP_BEFORE, DROP_ON or DROP_AFTER accordingly
|
|
// to the event coordinates. Skin authors could break us, we'll cross that
|
|
// bridge when they turn us 90degrees. -->
|
|
getBTOrientation: function (aEvent, aTarget)
|
|
{
|
|
var target
|
|
if (!aTarget)
|
|
target = aEvent.target;
|
|
else
|
|
target = aTarget;
|
|
if (target.localName == "menu" &&
|
|
target.parentNode.localName != "menupopup" ||
|
|
target.id == "bookmarks-chevron")
|
|
return BookmarksUtils.DROP_ON;
|
|
if (target.id == "bookmarks-ptf") {
|
|
return target.hasChildNodes()?
|
|
BookmarksUtils.DROP_AFTER:BookmarksUtils.DROP_ON;
|
|
}
|
|
|
|
var overButtonBoxObject = target.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
|
|
var overParentBoxObject = target.parentNode.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
|
|
|
|
var size, border;
|
|
var coordValue, clientCoordValue;
|
|
switch (target.localName) {
|
|
case "toolbarseparator":
|
|
case "toolbarbutton":
|
|
size = overButtonBoxObject.width;
|
|
coordValue = overButtonBoxObject.x;
|
|
clientCoordValue = aEvent.clientX;
|
|
break;
|
|
case "menuseparator":
|
|
case "menu":
|
|
case "menuitem":
|
|
size = overButtonBoxObject.height;
|
|
coordValue = overButtonBoxObject.screenY;
|
|
clientCoordValue = aEvent.screenY;
|
|
break;
|
|
default: return BookmarksUtils.DROP_ON;
|
|
}
|
|
if (this.isBTContainer(target))
|
|
if (target.localName == "toolbarbutton") {
|
|
// the DROP_BEFORE area excludes the label
|
|
var iconNode = document.getAnonymousElementByAttribute(target, "class", "toolbarbutton-icon");
|
|
border = parseInt(document.defaultView.getComputedStyle(target,"").getPropertyValue("padding-left")) +
|
|
parseInt(document.defaultView.getComputedStyle(iconNode ,"").getPropertyValue("width"));
|
|
border = Math.min(size/5,Math.max(border,4));
|
|
} else
|
|
border = size/5;
|
|
else
|
|
border = size/2;
|
|
|
|
// in the first region?
|
|
if (clientCoordValue-coordValue < border)
|
|
return BookmarksUtils.DROP_BEFORE;
|
|
// in the last region?
|
|
else if (clientCoordValue-coordValue >= size-border)
|
|
return BookmarksUtils.DROP_AFTER;
|
|
else // must be in the middle somewhere
|
|
return BookmarksUtils.DROP_ON;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// expand the folder targeted by the context menu.
|
|
expandBTFolder: function ()
|
|
{
|
|
var target = document.popupNode.lastChild;
|
|
if (document.popupNode.open)
|
|
target.hidePopup();
|
|
else
|
|
target.showPopup(document.popupNode);
|
|
},
|
|
|
|
onCommandUpdate: function ()
|
|
{
|
|
var selection = this._selection;
|
|
var target = this._target;
|
|
BookmarksController.onCommandUpdate(selection, target);
|
|
if (document.popupNode.id == "bookmarks-ptf") {
|
|
// disabling 'cut' and 'copy' on the empty area of the personal toolbar
|
|
var commandNode = document.getElementById("cmd_cut");
|
|
commandNode.setAttribute("disabled", "true");
|
|
commandNode = document.getElementById("cmd_copy");
|
|
commandNode.setAttribute("disabled", "true");
|
|
}
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Load a bookmark in menus or toolbar buttons
|
|
// aTarget may not the aEvent target (see Open all in tabs command)
|
|
loadBookmark: function (aEvent, aTarget, aDS)
|
|
{
|
|
if (aTarget.getAttribute("class") == "openintabs-menuitem")
|
|
aTarget = aTarget.parentNode.parentNode;
|
|
|
|
// Check for invalid bookmarks (most likely a static menu item like "Manage Bookmarks")
|
|
if (!this.isBTBookmark(aTarget.id))
|
|
return;
|
|
var rSource = RDF.GetResource(aTarget.id);
|
|
var selection = BookmarksUtils.getSelectionFromResource(rSource);
|
|
var browserTarget = whereToOpenLink(aEvent);
|
|
BookmarksCommand.openBookmark(selection, browserTarget, aDS);
|
|
aEvent.stopPropagation();
|
|
},
|
|
|
|
////////////////////////////////////////////////
|
|
// loads a bookmark with the mouse middle button
|
|
loadBookmarkMiddleClick: function (aEvent, aDS)
|
|
{
|
|
if (aEvent.button != 1)
|
|
return;
|
|
// unlike for command events, we have to close the menus manually
|
|
BookmarksMenuDNDObserver.mCurrentDragOverTarget = null;
|
|
BookmarksMenuDNDObserver.onDragCloseTarget();
|
|
this.loadBookmark(aEvent, aEvent.target, aDS);
|
|
}
|
|
}
|
|
|
|
var BookmarksMenuController = {
|
|
|
|
supportsCommand: BookmarksController.supportsCommand,
|
|
|
|
isCommandEnabled: function (aCommand)
|
|
{
|
|
var selection = BookmarksMenu._selection;
|
|
var target = BookmarksMenu._target;
|
|
if (selection)
|
|
return BookmarksController.isCommandEnabled(aCommand, selection, target);
|
|
return false;
|
|
},
|
|
|
|
doCommand: function (aCommand)
|
|
{
|
|
// we needed to focus the element that has the bm command controller
|
|
// to get here. Now, let's focus the content before performing the command:
|
|
// if a modal dialog is called from now, the content will be focused again
|
|
// automatically after dismissing the dialog
|
|
if (content)
|
|
content.focus();
|
|
BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
|
|
|
|
// if a dialog opens, the "open" attribute of a menuitem-container
|
|
// clicked on won't be removed. We do it manually.
|
|
var element = document.popupNode.firstChild;
|
|
if (element && element.localName == "menupopup")
|
|
element.hidePopup();
|
|
|
|
var selection = BookmarksMenu._selection;
|
|
var target = BookmarksMenu._target;
|
|
var db = BookmarksMenu._db;
|
|
switch (aCommand) {
|
|
case "cmd_bm_expandfolder":
|
|
BookmarksMenu.expandBTFolder();
|
|
break;
|
|
default:
|
|
BookmarksController.doCommand(aCommand, selection, target, db);
|
|
}
|
|
},
|
|
|
|
onMouseMove: function (aEvent)
|
|
{
|
|
var command = aEvent.target.getAttribute("command");
|
|
var isDisabled = aEvent.target.getAttribute("disabled")
|
|
if (isDisabled != "true" && (command == "cmd_bm_newfolder" || command == "cmd_paste")) {
|
|
BookmarksMenuDNDObserver.onDragSetFeedBack(document.popupNode, BookmarksMenu._orientation);
|
|
} else {
|
|
BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
var BookmarksMenuDNDObserver = {
|
|
|
|
////////////////////
|
|
// Public methods //
|
|
////////////////////
|
|
|
|
onDragStart: function (aEvent, aXferData, aDragAction)
|
|
{
|
|
var target = aEvent.target;
|
|
|
|
// Prevent dragging from invalid regions
|
|
|
|
// can't drag from the empty areas
|
|
if (target.id == "bookmarks-menu" ||
|
|
target.id == "bookmarks-chevron" ||
|
|
target.id == "bookmarks-ptf")
|
|
return false;
|
|
|
|
if (!BookmarksMenu.isBTBookmark(target.id))
|
|
return false;
|
|
|
|
// Prevent dragging out of menupopups on non Win32 platforms.
|
|
// a) on Mac drag from menus is generally regarded as being satanic
|
|
// b) on Linux, this causes an X-server crash, (bug 151336)
|
|
// c) on Windows, there is no hang or crash associated with this, so we'll leave
|
|
// the functionality there.
|
|
if (navigator.platform != "Win32" && target.localName != "toolbarbutton")
|
|
return false;
|
|
|
|
// a drag start is fired when leaving an open toolbarbutton(type=menu)
|
|
// (see bug 143031)
|
|
if (this.isContainer(target)) {
|
|
if (this.isPlatformNotSupported)
|
|
return false;
|
|
if (!aEvent.shiftKey && !aEvent.altKey && !aEvent.ctrlKey)
|
|
return false;
|
|
// menus open on mouse down
|
|
target.firstChild.hidePopup();
|
|
}
|
|
var selection = BookmarksMenu.getBTSelection(target);
|
|
aXferData.data = BookmarksUtils.getXferDataFromSelection(selection);
|
|
return true;
|
|
},
|
|
|
|
onDragOver: function(aEvent, aFlavour, aDragSession)
|
|
{
|
|
var orientation = BookmarksMenu.getBTOrientation(aEvent)
|
|
if (aDragSession.canDrop)
|
|
this.onDragSetFeedBack(aEvent.target, orientation);
|
|
if (orientation != this.mCurrentDropPosition) {
|
|
// emulating onDragExit and onDragEnter events since the drop region
|
|
// has changed on the target.
|
|
this.onDragExit(aEvent, aDragSession);
|
|
this.onDragEnter(aEvent, aDragSession);
|
|
}
|
|
if (this.isPlatformNotSupported)
|
|
return;
|
|
if (this.isTimerSupported || !aDragSession.sourceNode)
|
|
return;
|
|
this.onDragOverCheckTimers();
|
|
},
|
|
|
|
onDragEnter: function (aEvent, aDragSession)
|
|
{
|
|
var target = aEvent.target;
|
|
var orientation = BookmarksMenu.getBTOrientation(aEvent);
|
|
if (target.localName == "menupopup" || target.id == "bookmarks-ptf")
|
|
target = target.parentNode;
|
|
if (aDragSession.canDrop) {
|
|
this.onDragSetFeedBack(target, orientation);
|
|
this.onDragEnterSetTimer(target, aDragSession);
|
|
}
|
|
this.mCurrentDragOverTarget = target;
|
|
this.mCurrentDropPosition = orientation;
|
|
},
|
|
|
|
onDragExit: function (aEvent, aDragSession)
|
|
{
|
|
var target = aEvent.target;
|
|
if (target.localName == "menupopup" || target.id == "bookmarks-ptf")
|
|
target = target.parentNode;
|
|
this.onDragRemoveFeedBack(target);
|
|
this.onDragExitSetTimer(target, aDragSession);
|
|
this.mCurrentDragOverTarget = null;
|
|
this.mCurrentDropPosition = null;
|
|
},
|
|
|
|
onDrop: function (aEvent, aXferData, aDragSession)
|
|
{
|
|
var target = aEvent.target;
|
|
this.onDragRemoveFeedBack(target);
|
|
|
|
var selection = BookmarksUtils.getSelectionFromXferData(aDragSession);
|
|
|
|
var orientation = BookmarksMenu.getBTOrientation(aEvent);
|
|
|
|
// For RTL PersonalBar bookmarks buttons, orientation should be inverted (only in drop case)
|
|
var PBStyle = window.getComputedStyle(document.getElementById("PersonalToolbar"),'');
|
|
var isHorizontal = (target.localName == "toolbarbutton");
|
|
if ((PBStyle.direction == 'rtl') && isHorizontal) {
|
|
if (orientation == BookmarksUtils.DROP_AFTER)
|
|
orientation = BookmarksUtils.DROP_BEFORE;
|
|
else if (orientation == BookmarksUtils.DROP_BEFORE)
|
|
orientation = BookmarksUtils.DROP_AFTER;
|
|
}
|
|
|
|
var selTarget = BookmarksMenu.getBTTarget(target, orientation);
|
|
|
|
// we can only test for kCopyAction if the source is a bookmark
|
|
var checkCopy = aDragSession.isDataFlavorSupported("moz/rdfitem");
|
|
|
|
const kDSIID = Components.interfaces.nsIDragService;
|
|
const kCopyAction = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
|
|
|
|
// hide the 'open in tab' menuseparator because bookmarks
|
|
// can be inserted after it if they are dropped after the last bookmark
|
|
// a more comprehensive fix would be in the menupopup template builder
|
|
var menuSeparator = null;
|
|
var menuTarget = (target.localName == "toolbarbutton" ||
|
|
target.localName == "menu") &&
|
|
orientation == BookmarksUtils.DROP_ON?
|
|
target.lastChild:target.parentNode;
|
|
if (menuTarget.hasChildNodes() &&
|
|
menuTarget.lastChild.getAttribute("class") == "openintabs-menuitem") {
|
|
menuSeparator = menuTarget.lastChild.previousSibling;
|
|
menuTarget.removeChild(menuSeparator);
|
|
}
|
|
|
|
// doCopy defaults to true; check if we should make it false.
|
|
// we make it false only if all the selection items have valid parent
|
|
// bookmark DS containers (i.e. aren't generated via aggregation)
|
|
var doCopy = true;
|
|
if (checkCopy && !(aDragSession.dragAction & kCopyAction))
|
|
doCopy = BookmarksUtils.shouldCopySelection("drag", selection);
|
|
|
|
if (doCopy)
|
|
BookmarksUtils.insertAndCheckSelection("drag", selection, selTarget);
|
|
else
|
|
BookmarksUtils.moveAndCheckSelection("drag", selection, selTarget);
|
|
|
|
// show again the menuseparator
|
|
if (menuSeparator)
|
|
menuTarget.insertBefore(menuSeparator, menuTarget.lastChild);
|
|
|
|
},
|
|
|
|
canDrop: function (aEvent, aDragSession)
|
|
{
|
|
var target = aEvent.target;
|
|
if (!BookmarksMenu.isBTBookmark(target.id))
|
|
return false;
|
|
|
|
var btype = BookmarksUtils.resolveType(target.id);
|
|
|
|
return target.id == "bookmarks-menu" ||
|
|
target.id == "bookmarks-chevron" ||
|
|
target.id == "bookmarks-ptf" ||
|
|
(target.id != "NC:SystemBookmarksStaticRoot" &&
|
|
btype == "Folder" ||
|
|
btype == "Bookmark");
|
|
},
|
|
|
|
canHandleMultipleItems: true,
|
|
|
|
getSupportedFlavours: function ()
|
|
{
|
|
var flavourSet = new FlavourSet();
|
|
flavourSet.appendFlavour("moz/rdfitem");
|
|
flavourSet.appendFlavour("text/x-moz-url");
|
|
flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
|
|
flavourSet.appendFlavour("text/unicode");
|
|
return flavourSet;
|
|
},
|
|
|
|
|
|
////////////////////////////////////
|
|
// Private methods and properties //
|
|
////////////////////////////////////
|
|
|
|
springLoadedMenuDelay: 350, // milliseconds
|
|
isPlatformNotSupported: navigator.platform.indexOf("Mac") != -1, // see bug 136524
|
|
// Needs to be dynamically overridden (to |true|) in the case of an external drag: see bug 232795.
|
|
isTimerSupported: navigator.platform.indexOf("Win") == -1,
|
|
|
|
mCurrentDragOverTarget: null,
|
|
mCurrentDropPosition: null,
|
|
loadTimer : null,
|
|
closeTimer : null,
|
|
loadTarget : null,
|
|
closeTarget: null,
|
|
|
|
_observers : null,
|
|
get mObservers ()
|
|
{
|
|
if (!this._observers) {
|
|
this._observers = [
|
|
document.getElementById("bookmarks-ptf"),
|
|
// menubar menus haven't an "open" attribute: we can take the child
|
|
document.getElementById("bookmarks-menu").firstChild,
|
|
document.getElementById("bookmarks-chevron").parentNode
|
|
]
|
|
}
|
|
return this._observers;
|
|
},
|
|
|
|
getObserverForNode: function (aNode)
|
|
{
|
|
if (!aNode)
|
|
return null;
|
|
var node = aNode;
|
|
var observer;
|
|
while (node) {
|
|
for (var i=0; i < this.mObservers.length; i++) {
|
|
observer = this.mObservers[i];
|
|
if (observer == node)
|
|
return observer;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
onDragCloseMenu: function (aNode)
|
|
{
|
|
var children = aNode.childNodes;
|
|
for (var i = 0; i < children.length; i++) {
|
|
if (this.isContainer(children[i]) &&
|
|
children[i].getAttribute("open") == "true") {
|
|
this.onDragCloseMenu(children[i].lastChild);
|
|
if (children[i] != this.mCurrentDragOverTarget || this.mCurrentDropPosition != BookmarksUtils.DROP_ON)
|
|
children[i].lastChild.hidePopup();
|
|
}
|
|
}
|
|
},
|
|
|
|
onDragCloseTarget: function ()
|
|
{
|
|
var currentObserver = this.getObserverForNode(this.mCurrentDragOverTarget);
|
|
// close all the menus not hovered by the mouse
|
|
for (var i=0; i < this.mObservers.length; i++) {
|
|
if (currentObserver != this.mObservers[i]) {
|
|
this.onDragCloseMenu(this.mObservers[i]);
|
|
if (this.mObservers[i].parentNode.id == "bookmarks-menu")
|
|
this.mObservers[i].hidePopup();
|
|
} else
|
|
this.onDragCloseMenu(this.mCurrentDragOverTarget.parentNode);
|
|
}
|
|
},
|
|
|
|
onDragLoadTarget: function (aTarget)
|
|
{
|
|
if (!this.mCurrentDragOverTarget)
|
|
return;
|
|
// Load the current menu
|
|
if (this.mCurrentDropPosition == BookmarksUtils.DROP_ON &&
|
|
this.isContainer(aTarget))
|
|
aTarget.lastChild.showPopup(aTarget);
|
|
},
|
|
|
|
onDragOverCheckTimers: function ()
|
|
{
|
|
var now = new Date().getTime();
|
|
if (this.closeTimer && now-this.springLoadedMenuDelay>this.closeTimer) {
|
|
this.onDragCloseTarget();
|
|
this.closeTimer = null;
|
|
}
|
|
if (this.loadTimer && (now-this.springLoadedMenuDelay>this.loadTimer)) {
|
|
this.onDragLoadTarget(this.loadTarget);
|
|
this.loadTimer = null;
|
|
}
|
|
},
|
|
|
|
onDragEnterSetTimer: function (aTarget, aDragSession)
|
|
{
|
|
if (this.isPlatformNotSupported)
|
|
return;
|
|
if (this.isTimerSupported || !aDragSession.sourceNode) {
|
|
var targetToBeLoaded = aTarget;
|
|
clearTimeout(this.loadTimer);
|
|
if (aTarget == aDragSession.sourceNode)
|
|
return;
|
|
var This = this;
|
|
this.loadTimer=setTimeout(function () {This.onDragLoadTarget(targetToBeLoaded)}, This.springLoadedMenuDelay);
|
|
} else {
|
|
var now = new Date().getTime();
|
|
this.loadTimer = now;
|
|
this.loadTarget = aTarget;
|
|
}
|
|
},
|
|
|
|
onDragExitSetTimer: function (aTarget, aDragSession)
|
|
{
|
|
if (this.isPlatformNotSupported)
|
|
return;
|
|
var This = this;
|
|
if (this.isTimerSupported || !aDragSession.sourceNode) {
|
|
clearTimeout(this.closeTimer)
|
|
this.closeTimer=setTimeout(function () {This.onDragCloseTarget()}, This.springLoadedMenuDelay);
|
|
} else {
|
|
var now = new Date().getTime();
|
|
this.closeTimer = now;
|
|
this.closeTarget = aTarget;
|
|
this.loadTimer = null;
|
|
|
|
// If the user isn't rearranging within the menu, close it
|
|
// To do so, we exploit a Mac bug: timeout set during
|
|
// drag and drop on Windows and Mac are fired only after that the drop is released.
|
|
// timeouts will pile up, we may have a better approach but for the moment, this one
|
|
// correctly close the menus after a drop/cancel outside the personal toolbar.
|
|
// The if statement in the function has been introduced to deal with rare but reproducible
|
|
// missing Exit events.
|
|
if (aDragSession.sourceNode.localName != "menuitem" && aDragSession.sourceNode.localName != "menu")
|
|
setTimeout(function () { if (This.mCurrentDragOverTarget) {This.onDragRemoveFeedBack(This.mCurrentDragOverTarget); This.mCurrentDragOverTarget=null} This.loadTimer=null; This.onDragCloseTarget() }, 0);
|
|
}
|
|
},
|
|
|
|
onDragSetFeedBack: function (aTarget, aOrientation)
|
|
{
|
|
switch (aTarget.localName) {
|
|
case "toolbarseparator":
|
|
case "toolbarbutton":
|
|
switch (aOrientation) {
|
|
case BookmarksUtils.DROP_BEFORE:
|
|
aTarget.setAttribute("dragover-left", "true");
|
|
break;
|
|
case BookmarksUtils.DROP_AFTER:
|
|
aTarget.setAttribute("dragover-right", "true");
|
|
break;
|
|
case BookmarksUtils.DROP_ON:
|
|
aTarget.setAttribute("dragover-top" , "true");
|
|
aTarget.setAttribute("dragover-bottom", "true");
|
|
aTarget.setAttribute("dragover-left" , "true");
|
|
aTarget.setAttribute("dragover-right" , "true");
|
|
break;
|
|
}
|
|
break;
|
|
case "menuseparator":
|
|
case "menu":
|
|
case "menuitem":
|
|
switch (aOrientation) {
|
|
case BookmarksUtils.DROP_BEFORE:
|
|
aTarget.setAttribute("dragover-top", "true");
|
|
break;
|
|
case BookmarksUtils.DROP_AFTER:
|
|
aTarget.setAttribute("dragover-bottom", "true");
|
|
break;
|
|
case BookmarksUtils.DROP_ON:
|
|
break;
|
|
}
|
|
break;
|
|
case "hbox" :
|
|
// hit between the last visible bookmark and the chevron
|
|
var newTarget = BookmarksToolbar.getLastVisibleBookmark();
|
|
if (newTarget)
|
|
newTarget.setAttribute("dragover-right", "true");
|
|
break;
|
|
case "stack" :
|
|
case "menupopup": break;
|
|
default: dump("No feedback for: "+aTarget.localName+"\n");
|
|
}
|
|
},
|
|
|
|
onDragRemoveFeedBack: function (aTarget)
|
|
{
|
|
var newTarget;
|
|
var bt;
|
|
if (aTarget.id == "bookmarks-ptf") {
|
|
// hit when dropping in the bt or between the last visible bookmark
|
|
// and the chevron
|
|
newTarget = BookmarksToolbar.getLastVisibleBookmark();
|
|
if (newTarget)
|
|
newTarget.removeAttribute("dragover-right");
|
|
} else if (aTarget.id == "bookmarks-stack") {
|
|
newTarget = BookmarksToolbar.getLastVisibleBookmark();
|
|
newTarget.removeAttribute("dragover-right");
|
|
} else {
|
|
aTarget.removeAttribute("dragover-left");
|
|
aTarget.removeAttribute("dragover-right");
|
|
aTarget.removeAttribute("dragover-top");
|
|
aTarget.removeAttribute("dragover-bottom");
|
|
}
|
|
},
|
|
|
|
onDropSetFeedBack: function (aTarget)
|
|
{
|
|
//XXX Not yet...
|
|
},
|
|
|
|
isContainer: function (aTarget)
|
|
{
|
|
return aTarget.localName == "menu" ||
|
|
aTarget.localName == "toolbarbutton" &&
|
|
aTarget.getAttribute("type") == "menu";
|
|
}
|
|
}
|
|
|
|
var BookmarksToolbar =
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// make bookmarks toolbar act like menus
|
|
openedMenuButton:null,
|
|
autoOpenMenu: function (aEvent)
|
|
{
|
|
var target = aEvent.target;
|
|
if (BookmarksToolbar.openedMenuButton != target &&
|
|
target.nodeName == "toolbarbutton" &&
|
|
target.type == "menu") {
|
|
BookmarksToolbar.openedMenuButton.open = false;
|
|
target.open = true;
|
|
}
|
|
},
|
|
setOpenedMenu: function (aEvent)
|
|
{
|
|
if (aEvent.target.parentNode.localName == 'toolbarbutton') {
|
|
if (!this.openedMenuButton)
|
|
aEvent.currentTarget.addEventListener("mouseover", this.autoOpenMenu, true);
|
|
this.openedMenuButton = aEvent.target.parentNode;
|
|
}
|
|
},
|
|
unsetOpenedMenu: function (aEvent)
|
|
{
|
|
if (aEvent.target.parentNode.localName == 'toolbarbutton') {
|
|
aEvent.currentTarget.removeEventListener("mouseover", this.autoOpenMenu, true);
|
|
this.openedMenuButton = null;
|
|
}
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// returns the node of the last visible bookmark on the toolbar -->
|
|
getLastVisibleBookmark: function ()
|
|
{
|
|
var buttons = document.getElementById("bookmarks-ptf");
|
|
var button = buttons.firstChild;
|
|
if (!button)
|
|
return null; // empty bookmarks toolbar
|
|
do {
|
|
if (button.collapsed)
|
|
return button.previousSibling;
|
|
button = button.nextSibling;
|
|
} while (button)
|
|
return buttons.lastChild;
|
|
},
|
|
|
|
updateOverflowMenu: function (aMenuPopup)
|
|
{
|
|
var hbox = document.getElementById("bookmarks-ptf");
|
|
for (var i = 0; i < hbox.childNodes.length; i++) {
|
|
var button = hbox.childNodes[i];
|
|
var menu = aMenuPopup.childNodes[i];
|
|
if (menu.collapsed == button.collapsed)
|
|
menu.collapsed = !menu.collapsed;
|
|
}
|
|
},
|
|
|
|
resizeFunc: function(event)
|
|
{
|
|
if (event && event.type == 'focus')
|
|
window.removeEventListener('focus', BookmarksToolbar.resizeFunc, false); // hack for bug 266737
|
|
var buttons = document.getElementById("bookmarks-ptf");
|
|
if (!buttons)
|
|
return;
|
|
var chevron = document.getElementById("bookmarks-chevron");
|
|
var width = window.innerWidth;
|
|
if (width == 0) { // hack for bug 266737
|
|
window.addEventListener('focus', BookmarksToolbar.resizeFunc, false);
|
|
return;
|
|
}
|
|
var myToolbar = buttons.parentNode.parentNode.parentNode;
|
|
for (var i = myToolbar.childNodes.length-1; i >= 0; i--){
|
|
var anItem = myToolbar.childNodes[i];
|
|
if (anItem.id == "personal-bookmarks") {
|
|
break;
|
|
}
|
|
width -= anItem.boxObject.width;
|
|
}
|
|
var chevronWidth = 0;
|
|
chevron.collapsed = false;
|
|
chevronWidth = chevron.boxObject.width;
|
|
chevron.collapsed = true;
|
|
var overflowed = false;
|
|
|
|
var isLTR=window.getComputedStyle(document.getElementById("PersonalToolbar"),'').direction=='ltr';
|
|
|
|
for (var i=0; i<buttons.childNodes.length; i++) {
|
|
var button = buttons.childNodes[i];
|
|
button.collapsed = overflowed;
|
|
|
|
if (i == buttons.childNodes.length - 1) // last ptf item...
|
|
chevronWidth = 0;
|
|
var offset = isLTR ? button.boxObject.x
|
|
: width - button.boxObject.x;
|
|
if (offset + button.boxObject.width + chevronWidth > width) {
|
|
overflowed = true;
|
|
// This button doesn't fit. Show it in the menu. Hide it in the toolbar.
|
|
if (!button.collapsed)
|
|
button.collapsed = true;
|
|
if (chevron.collapsed) {
|
|
chevron.collapsed = false;
|
|
var overflowPadder = document.getElementById("overflow-padder");
|
|
offset = isLTR ? buttons.boxObject.x
|
|
: width - buttons.boxObject.x - buttons.boxObject.width;
|
|
overflowPadder.width = width - chevron.boxObject.width - offset;
|
|
}
|
|
}
|
|
}
|
|
BookmarksToolbarRDFObserver._overflowTimerInEffect = false;
|
|
},
|
|
|
|
// Fill in tooltips for personal toolbar
|
|
fillInBTTooltip: function (tipElement)
|
|
{
|
|
|
|
var title = tipElement.label;
|
|
var url = tipElement.statusText;
|
|
|
|
if (!title && !url) {
|
|
// bail out early if there is nothing to show
|
|
return false;
|
|
}
|
|
|
|
var tooltipTitle = document.getElementById("btTitleText");
|
|
var tooltipUrl = document.getElementById("btUrlText");
|
|
if (title && title != url) {
|
|
tooltipTitle.removeAttribute("hidden");
|
|
tooltipTitle.setAttribute("value", title);
|
|
} else {
|
|
tooltipTitle.setAttribute("hidden", "true");
|
|
}
|
|
if (url) {
|
|
tooltipUrl.removeAttribute("hidden");
|
|
tooltipUrl.setAttribute("value", url);
|
|
} else {
|
|
tooltipUrl.setAttribute("hidden", "true");
|
|
}
|
|
return true; // show tooltip
|
|
}
|
|
}
|
|
|
|
// Implement nsIRDFObserver so we can update our overflow state when items get
|
|
// added/removed from the toolbar
|
|
var BookmarksToolbarRDFObserver =
|
|
{
|
|
onAssert: function (aDataSource, aSource, aProperty, aTarget)
|
|
{
|
|
if (aProperty.Value == gNC_NS+"BookmarksToolbarFolder") {
|
|
var bt = document.getElementById("bookmarks-ptf");
|
|
if (bt) {
|
|
bt.ref = aSource.Value;
|
|
document.getElementById("bookmarks-chevron").ref = aSource.Value;
|
|
}
|
|
}
|
|
this.setOverflowTimeout(aSource, aProperty);
|
|
},
|
|
onUnassert: function (aDataSource, aSource, aProperty, aTarget)
|
|
{
|
|
this.setOverflowTimeout(aSource, aProperty);
|
|
},
|
|
onChange: function (aDataSource, aSource, aProperty, aOldTarget, aNewTarget)
|
|
{
|
|
this.setOverflowTimeout(aSource, aProperty);
|
|
},
|
|
onMove: function (aDataSource, aOldSource, aNewSource, aProperty, aTarget) {},
|
|
onBeginUpdateBatch: function (aDataSource) {},
|
|
onEndUpdateBatch: function (aDataSource)
|
|
{
|
|
this._overflowTimerInEffect = true;
|
|
setTimeout(BookmarksToolbar.resizeFunc, 0, null);
|
|
},
|
|
_overflowTimerInEffect: false,
|
|
setOverflowTimeout: function (aSource, aProperty)
|
|
{
|
|
if (this._overflowTimerInEffect)
|
|
return;
|
|
if (aProperty.Value == gWEB_NS+"LastModifiedDate")
|
|
return;
|
|
this._overflowTimerInEffect = true;
|
|
setTimeout(BookmarksToolbar.resizeFunc, 0, null);
|
|
}
|
|
}
|