gecko-dev/xpfe/browser/resources/content/personalToolbar.js
2001-12-07 02:44:11 +00:00

519 lines
19 KiB
JavaScript

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Original Author)
*
* 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 NPL, 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 NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var gBookmarksShell = null;
///////////////////////////////////////////////////////////////////////////////
// Class which defines methods for a bookmarks UI implementation based around
// a toolbar. Subclasses BookmarksBase in bookmarksOverlay.js. Some methods
// are required by the base class, others are for event handling. Window specific
// glue code should go into the BookmarksWindow class in bookmarks.js
function BookmarksToolbar (aID)
{
this.id = aID;
}
BookmarksToolbar.prototype = {
__proto__: BookmarksUIElement.prototype,
/////////////////////////////////////////////////////////////////////////////
// Personal Toolbar Specific Stuff
get db ()
{
return this.element.database;
},
get element ()
{
return document.getElementById(this.id);
},
/////////////////////////////////////////////////////////////////////////////
// This method constructs a menuitem for a context menu for the given command.
// This is implemented by the client so that it can intercept menuitem naming
// as appropriate.
createMenuItem: function (aDisplayName, aCommandName, aItemNode)
{
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var xulElement = document.createElementNS(kXULNS, "menuitem");
xulElement.setAttribute("cmd", aCommandName);
var cmd = "cmd_" + aCommandName.substring(NC_NS_CMD.length)
xulElement.setAttribute("command", cmd);
switch (aCommandName) {
case NC_NS_CMD + "bm_open":
xulElement.setAttribute("label", aDisplayName);
xulElement.setAttribute("default", "true");
break;
case NC_NS_CMD + "bm_openfolder":
xulElement.setAttribute("default", "true");
if (aItemNode.localName == "hbox")
// Don't show an "Open Folder" item for clicks on the toolbar itself.
return null;
default:
xulElement.setAttribute("label", aDisplayName);
break;
}
return xulElement;
},
// Command implementation
commands: {
openFolder: function (aSelectedItem)
{
var mbo = aSelectedItem.boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject);
mbo.openMenu(true);
},
editCell: function (aSelectedItem, aXXXLameAssIndex)
{
goDoCommand("cmd_bm_properties");
return; // Disable Inline Edit for now. See bug 77125 for why this is being disabled
// on the personal toolbar for the moment.
if (aSelectedItem.getAttribute("editable") != "true")
return;
var property = "http://home.netscape.com/NC-rdf#Name";
aSelectedItem.setMode("edit");
aSelectedItem.addObserver(this.postModifyCallback, "accept",
[gBookmarksShell, aSelectedItem, property]);
},
///////////////////////////////////////////////////////////////////////////
// Called after an inline-edit cell has left inline-edit mode, and data
// needs to be modified in the datasource.
postModifyCallback: function (aParams)
{
var aShell = aParams[0];
var selItemURI = NODE_ID(aParams[1]);
aShell.propertySet(selItemURI, aParams[2], aParams[3]);
},
///////////////////////////////////////////////////////////////////////////
// Creates a dummy item that can be placed in edit mode to retrieve data
// to create new bookmarks/folders.
createBookmarkItem: function (aMode, aSelectedItem)
{
/////////////////////////////////////////////////////////////////////////
// HACK HACK HACK HACK HACK
// Disable Inline-Edit for now and just use a dialog.
// XXX - most of this is just copy-pasted from the other two folder
// creation functions. Yes it's ugly, but it'll do the trick for
// now as this is in no way intended to be a long-term solution.
const kPromptSvcContractID = "@mozilla.org/embedcomp/prompt-service;1";
const kPromptSvcIID = Components.interfaces.nsIPromptService;
const kPromptSvc = Components.classes[kPromptSvcContractID].getService(kPromptSvcIID);
var defaultValue = gBookmarksShell.getLocaleString("ile_newfolder");
var dialogTitle = gBookmarksShell.getLocaleString("newfolder_dialog_title");
var dialogMsg = gBookmarksShell.getLocaleString("newfolder_dialog_msg");
var stringValue = { value: defaultValue };
if (kPromptSvc.prompt(window, dialogTitle, dialogMsg, stringValue, null, { value: 0 })) {
var relativeNode = aSelectedItem || gBookmarksShell.element;
var parentNode = relativeNode ? gBookmarksShell.findRDFNode(relativeNode, false) : gBookmarksShell.element;
var args = [{ property: NC_NS + "parent",
resource: NODE_ID(parentNode) },
{ property: NC_NS + "Name",
literal: stringValue.value }];
const kBMDS = gBookmarksShell.RDF.GetDataSource("rdf:bookmarks");
var relId = relativeNode ? NODE_ID(relativeNode) : "NC:PersonalToolbarFolder";
BookmarksUtils.doBookmarksCommand(relId, NC_NS_CMD + "newfolder", args);
}
return;
// HACK HACK HACK HACK HACK
/////////////////////////////////////////////////////////////////////////
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var dummyButton = document.createElementNS(kXULNS, "menubutton");
dummyButton = gBookmarksShell.createBookmarkFolderDecorations(dummyButton);
dummyButton.setAttribute("class", "button-toolbar bookmark-item");
dummyButton.setAttribute("label", gBookmarksShell.getLocaleString("ile_newfolder") + " ");
// By default, create adjacent to the selected button. If there is no button after
// the selected button, or the target is the toolbar itself, just append.
var bIsButton = aSelectedItem.localName == "button" || aSelectedItem.localName == "menubutton";
if (aSelectedItem.nextSibling && bIsButton)
aSelectedItem.parentNode.insertBefore(dummyButton, aSelectedItem.nextSibling);
else
(bIsButton ? aSelectedItem.parentNode : aSelectedItem).appendChild(dummyButton);
gBookmarksShell._focusElt = document.commandDispatcher.focusedElement;
dummyButton.setMode("edit");
// |aSelectedItem| will be the node we create the new folder relative to.
dummyButton.addObserver(this.onEditFolderName, "accept",
[dummyButton, aSelectedItem, dummyButton]);
dummyButton.addObserver(this.onEditFolderName, "reject",
[dummyButton, aSelectedItem, dummyButton]);
},
///////////////////////////////////////////////////////////////////////////
// Edit folder name & update the datasource if name is valid
onEditFolderName: function (aParams, aTopic)
{
// Because the toolbar has no concept of selection, this function
// is much simpler than the one in bookmarksTree.js. However it may
// become more complex if pink ever lets me put context menus on menus ;)
var name = aParams[3];
var dummyButton = aParams[2];
var relativeNode = aParams[1];
var parentNode = gBookmarksShell.findRDFNode(relativeNode, false);
dummyButton.parentNode.removeChild(dummyButton);
if (!gBookmarksShell.commands.validateNameAndTopic(name, aTopic, relativeNode, dummyButton))
return;
parentNode = relativeNode.parentNode;
if (relativeNode.localName == "hbox") {
parentNode = relativeNode;
relativeNode = (gBookmarksShell.commands.nodeIsValidType(relativeNode) &&
relativeNode.lastChild) || relativeNode;
}
var args = [{ property: NC_NS + "parent",
resource: NODE_ID(parentNode) },
{ property: NC_NS + "Name",
literal: name }];
BookmarksUtils.doBookmarksCommand(NODE_ID(relativeNode),
NC_NS_CMD + "newfolder", args);
// We need to do this because somehow focus shifts and no commands
// operate any more.
//gBookmarksShell._focusElt.focus();
},
nodeIsValidType: function (aNode)
{
switch (aNode.localName) {
case "button":
case "menubutton":
// case "menu":
// case "menuitem":
return true;
}
return false;
},
///////////////////////////////////////////////////////////////////////////
// Performs simple validation on what the user has entered:
// 1) prevents entering an empty string
// 2) in the case of a canceled operation, remove the dummy item and
// restore selection.
validateNameAndTopic: function (aName, aTopic, aOldSelectedItem, aDummyItem)
{
// Don't allow user to enter an empty string "";
if (!aName) return false;
// If the user hit escape, go no further.
return !(aTopic == "reject");
}
},
_focusElt: null,
/////////////////////////////////////////////////////////////////////////////
// Evaluates an event to determine whether or not it affords opening a tree
// item. Typically, this is when the left mouse button is used, and provided
// the click-rate matches that specified by our owning tree class. For example,
// some trees open an item when double clicked (bookmarks/history windows) and
// others on a single click (sidebar panels).
isValidOpenEvent: function (aEvent)
{
return !(aEvent.type == "click" &&
(aEvent.button != 0 || aEvent.detail != this.openClickCount))
},
/////////////////////////////////////////////////////////////////////////////
// For the given selection, selects the best adjacent element. This method is
// useful when an action such as a cut or a deletion is performed on a
// selection, and focus/selection needs to be restored after the operation
// is performed.
getNextElement: function (aElement)
{
if (aElement.nextSibling)
return aElement.nextSibling;
else if (aElement.previousSibling)
return aElement.previousSibling;
else
return aElement.parentNode;
},
selectElement: function (aElement)
{
},
//////////////////////////////////////////////////////////////////////////////
// Add the treeitem element specified by aURI to the tree's current selection.
addItemToSelection: function (aURI)
{
},
/////////////////////////////////////////////////////////////////////////////
// Return a set of DOM nodes that represents the current item in the Bookmarks
// Toolbar. This is always |document.popupNode|.
getSelection: function ()
{
return [document.popupNode];
},
getBestItem: function ()
{
var seln = this.getSelection ();
if (seln.length < 1) {
var kids = ContentUtils.childByLocalName(this.tree, "treechildren");
if (kids) return kids.lastChild;
}
else
return seln[0];
return null;
},
/////////////////////////////////////////////////////////////////////////////
// Return a set of DOM nodes that represent the selection in the tree widget.
// This method is takes a node parameter which is the popupNode for the
// document. If the popupNode is not contained by the selection, the
// popupNode is selected and the new selection returned.
getContextSelection: function (aItemNode)
{
return [aItemNode];
},
getSelectedFolder: function ()
{
return "NC:PersonalToolbarFolder";
},
/////////////////////////////////////////////////////////////////////////////
// For a given start DOM element, find the enclosing DOM element that contains
// the template builder RDF resource decorations (id, ref, etc). In the
// Toolbar case, this is always the popup node (until we're proven wrong ;)
findRDFNode: function (aStartNode, aIncludeStartNodeFlag)
{
var temp = aStartNode;
while (temp && temp.localName != (aIncludeStartNodeFlag ? "toolbarbutton" : "hbox"))
temp = temp.parentNode;
return temp || this.element;
},
selectFolderItem: function (aFolderURI, aItemURI, aAdditiveFlag)
{
var folder = document.getElementById(aFolderURI);
var kids = ContentUtils.childByLocalName(folder, "treechildren");
if (!kids) return;
var item = kids.firstChild;
while (item) {
if (item.id == aItemURI) break;
item = item.nextSibling;
}
if (!item) return;
this.tree[aAdditiveFlag ? "addItemToSelection" : "selectItem"](item);
},
/////////////////////////////////////////////////////////////////////////////
// Command handling & Updating.
controller: {
supportsCommand: function (aCommand)
{
switch(aCommand) {
case "cmd_bm_undo":
case "cmd_bm_redo":
return false;
case "cmd_bm_cut":
case "cmd_bm_copy":
case "cmd_bm_paste":
case "cmd_bm_delete":
case "cmd_bm_selectAll":
case "cmd_bm_open":
case "cmd_bm_openfolder":
case "cmd_bm_openinnewwindow":
case "cmd_bm_newbookmark":
case "cmd_bm_newfolder":
case "cmd_bm_newseparator":
case "cmd_bm_find":
case "cmd_bm_properties":
case "cmd_bm_rename":
case "cmd_bm_setnewbookmarkfolder":
case "cmd_bm_setpersonaltoolbarfolder":
case "cmd_bm_setnewsearchfolder":
case "cmd_bm_import":
case "cmd_bm_export":
case "cmd_bm_fileBookmark":
return true;
default:
return false;
}
},
isCommandEnabled: function (aCommand)
{
switch(aCommand) {
case "cmd_bm_undo":
case "cmd_bm_redo":
return false;
case "cmd_bm_paste":
var cp = gBookmarksShell.canPaste();
return cp;
case "cmd_bm_cut":
case "cmd_bm_copy":
case "cmd_bm_delete":
return (document.popupNode != null) && (NODE_ID(document.popupNode) != "NC:PersonalToolbarFolder");
case "cmd_bm_selectAll":
return false;
case "cmd_bm_open":
var seln = gBookmarksShell.getSelection();
return document.popupNode != null && seln[0].getAttributeNS(RDF_NS, "type") == NC_NS + "Bookmark";
case "cmd_bm_openfolder":
seln = gBookmarksShell.getSelection();
return document.popupNode != null && seln[0].getAttributeNS(RDF_NS, "type") == NC_NS + "Folder";
case "cmd_bm_openinnewwindow":
return true;
case "cmd_bm_find":
case "cmd_bm_newbookmark":
case "cmd_bm_newfolder":
case "cmd_bm_newseparator":
case "cmd_bm_import":
case "cmd_bm_export":
return true;
case "cmd_bm_properties":
case "cmd_bm_rename":
return document.popupNode != null;
case "cmd_bm_setnewbookmarkfolder":
seln = gBookmarksShell.getSelection();
if (!seln.length) return false;
var folderType = seln[0].getAttributeNS(RDF_NS, "type") == (NC_NS + "Folder");
return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:NewBookmarkFolder") && folderType;
case "cmd_bm_setpersonaltoolbarfolder":
seln = gBookmarksShell.getSelection();
if (!seln.length) return false;
folderType = seln[0].getAttributeNS(RDF_NS, "type") == (NC_NS + "Folder");
return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:PersonalToolbarFolder") && folderType;
case "cmd_bm_setnewsearchfolder":
seln = gBookmarksShell.getSelection();
if (!seln.length) return false;
folderType = seln[0].getAttributeNS(RDF_NS, "type") == (NC_NS + "Folder");
return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:NewSearchFolder") && folderType;
case "cmd_bm_fileBookmark":
seln = gBookmarksShell.getSelection();
return seln.length > 0;
default:
return false;
}
},
doCommand: function (aCommand)
{
switch(aCommand) {
case "cmd_bm_undo":
case "cmd_bm_redo":
break;
case "cmd_bm_paste":
case "cmd_bm_copy":
case "cmd_bm_cut":
case "cmd_bm_delete":
case "cmd_bm_newbookmark":
case "cmd_bm_newfolder":
case "cmd_bm_newseparator":
case "cmd_bm_properties":
case "cmd_bm_rename":
case "cmd_bm_open":
case "cmd_bm_openfolder":
case "cmd_bm_openinnewwindow":
case "cmd_bm_setnewbookmarkfolder":
case "cmd_bm_setpersonaltoolbarfolder":
case "cmd_bm_setnewsearchfolder":
case "cmd_bm_find":
case "cmd_bm_import":
case "cmd_bm_export":
case "cmd_bm_fileBookmark":
gBookmarksShell.execCommand(aCommand.substring("cmd_".length));
break;
case "cmd_bm_selectAll":
break;
}
},
onEvent: function (aEvent)
{
},
onCommandUpdate: function ()
{
}
}
};
function BM_navigatorLoad(aEvent)
{
if (!gBookmarksShell) {
gBookmarksShell = new BookmarksToolbar("innermostBox");
controllers.appendController(gBookmarksShell.controller);
removeEventListener("load", BM_navigatorLoad, false);
}
}
// An interim workaround for 101131 - Bookmarks Toolbar button nonfunctional.
// This simply checks to see if the bookmark menu is empty (aside from static
// items) when it is opened and if it is, prompts a rebuild.
// The best fix for this is more time consuming, and relies on document
// <template>s without content (referencing a remote <template/> by id)
// be noted as 'waiting' for a template to load from somewhere. When the
// ::Merge function in nsXULDocument is called and a template node inserted,
// the id of the template to be inserted is looked up in the map of waiting
// references, and then the template builder hooked up.
function checkBookmarksMenuTemplateBuilder()
{
var lastStaticSeparator = document.getElementById("lastStaticSeparator");
if (!lastStaticSeparator.nextSibling) {
var button = document.getElementById("bookmarks-button");
button.builder.rebuild();
}
}
addEventListener("load", BM_navigatorLoad, false);