mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-04-08 08:01:26 +00:00
1003 lines
37 KiB
JavaScript
1003 lines
37 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 NC_NS = "http://home.netscape.com/NC-rdf#";
|
|
var RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
|
const NC_NS_CMD = NC_NS + "command?cmd=";
|
|
|
|
/**
|
|
* XXX - 04/16/01
|
|
* ACK! massive command name collision problems are causing big issues
|
|
* in getting this stuff to work in the Navigator window. For sanity's
|
|
* sake, we need to rename all the commands to be of the form cmd_bm_*
|
|
* otherwise there'll continue to be problems. For now, we're just
|
|
* renaming those that affect the personal toolbar (edit operations,
|
|
* which were clashing with the textfield controller)
|
|
*
|
|
* There are also several places that need to be updated if you need
|
|
* to change a command name.
|
|
* 1) the controller in ALL clients (bookmarksTree.js, personalToolbar.js)
|
|
* 2) the command nodes in the overlay
|
|
* 3) the command human-readable name key in bookmark.properties
|
|
* 4) the function 'getAllCmds' in bookmarksOverlay.js
|
|
* 5) the function 'execCommand' in bookmarksOverlay.js
|
|
* Yes, this blows crusty dead goats through straws, and I should probably
|
|
* create some constants somewhere to bring this number down to 3.
|
|
* However, if you fail to do one of these, you WILL break something
|
|
* and I WILL come after you with a knife.
|
|
*/
|
|
|
|
function LITERAL (aDB, aElement, aPropertyID)
|
|
{
|
|
var RDF = BookmarksUIElement.prototype.RDF;
|
|
var rSource = RDF.GetResource(aElement.id);
|
|
var rProperty = RDF.GetResource(aPropertyID);
|
|
var node = aDB.GetTarget(rSource, rProperty, true);
|
|
return node ? node.QueryInterface(Components.interfaces.nsIRDFLiteral).Value : "";
|
|
}
|
|
|
|
function BookmarksUIElement () { }
|
|
BookmarksUIElement.prototype = {
|
|
_rdf: null,
|
|
get RDF ()
|
|
{
|
|
if (!this._rdf) {
|
|
const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
|
|
const kRDFIID = Components.interfaces.nsIRDFService;
|
|
this._rdf = Components.classes[kRDFContractID].getService(kRDFIID);
|
|
}
|
|
return this._rdf;
|
|
},
|
|
|
|
propertySet: function (sourceID, propertyID, newValue)
|
|
{
|
|
if (!newValue) return;
|
|
const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
|
|
const kRDFIID = Components.interfaces.nsIRDFService;
|
|
const kRDF = Components.classes[kRDFContractID].getService(kRDFIID);
|
|
// need to shuffle this into an API.
|
|
const kBMDS = kRDF.GetDataSource("rdf:bookmarks");
|
|
const krProperty = kRDF.GetResource(propertyID);
|
|
const krItem = kRDF.GetResource(sourceID);
|
|
var rCurrValue = kBMDS.GetTarget(krItem, krProperty, true);
|
|
const krNewValue = kRDF.GetLiteral(newValue);
|
|
if (!rCurrValue)
|
|
kBMDS.Assert(krItem, krProperty, krNewValue, true);
|
|
else {
|
|
rCurrValue = rCurrValue.QueryInterface(Components.interfaces.nsIRDFLiteral);
|
|
if (rCurrValue.Value != newValue)
|
|
kBMDS.Change(krItem, krProperty, rCurrValue, krNewValue);
|
|
}
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Fill a context menu popup with menuitems that are appropriate for the current
|
|
// selection.
|
|
createContextMenu: function (aEvent)
|
|
{
|
|
var popup = aEvent.target;
|
|
// clear out the old context menu contents (if any)
|
|
while (popup.hasChildNodes())
|
|
popup.removeChild(popup.firstChild);
|
|
|
|
var popupNode = document.popupNode;
|
|
|
|
if (!("findRDFNode" in this))
|
|
throw "Clients must implement findRDFNode!";
|
|
var itemNode = this.findRDFNode(popupNode, true);
|
|
if (!itemNode || !itemNode.getAttributeNS(RDF_NS, "type") || itemNode.getAttribute("mode") == "edit") {
|
|
aEvent.preventDefault();
|
|
return;
|
|
}
|
|
if (!("getContextSelection" in this))
|
|
throw "Clients must implement getContextSelection!";
|
|
var selection = this.getContextSelection (itemNode);
|
|
var commonCommands = [];
|
|
for (var i = 0; i < selection.length; ++i) {
|
|
var commands = this.getAllCmds(selection[i].id);
|
|
if (!commands) {
|
|
aEvent.preventDefault();
|
|
return;
|
|
}
|
|
commands = this.flattenEnumerator(commands);
|
|
if (!commonCommands.length) commonCommands = commands;
|
|
commonCommands = this.findCommonNodes(commands, commonCommands);
|
|
}
|
|
|
|
if (!commonCommands.length) {
|
|
aEvent.preventDefault();
|
|
return;
|
|
}
|
|
|
|
// Now that we should have generated a list of commands that is valid
|
|
// for the entire selection, build a context menu.
|
|
for (i = 0; i < commonCommands.length; ++i) {
|
|
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
var currCommand = commonCommands[i].QueryInterface(Components.interfaces.nsIRDFResource).Value;
|
|
var element = null;
|
|
if (currCommand != NC_NS_CMD + "bm_separator") {
|
|
var commandName = this.getCommandName(currCommand);
|
|
element = this.createMenuItem(commandName, currCommand, itemNode);
|
|
}
|
|
else if (i != 0 && i < commonCommands.length-1) {
|
|
// Never append a separator as the first or last element in a context
|
|
// menu.
|
|
element = document.createElementNS(kXULNS, "menuseparator");
|
|
}
|
|
|
|
if (element)
|
|
popup.appendChild(element);
|
|
}
|
|
return;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Given two unique arrays, return an array that contains only the elements
|
|
// common to both.
|
|
findCommonNodes: function (aNewArray, aOldArray)
|
|
{
|
|
var common = [];
|
|
for (var i = 0; i < aNewArray.length; ++i) {
|
|
for (var j = 0; j < aOldArray.length; ++j) {
|
|
if (common.length > 0 && common[common.length-1] == aNewArray[i])
|
|
continue;
|
|
if (aNewArray[i] == aOldArray[j])
|
|
common.push(aNewArray[i]);
|
|
}
|
|
}
|
|
return common;
|
|
},
|
|
|
|
flattenEnumerator: function (aEnumerator)
|
|
{
|
|
if ("_index" in aEnumerator)
|
|
return aEnumerator._inner;
|
|
|
|
var temp = [];
|
|
while (aEnumerator.hasMoreElements())
|
|
temp.push(aEnumerator.getNext());
|
|
return temp;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// For a given URI (a unique identifier of a resource in the graph) return
|
|
// an enumeration of applicable commands for that URI.
|
|
getAllCmds: function (aNodeID)
|
|
{
|
|
var type = BookmarksUtils.resolveType(aNodeID);
|
|
if (!type) {
|
|
if (aNodeID == "NC:PersonalToolbarFolder" || aNodeID == "NC:BookmarksRoot")
|
|
type = "http://home.netscape.com/NC-rdf#Folder";
|
|
else
|
|
return null;
|
|
}
|
|
var commands = [];
|
|
// menu order:
|
|
//
|
|
// bm_open
|
|
// bm_openfolder
|
|
// bm_openinnewwindow
|
|
// /* bm_openinnewtab not yet supported */
|
|
// ---------------------
|
|
// /* bm_find removed */
|
|
// bm_newfolder
|
|
// ---------------------
|
|
// bm_cut
|
|
// bm_copy
|
|
// bm_paste
|
|
// bm_fileBookmark
|
|
// ---------------------
|
|
// bm_delete
|
|
// bm_rename
|
|
// ---------------------
|
|
// bm_properties
|
|
switch (type) {
|
|
case "http://home.netscape.com/NC-rdf#BookmarkSeparator":
|
|
commands = ["bm_newfolder", "bm_separator",
|
|
"bm_cut", "bm_copy", "bm_paste", "bm_separator",
|
|
"bm_delete"];
|
|
break;
|
|
case "http://home.netscape.com/NC-rdf#Bookmark":
|
|
commands = ["bm_open", "bm_openinnewwindow", /* "bm_openinnewtab", */ "bm_separator",
|
|
"bm_newfolder", "bm_separator",
|
|
"bm_cut", "bm_copy", "bm_paste", "bm_fileBookmark", "bm_separator",
|
|
"bm_delete", "bm_rename", "bm_separator",
|
|
"bm_properties"];
|
|
break;
|
|
case "http://home.netscape.com/NC-rdf#Folder":
|
|
commands = ["bm_openfolder", "bm_openinnewwindow", "bm_separator",
|
|
"bm_newfolder", "bm_separator",
|
|
"bm_cut", "bm_copy", "bm_paste", "bm_fileBookmark", "bm_separator",
|
|
"bm_delete", "bm_rename", "bm_separator",
|
|
"bm_properties"];
|
|
break;
|
|
case "http://home.netscape.com/NC-rdf#IEFavoriteFolder":
|
|
commands = ["bm_openfolder", "bm_separator",
|
|
"bm_delete"];
|
|
break;
|
|
case "http://home.netscape.com/NC-rdf#IEFavorite":
|
|
commands = ["bm_open", "bm_openinnewwindow", /* "bm_openinnewtab", */ "bm_separator",
|
|
"bm_copy"];
|
|
break;
|
|
case "http://home.netscape.com/NC-rdf#FileSystemObject":
|
|
commands = ["bm_open", "bm_openinnewwindow", /* "bm_openinnewtab", */ "bm_separator",
|
|
"bm_copy"];
|
|
break;
|
|
default:
|
|
var source = this.RDF.GetResource(aNodeID);
|
|
return this.db.GetAllCmds(source);
|
|
}
|
|
return new CommandArrayEnumerator(commands);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Retrieve the human-readable name for a particular command. Used when
|
|
// manufacturing a UI to invoke commands.
|
|
getCommandName: function (aCommand)
|
|
{
|
|
var cmdName = aCommand.substring(NC_NS_CMD.length);
|
|
try {
|
|
// Note: this will succeed only if there's a string in the bookmarks
|
|
// string bundle for this command name. Otherwise, <xul:stringbundle/>
|
|
// will throw, we'll catch & stifle the error, and look up the command
|
|
// name in the datasource.
|
|
return BookmarksUtils.getLocaleString ("cmd_" + cmdName);
|
|
}
|
|
catch (e) {
|
|
}
|
|
// XXX - WORK TO DO HERE! (rjc will cry if we don't fix this)
|
|
// need to ask the ds for the commands for this node, however we don't
|
|
// have the right params. This is kind of a problem.
|
|
dump("*** BAD! EVIL! WICKED! NO! ACK! ARGH! ORGH!\n");
|
|
const rName = this.RDF.GetResource(NC_NS + "Name");
|
|
const rSource = this.RDF.GetResource(aNodeID);
|
|
return this.db.GetTarget(rSource, rName, true).Value;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Perform a command based on a UI event. XXX - work to do here.
|
|
preExecCommand: function (aEvent)
|
|
{
|
|
var commandID = aEvent.target.getAttribute("cmd");
|
|
if (!commandID) return;
|
|
goDoCommand("cmd_" + commandID.substring(NC_NS_CMD.length));
|
|
},
|
|
|
|
execCommand: function (aCommandID)
|
|
{
|
|
var args = [];
|
|
var selection = this.getSelection ();
|
|
if (selection.length >= 1)
|
|
var selectedItem = selection[0];
|
|
switch (aCommandID) {
|
|
case "bm_open":
|
|
this.open(null, selectedItem, false);
|
|
break;
|
|
case "bm_openfolder":
|
|
this.commands.openFolder(selectedItem);
|
|
break;
|
|
case "bm_openinnewwindow":
|
|
if (BookmarksUtils.resolveType(selectedItem.id) == NC_NS + "Folder")
|
|
this.openFolderInNewWindow(selectedItem);
|
|
else
|
|
this.open(null, selectedItem, true);
|
|
break;
|
|
case "bm_rename":
|
|
// XXX - this is SO going to break if we ever do column re-ordering.
|
|
this.commands.editCell(selectedItem, 0);
|
|
break;
|
|
case "bm_editurl":
|
|
this.commands.editCell(selectedItem, 1);
|
|
break;
|
|
case "bm_setnewbookmarkfolder":
|
|
case "bm_setpersonaltoolbarfolder":
|
|
case "bm_setnewsearchfolder":
|
|
BookmarksUtils.doBookmarksCommand(selectedItem.id, NC_NS_CMD + aCommandID, args);
|
|
// XXX - The containing node seems to be closed here and the
|
|
// focus/selection is destroyed.
|
|
this.selectElement(selectedItem);
|
|
break;
|
|
case "bm_properties":
|
|
this.showPropertiesForNode(selectedItem);
|
|
break;
|
|
case "bm_find":
|
|
this.findInBookmarks();
|
|
break;
|
|
case "bm_cut":
|
|
this.copySelection(selection);
|
|
this.deleteSelection(selection);
|
|
break;
|
|
case "bm_copy":
|
|
this.copySelection(selection);
|
|
break;
|
|
case "bm_paste":
|
|
this.paste(selection);
|
|
break;
|
|
case "bm_delete":
|
|
this.deleteSelection(selection);
|
|
break;
|
|
case "bm_fileBookmark":
|
|
var rv = { selectedFolder: null };
|
|
openDialog("chrome://communicator/content/bookmarks/addBookmark.xul", "",
|
|
"centerscreen,chrome,modal=yes,dialog=yes,resizable=yes", null, null, folder, null, "selectFolder", rv);
|
|
if (rv.selectedFolder) {
|
|
for (var k = 0; k < selection.length; ++k) {
|
|
if (selection[k].id == rv.selectedFolder)
|
|
return; // Selection contains the target folder. Just fail silently.
|
|
}
|
|
var additiveFlag = false;
|
|
var selectedItems = [].concat(this.getSelection())
|
|
for (var i = 0; i < selectedItems.length; ++i) {
|
|
var currItem = selectedItems[i];
|
|
var currURI = currItem.id;
|
|
var parent = gBookmarksShell.findRDFNode(currItem, false);
|
|
gBookmarksShell.moveBookmark(currURI, parent.id, rv.selectedFolder);
|
|
gBookmarksShell.selectFolderItem(rv.selectedFolder, currURI, additiveFlag);
|
|
if (!additiveFlag) additiveFlag = true;
|
|
}
|
|
gBookmarksShell.flushDataSource();
|
|
}
|
|
break;
|
|
case "bm_newfolder":
|
|
var nfseln = document.popupNode;
|
|
this.commands.createBookmarkItem("folder", nfseln);
|
|
break;
|
|
case "bm_newbookmark":
|
|
var folder = this.getSelectedFolder();
|
|
openDialog("chrome://communicator/content/bookmarks/addBookmark.xul", "",
|
|
"centerscreen,chrome,modal=yes,dialog=yes,resizable=no", null, null, folder, null, "newBookmark");
|
|
break;
|
|
case "bm_newseparator":
|
|
nfseln = this.getBestItem();
|
|
var parentNode = this.findRDFNode(nfseln, false);
|
|
args = [{ property: NC_NS + "parent",
|
|
resource: parentNode.id }];
|
|
BookmarksUtils.doBookmarksCommand(nfseln.id, NC_NS_CMD + "newseparator", args);
|
|
break;
|
|
case "bm_import":
|
|
case "bm_export":
|
|
const isImport = aCommandID == "bm_import";
|
|
try {
|
|
const kFilePickerContractID = "@mozilla.org/filepicker;1";
|
|
const kFilePickerIID = Components.interfaces.nsIFilePicker;
|
|
const kFilePicker = Components.classes[kFilePickerContractID].createInstance(kFilePickerIID);
|
|
|
|
const kTitle = BookmarksUtils.getLocaleString(isImport ? "SelectImport": "EnterExport");
|
|
kFilePicker.init(window, kTitle, kFilePickerIID[isImport ? "modeOpen" : "modeSave"]);
|
|
kFilePicker.appendFilters(kFilePickerIID.filterHTML | kFilePickerIID.filterAll);
|
|
if (!isImport) kFilePicker.defaultString = "bookmarks.html";
|
|
if (kFilePicker.show() != kFilePickerIID.returnCancel) {
|
|
var fileName = kFilePicker.fileURL.spec;
|
|
if (!fileName) break;
|
|
}
|
|
else break;
|
|
}
|
|
catch (e) {
|
|
break;
|
|
}
|
|
var seln = this.getBestItem();
|
|
args = [{ property: NC_NS + "URL", literal: fileName}];
|
|
BookmarksUtils.doBookmarksCommand(seln.id, NC_NS_CMD + aCommandID, args);
|
|
break;
|
|
}
|
|
},
|
|
|
|
openFolderInNewWindow: function (aSelectedItem)
|
|
{
|
|
openDialog("chrome://communicator/content/bookmarks/bookmarks.xul",
|
|
"", "chrome,all,dialog=no", aSelectedItem.id);
|
|
},
|
|
|
|
copySelection: function (aSelection)
|
|
{
|
|
const kSuppArrayContractID = "@mozilla.org/supports-array;1";
|
|
const kSuppArrayIID = Components.interfaces.nsISupportsArray;
|
|
var itemArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
|
|
|
|
const kSuppWStringContractID = "@mozilla.org/supports-string;1";
|
|
const kSuppWStringIID = Components.interfaces.nsISupportsString;
|
|
var bmstring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
|
|
var unicodestring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
|
|
var htmlstring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
|
|
|
|
var sBookmarkItem = ""; var sTextUnicode = ""; var sTextHTML = "";
|
|
for (var i = 0; i < aSelection.length; ++i) {
|
|
var url = LITERAL(this.db, aSelection[i], NC_NS + "URL");
|
|
var name = LITERAL(this.db, aSelection[i], NC_NS + "Name");
|
|
sBookmarkItem += aSelection[i].id + "\n";
|
|
sTextUnicode += url + "\n";
|
|
sTextHTML += "<A HREF=\"" + url + "\">" + name + "</A>";
|
|
}
|
|
|
|
const kXferableContractID = "@mozilla.org/widget/transferable;1";
|
|
const kXferableIID = Components.interfaces.nsITransferable;
|
|
var xferable = Components.classes[kXferableContractID].createInstance(kXferableIID);
|
|
|
|
xferable.addDataFlavor("moz/bookmarkclipboarditem");
|
|
bmstring.data = sBookmarkItem;
|
|
xferable.setTransferData("moz/bookmarkclipboarditem", bmstring, sBookmarkItem.length*2)
|
|
|
|
xferable.addDataFlavor("text/html");
|
|
htmlstring.data = sTextHTML;
|
|
xferable.setTransferData("text/html", htmlstring, sTextHTML.length*2)
|
|
|
|
xferable.addDataFlavor("text/unicode");
|
|
unicodestring.data = sTextUnicode;
|
|
xferable.setTransferData("text/unicode", unicodestring, sTextUnicode.length*2)
|
|
|
|
const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
|
|
const kClipboardIID = Components.interfaces.nsIClipboard;
|
|
var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
|
|
clipboard.setData(xferable, null, kClipboardIID.kGlobalClipboard);
|
|
},
|
|
|
|
paste: function (aSelection)
|
|
{
|
|
const kXferableContractID = "@mozilla.org/widget/transferable;1";
|
|
const kXferableIID = Components.interfaces.nsITransferable;
|
|
var xferable = Components.classes[kXferableContractID].createInstance(kXferableIID);
|
|
xferable.addDataFlavor("moz/bookmarkclipboarditem");
|
|
xferable.addDataFlavor("text/x-moz-url");
|
|
xferable.addDataFlavor("text/unicode");
|
|
|
|
const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
|
|
const kClipboardIID = Components.interfaces.nsIClipboard;
|
|
var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
|
|
clipboard.getData(xferable, kClipboardIID.kGlobalClipboard);
|
|
|
|
var flavour = { };
|
|
var data = { };
|
|
var length = { };
|
|
xferable.getAnyTransferData(flavour, data, length);
|
|
var nodes = []; var names = [];
|
|
data = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
|
|
switch (flavour.value) {
|
|
case "moz/bookmarkclipboarditem":
|
|
nodes = data.split("\n");
|
|
break;
|
|
case "text/x-moz-url":
|
|
var ix = data.indexOf("\n");
|
|
nodes.push(data.substring(0, ix != -1 ? ix : data.length));
|
|
names.push(data.substring(ix));
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
const lastSelected = aSelection[aSelection.length-1];
|
|
const kParentNode = this.resolvePasteFolder(aSelection);
|
|
const krParent = this.RDF.GetResource(kParentNode.id);
|
|
const krSource = this.RDF.GetResource(lastSelected.id);
|
|
|
|
const kRDFCContractID = "@mozilla.org/rdf/container;1";
|
|
const kRDFCIID = Components.interfaces.nsIRDFContainer;
|
|
const ksRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
|
|
const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
|
|
var additiveFlag = false;
|
|
for (var i = 0; i < nodes.length; ++i) {
|
|
if (!nodes[i]) continue;
|
|
var rCurrent = this.RDF.GetResource(nodes[i]);
|
|
const krTypeProperty = this.RDF.GetResource(RDF_NS + "type");
|
|
var rType = this.db.GetTarget(rCurrent, krTypeProperty, true);
|
|
try {
|
|
rType = rType.QueryInterface(Components.interfaces.nsIRDFResource);
|
|
}
|
|
catch (e) {
|
|
try {
|
|
rType = rType.QueryInterface(Components.interfaces.nsIRDFLiteral);
|
|
}
|
|
catch (e) {
|
|
// OK, no type exists, so node does not exist in the graph.
|
|
// (e.g. user pastes url as text)
|
|
// Do some housekeeping.
|
|
const krName = this.RDF.GetResource(names[i]);
|
|
const krNameProperty = this.RDF.GetResource(NC_NS + "Name");
|
|
const krBookmark = this.RDF.GetResource(NC_NS + "Bookmark");
|
|
kBMDS.Assert(rCurrent, krNameProperty, krName, true);
|
|
kBMDS.Assert(rCurrent, krTypeProperty, krBookmark, true);
|
|
}
|
|
}
|
|
|
|
// If the node is a folder, then we need to create a new anonymous
|
|
// resource and copy all the arcs over.
|
|
if (rType && rType.Value == NC_NS + "Folder")
|
|
rCurrent = BookmarksUtils.cloneFolder(rCurrent, krParent, krSource);
|
|
|
|
// If this item already exists in this container, don't paste, as
|
|
// this will result in the creation of multiple copies in the datasource
|
|
// but will not result in an update of the UI. (In Short: we don't
|
|
// handle multiple bookmarks well)
|
|
ksRDFC.Init(kBMDS, krParent);
|
|
ix = ksRDFC.IndexOf(rCurrent);
|
|
if (ix != -1)
|
|
continue;
|
|
|
|
ix = ksRDFC.IndexOf(krSource);
|
|
if (ix != -1)
|
|
ksRDFC.InsertElementAt(rCurrent, ix+1, true);
|
|
else
|
|
ksRDFC.AppendElement(rCurrent);
|
|
this.selectFolderItem(krSource.Value, rCurrent.Value, additiveFlag);
|
|
if (!additiveFlag) additiveFlag = true;
|
|
|
|
var rds = kBMDS.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
|
|
rds.Flush();
|
|
}
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// For the given selection, determines the element that should form the
|
|
// container to paste items into.
|
|
resolvePasteFolder: function (aSelection)
|
|
{
|
|
const lastSelected = aSelection[aSelection.length-1];
|
|
if (lastSelected.getAttribute("container") == "true" &&
|
|
aSelection.length == 1)
|
|
return lastSelected;
|
|
return this.findRDFNode(lastSelected, false);
|
|
},
|
|
|
|
canPaste: function ()
|
|
{
|
|
const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
|
|
const kClipboardIID = Components.interfaces.nsIClipboard;
|
|
var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
|
|
const kSuppArrayContractID = "@mozilla.org/supports-array;1";
|
|
const kSuppArrayIID = Components.interfaces.nsISupportsArray;
|
|
var flavourArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
|
|
const kSuppStringContractID = "@mozilla.org/supports-cstring;1";
|
|
const kSuppStringIID = Components.interfaces.nsISupportsCString;
|
|
|
|
var flavours = ["moz/bookmarkclipboarditem", "text/x-moz-url"];
|
|
for (var i = 0; i < flavours.length; ++i) {
|
|
const kSuppString = Components.classes[kSuppStringContractID].createInstance(kSuppStringIID);
|
|
kSuppString.data = flavours[i];
|
|
flavourArray.AppendElement(kSuppString);
|
|
}
|
|
var hasFlavours = clipboard.hasDataMatchingFlavors(flavourArray, kClipboardIID.kGlobalClipboard);
|
|
return hasFlavours;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// aSelection is a mutable array, not a NodeList.
|
|
deleteSelection: function (aSelection)
|
|
{
|
|
const kRDFCContractID = "@mozilla.org/rdf/container;1";
|
|
const kRDFCIID = Components.interfaces.nsIRDFContainer;
|
|
const ksRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
|
|
|
|
var nextElement;
|
|
var count = 0;
|
|
|
|
var selectionLength = aSelection.length;
|
|
while (aSelection.length && aSelection[count]) {
|
|
const currParent = this.findRDFNode(aSelection[count], false);
|
|
const kSelectionURI = aSelection[count].id;
|
|
|
|
// Disallow the removal of certain 'special' nodes
|
|
if (kSelectionURI == "NC:BookmarksRoot") {
|
|
aSelection.splice(count++,1);
|
|
continue;
|
|
}
|
|
|
|
// If the current bookmark is the IE Favorites folder, we have a little
|
|
// extra work to do - set the pref |browser.bookmarks.import_system_favorites|
|
|
// to ensure that we don't re-import next time.
|
|
if (aSelection[count].getAttribute("type") == (NC_NS + "IEFavoriteFolder")) {
|
|
const kPrefSvcContractID = "@mozilla.org/preferences-service;1";
|
|
const kPrefSvcIID = Components.interfaces.nsIPrefBranch;
|
|
const kPrefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
|
|
kPrefSvc.setBoolPref("browser.bookmarks.import_system_favorites", false);
|
|
}
|
|
|
|
const krParent = this.RDF.GetResource(currParent.id);
|
|
const krBookmark = this.RDF.GetResource(kSelectionURI);
|
|
const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
|
|
ksRDFC.Init(kBMDS, krParent);
|
|
nextElement = this.getNextElement(aSelection[count]);
|
|
ksRDFC.RemoveElement(krBookmark, true);
|
|
|
|
try {
|
|
// XXX - UGH. Template builder is NOT removing the element from the
|
|
// tree, and so selection remains non-zero in length and we go into
|
|
// an infinite loop here. Tear the node out of the document.
|
|
var parent = aSelection[count].parentNode;
|
|
parent.removeChild(aSelection[count]);
|
|
}
|
|
catch (e) {
|
|
}
|
|
// Manipulate the selection array ourselves.
|
|
aSelection.splice(count,1);
|
|
}
|
|
this.selectElement(nextElement);
|
|
},
|
|
|
|
moveBookmark: function (aBookmarkURI, aFromFolderURI, aToFolderURI)
|
|
{
|
|
const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
const kRDFCContractID = "@mozilla.org/rdf/container;1";
|
|
const kRDFCIID = Components.interfaces.nsIRDFContainer;
|
|
const kRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
|
|
const krSrc = this.RDF.GetResource(aBookmarkURI);
|
|
const krOldParent = this.RDF.GetResource(aFromFolderURI);
|
|
const krNewParent = this.RDF.GetResource(aToFolderURI);
|
|
kRDFC.Init(kBMDS, krNewParent);
|
|
kRDFC.AppendElement(krSrc);
|
|
kRDFC.Init(kBMDS, krOldParent);
|
|
kRDFC.RemoveElement(krSrc, true);
|
|
},
|
|
|
|
open: function (aEvent, aRDFNode, aInNewWindow)
|
|
{
|
|
var urlValue = LITERAL(this.db, aRDFNode, NC_NS + "URL");
|
|
|
|
// Ignore "NC:" and empty urls.
|
|
if (urlValue.substring(0,3) == "NC:" || !urlValue) return;
|
|
|
|
if (aEvent && aEvent.altKey)
|
|
this.showPropertiesForNode (aRDFNode);
|
|
else if (aInNewWindow)
|
|
openDialog (getBrowserURL(), "_blank", "chrome,all,dialog=no", urlValue);
|
|
else
|
|
openTopWin (urlValue);
|
|
if (aEvent)
|
|
aEvent.preventBubble();
|
|
},
|
|
|
|
showPropertiesForNode: function (aBookmarkItem)
|
|
{
|
|
if (aBookmarkItem.getAttribute("type") != NC_NS + "BookmarkSeparator")
|
|
openDialog("chrome://communicator/content/bookmarks/bm-props.xul",
|
|
"", "centerscreen,chrome,resizable=no", aBookmarkItem.id);
|
|
},
|
|
|
|
findInBookmarks: function ()
|
|
{
|
|
openDialog("chrome://communicator/content/bookmarks/findBookmark.xul",
|
|
"FindBookmarksWindow",
|
|
"centerscreen,resizable=no,chrome,dependent");
|
|
},
|
|
|
|
flushDataSource: function ()
|
|
{
|
|
const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
var remoteDS = kBMDS.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
|
|
remoteDS.Flush();
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// takes a node and adds the appropriate adornments for a bookmark container.
|
|
createBookmarkFolderDecorations: function (aNode)
|
|
{
|
|
aNode.setAttribute("type", "http://home.netscape.com/NC-rdf#Folder");
|
|
aNode.setAttribute("container", "true");
|
|
return aNode;
|
|
}
|
|
};
|
|
|
|
function CommandArrayEnumerator (aCommandArray)
|
|
{
|
|
this._inner = [];
|
|
const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
|
|
const kRDFIID = Components.interfaces.nsIRDFService;
|
|
const RDF = Components.classes[kRDFContractID].getService(kRDFIID);
|
|
for (var i = 0; i < aCommandArray.length; ++i)
|
|
this._inner.push(RDF.GetResource(NC_NS_CMD + aCommandArray[i]));
|
|
|
|
this._index = 0;
|
|
}
|
|
|
|
CommandArrayEnumerator.prototype = {
|
|
getNext: function ()
|
|
{
|
|
return this._inner[this._index];
|
|
},
|
|
|
|
hasMoreElements: function ()
|
|
{
|
|
return this._index < this._inner.length;
|
|
}
|
|
};
|
|
|
|
var BookmarksUtils = {
|
|
|
|
_rdf: null,
|
|
get RDF ()
|
|
{
|
|
if (!this._rdf) {
|
|
const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
|
|
const kRDFIID = Components.interfaces.nsIRDFService;
|
|
this._rdf = Components.classes[kRDFContractID].getService(kRDFIID);
|
|
}
|
|
return this._rdf;
|
|
},
|
|
|
|
_bmds: null,
|
|
get BMDS ()
|
|
{
|
|
if (!this._bmds) {
|
|
this._bmds = this.RDF.GetDataSource("rdf:bookmarks");
|
|
}
|
|
return this._bmds;
|
|
},
|
|
|
|
_bundle : null,
|
|
_brandShortName: null,
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// returns a property from chrome://communicator/locale/bookmarks/bookmark.properties
|
|
getLocaleString: function (aStringKey)
|
|
{
|
|
if (!this._bundle) {
|
|
this._bundle = document.getElementById("bundle_bookmarks");
|
|
this._brandShortName = document.getElementById("bundle_brand")
|
|
.getString("brandShortName");
|
|
}
|
|
bundle = this._bundle.getString(aStringKey)
|
|
.replace(/%brandShortName%/, this._brandShortName);
|
|
return bundle;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Determine the rdf:type property for the given resource.
|
|
resolveType: function (aResource)
|
|
{
|
|
var rElement;
|
|
if (typeof(aResource) == "string")
|
|
rElement = this.RDF.GetResource(aResource);
|
|
else
|
|
rElement = aResource;
|
|
|
|
const typeArc = this.RDF.GetResource(RDF_NS + "type");
|
|
const type = this.BMDS.GetTarget(rElement, typeArc, true);
|
|
try {
|
|
return type.QueryInterface(Components.interfaces.nsIRDFResource).Value;
|
|
}
|
|
catch (e) {
|
|
try {
|
|
return type.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
|
|
}
|
|
catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Execute a command with the given source and arguments
|
|
doBookmarksCommand: function (aSourceURI, aCommand, aArgumentsArray)
|
|
{
|
|
var rCommand = this.RDF.GetResource(aCommand);
|
|
|
|
var kSuppArrayContractID = "@mozilla.org/supports-array;1";
|
|
var kSuppArrayIID = Components.interfaces.nsISupportsArray;
|
|
var sourcesArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
|
|
if (aSourceURI) {
|
|
var rSource = this.RDF.GetResource(aSourceURI);
|
|
sourcesArray.AppendElement (rSource);
|
|
}
|
|
|
|
var argsArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
|
|
for (var i = 0; i < aArgumentsArray.length; ++i) {
|
|
var rArc = this.RDF.GetResource(aArgumentsArray[i].property);
|
|
argsArray.AppendElement(rArc);
|
|
var rValue = null;
|
|
if ("resource" in aArgumentsArray[i]) {
|
|
rValue = this.RDF.GetResource(aArgumentsArray[i].resource);
|
|
}
|
|
else
|
|
rValue = this.RDF.GetLiteral(aArgumentsArray[i].literal);
|
|
argsArray.AppendElement(rValue);
|
|
}
|
|
|
|
// Exec the command in the Bookmarks datasource.
|
|
const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
kBMDS.DoCommand(sourcesArray, rCommand, argsArray);
|
|
},
|
|
|
|
cloneFolder: function (aFolder, aParent, aRelativeItem)
|
|
{
|
|
var BMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
|
|
var nameArc = this.RDF.GetResource(NC_NS + "Name");
|
|
var rName = BMDS.GetTarget(aFolder, nameArc, true);
|
|
rName = rName.QueryInterface(Components.interfaces.nsIRDFLiteral);
|
|
|
|
var newFolder = this.createFolderWithID(rName.Value, aRelativeItem, aParent);
|
|
|
|
// Now need to append kiddies.
|
|
try {
|
|
const kRDFCContractID = "@mozilla.org/rdf/container;1";
|
|
const kRDFCIID = Components.interfaces.nsIRDFContainer;
|
|
var RDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
|
|
const kRDFCUContractID = "@mozilla.org/rdf/container-utils;1";
|
|
const kRDFCUIID = Components.interfaces.nsIRDFContainerUtils;
|
|
var RDFCU = Components.classes[kRDFCUContractID].getService(kRDFCUIID);
|
|
|
|
RDFC.Init(BMDS, aFolder);
|
|
var elts = RDFC.GetElements();
|
|
RDFC.Init(BMDS, newFolder);
|
|
|
|
while (elts.hasMoreElements()) {
|
|
var curr = elts.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
|
|
if (RDFCU.IsContainer(BMDS, curr))
|
|
BookmarksUtils.cloneFolder(curr, newFolder);
|
|
else
|
|
RDFC.AppendElement(curr);
|
|
}
|
|
}
|
|
catch (e) {
|
|
}
|
|
return newFolder;
|
|
},
|
|
|
|
createFolderWithID: function (aTitle, aRelativeItem, aParentFolder)
|
|
{
|
|
const kRDFCContractID = "@mozilla.org/rdf/container;1";
|
|
const kRDFCIID = Components.interfaces.nsIRDFContainer;
|
|
var RDFC = Components.classes[kRDFCContractID].createInstance(kRDFCIID);
|
|
var BMDS = this.RDF.GetDataSource("rdf:bookmarks");
|
|
try {
|
|
RDFC.Init(BMDS, aParentFolder);
|
|
}
|
|
catch (e) {
|
|
return null;
|
|
}
|
|
|
|
var ix = RDFC.IndexOf(aRelativeItem);
|
|
var BMSvc = BMDS.QueryInterface(Components.interfaces.nsIBookmarksService);
|
|
return BMSvc.createFolderWithDetails(aTitle, aParentFolder, ix);
|
|
},
|
|
|
|
addBookmarkForTabBrowser: function( aTabBrowser, aSelect )
|
|
{
|
|
var tabsInfo = [];
|
|
var currentTabInfo = { name: "", url: "", charset: null };
|
|
|
|
const activeBrowser = aTabBrowser.selectedBrowser;
|
|
const browsers = aTabBrowser.browsers;
|
|
for (var i = 0; i < browsers.length; ++i) {
|
|
var webNav = browsers[i].webNavigation;
|
|
var url = webNav.currentURI.spec;
|
|
var name = "";
|
|
var charset;
|
|
try {
|
|
var doc = webNav.document;
|
|
name = doc.title || url;
|
|
charset = doc.characterSet;
|
|
} catch (e) {
|
|
name = url;
|
|
}
|
|
|
|
tabsInfo[i] = { name: name, url: url, charset: charset };
|
|
|
|
if (browsers[i] == activeBrowser)
|
|
currentTabInfo = tabsInfo[i];
|
|
}
|
|
|
|
openDialog("chrome://communicator/content/bookmarks/addBookmark.xul", "",
|
|
"centerscreen,chrome,dialog=yes,resizable,dependent",
|
|
currentTabInfo.name, currentTabInfo.url, null,
|
|
currentTabInfo.charset, "addGroup" + (aSelect ? ",group" : ""), tabsInfo);
|
|
},
|
|
|
|
addBookmarkForBrowser: function (aDocShell, aShowDialog)
|
|
{
|
|
// Bug 52536: We obtain the URL and title from the nsIWebNavigation
|
|
// associated with a <browser/> rather than from a DOMWindow.
|
|
// This is because when a full page plugin is loaded, there is
|
|
// no DOMWindow (?) but information about the loaded document
|
|
// may still be obtained from the webNavigation.
|
|
var url = aDocShell.currentURI.spec;
|
|
var title, docCharset = null;
|
|
try {
|
|
title = aDocShell.document.title || url;
|
|
docCharset = aDocShell.document.characterSet;
|
|
}
|
|
catch (e) {
|
|
title = url;
|
|
}
|
|
|
|
this.addBookmark(url, title, docCharset, aShowDialog);
|
|
},
|
|
|
|
addBookmark: function (aURL, aTitle, aCharset, aShowDialog)
|
|
{
|
|
if (aCharset === undefined) {
|
|
var fw = document.commandDispatcher.focusedWindow;
|
|
aCharset = fw.document.characterSet;
|
|
}
|
|
|
|
if (aShowDialog)
|
|
openDialog("chrome://communicator/content/bookmarks/addBookmark.xul", "",
|
|
"centerscreen,chrome,dialog=yes,resizable,dependent", aTitle, aURL, null, aCharset);
|
|
else {
|
|
// User has elected to override the file dialog and always file bookmarks
|
|
// into the default bookmark folder.
|
|
const kBMSvcContractID = "@mozilla.org/browser/bookmarks-service;1";
|
|
const kBMSvcIID = Components.interfaces.nsIBookmarksService;
|
|
const kBMSvc = Components.classes[kBMSvcContractID].getService(kBMSvcIID);
|
|
kBMSvc.addBookmarkImmediately(aURL, aTitle, kBMSvcIID.BOOKMARK_DEFAULT_TYPE, aCharset);
|
|
}
|
|
},
|
|
|
|
getSpecialFolder: function (aID)
|
|
{
|
|
var sources = this.BMDS.GetSources(this.RDF.GetResource("http://home.netscape.com/NC-rdf#FolderType"),
|
|
this.RDF.GetResource(aID), true);
|
|
var folder = null;
|
|
if (sources.hasMoreElements())
|
|
folder = sources.getNext();
|
|
else
|
|
folder = this.RDF.GetResource("NC:BookmarksRoot");
|
|
return folder;
|
|
},
|
|
|
|
getNewBookmarkFolder: function()
|
|
{
|
|
return this.getSpecialFolder("NC:NewBookmarkFolder");
|
|
},
|
|
|
|
getNewSearchFolder: function()
|
|
{
|
|
return this.getSpecialFolder("NC:NewSearchFolder");
|
|
}
|
|
|
|
}
|
|
|
|
var ContentUtils = {
|
|
childByLocalName: function (aSelectedItem, aLocalName)
|
|
{
|
|
var temp = aSelectedItem.firstChild;
|
|
while (temp) {
|
|
if (temp.localName == aLocalName)
|
|
return temp;
|
|
temp = temp.nextSibling;
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|