# -*- 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 (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, // 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 += "" + name + ""; } 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 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; } };