home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 May / PCWorld_2003-05_cd.bin / Komunik / phoenix / chrome / browser.jar / content / browser / bookmarks / bookmarks.js < prev    next >
Text File  |  2002-10-31  |  66KB  |  1,935 lines

  1.  
  2. var NC_NS, RDF_NS, XUL_NS, NC_NS_CMD;
  3.  
  4. // definition of the services frequently used for bookmarks
  5. var kRDFContractID;
  6. var kRDFSVCIID;
  7. var kRDFRSCIID;
  8. var kRDFLITIID;
  9. var RDF;
  10.  
  11. var kRDFCContractID;
  12. var kRDFCIID;
  13. var RDFC;
  14.  
  15. var kRDFCUContractID;
  16. var kRDFCUIID;
  17. var RDFCU;
  18.  
  19. var BMDS;
  20. var BMSVC;
  21.  
  22. var kPREFContractID;
  23. var kPREFIID;
  24. var PREF;
  25.  
  26. var kSOUNDContractID;
  27. var kSOUNDIID;
  28. var SOUND;
  29.  
  30. var kWINDOWContractID;
  31. var kWINDOWIID;
  32. var WINDOWSVC;
  33.  
  34. var gBMtxmgr;
  35.  
  36. // should be moved in a separate file
  37. function initServices()
  38. {
  39.   NC_NS     = "http://home.netscape.com/NC-rdf#";
  40.   RDF_NS    = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  41.   XUL_NS    = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  42.   NC_NS_CMD = NC_NS + "command?cmd=";
  43.  
  44.   kRDFContractID   = "@mozilla.org/rdf/rdf-service;1";
  45.   kRDFSVCIID       = Components.interfaces.nsIRDFService;
  46.   kRDFRSCIID       = Components.interfaces.nsIRDFResource;
  47.   kRDFLITIID       = Components.interfaces.nsIRDFLiteral;
  48.   RDF              = Components.classes[kRDFContractID].getService(kRDFSVCIID);
  49.  
  50.   kRDFCContractID  = "@mozilla.org/rdf/container;1";
  51.   kRDFCIID         = Components.interfaces.nsIRDFContainer;
  52.   RDFC             = Components.classes[kRDFCContractID].getService(kRDFCIID);
  53.  
  54.   kRDFCUContractID = "@mozilla.org/rdf/container-utils;1";
  55.   kRDFCUIID        = Components.interfaces.nsIRDFContainerUtils;
  56.   RDFCU            = Components.classes[kRDFCUContractID].getService(kRDFCUIID);
  57.  
  58.   kPREFContractID  = "@mozilla.org/preferences-service;1";
  59.   kPREFIID         = Components.interfaces.nsIPrefService;
  60.   PREF             = Components.classes[kPREFContractID].getService(kPREFIID)
  61.                                .getBranch(null)
  62.  
  63.   kSOUNDContractID = "@mozilla.org/sound;1";
  64.   kSOUNDIID        = Components.interfaces.nsISound;
  65.   SOUND            = Components.classes[kSOUNDContractID].createInstance(kSOUNDIID);
  66.  
  67.   kWINDOWContractID = "@mozilla.org/appshell/window-mediator;1";
  68.   kWINDOWIID        = Components.interfaces.nsIWindowMediator;
  69.   WINDOWSVC         = Components.classes[kWINDOWContractID].getService(kWINDOWIID);
  70.  
  71. }
  72.  
  73. function initBMService()
  74. {
  75.   BMDS  = RDF.GetDataSource("rdf:bookmarks");
  76.   BMSVC = BMDS.QueryInterface(Components.interfaces.nsIBookmarksService);
  77.   BookmarkInsertTransaction.prototype.RDFC = RDFC;
  78.   BookmarkInsertTransaction.prototype.BMDS = BMDS;
  79.   BookmarkRemoveTransaction.prototype.RDFC = RDFC;
  80.   BookmarkRemoveTransaction.prototype.BMDS = BMDS;
  81.   BookmarkImportTransaction.prototype.RDFC = RDFC;
  82.   BookmarkImportTransaction.prototype.BMDS = BMDS;
  83. }
  84.  
  85. /**
  86.  * XXX - 04/16/01
  87.  *  ACK! massive command name collision problems are causing big issues
  88.  *  in getting this stuff to work in the Navigator window. For sanity's 
  89.  *  sake, we need to rename all the commands to be of the form cmd_bm_*
  90.  *  otherwise there'll continue to be problems. For now, we're just 
  91.  *  renaming those that affect the personal toolbar (edit operations,
  92.  *  which were clashing with the textfield controller)
  93.  *
  94.  * There are also several places that need to be updated if you need
  95.  * to change a command name. 
  96.  *   1) the controller...
  97.  *      - in bookmarksTree.xml if the command is tree-specifc
  98.  *      - in bookmarksToolbar.xml if the command is DOM-specific
  99.  *      - in bookmarks.js otherwise
  100.  *   2) the command nodes in the overlay or xul file
  101.  *   3) the command human-readable name key in bookmarks.properties
  102.  *   4) the function 'getCommands' in bookmarks.js
  103.  */
  104.  
  105. var BookmarksCommand = {
  106.  
  107.   /////////////////////////////////////////////////////////////////////////////
  108.   // This method constructs a menuitem for a context menu for the given command.
  109.   // This is implemented by the client so that it can intercept menuitem naming
  110.   // as appropriate.
  111.   createMenuItem: function (aDisplayName, aCommandName, aSelection)
  112.   {
  113.     var xulElement = document.createElementNS(XUL_NS, "menuitem");
  114.     xulElement.setAttribute("cmd", aCommandName);
  115.     var cmd = "cmd_" + aCommandName.substring(NC_NS_CMD.length)
  116.     xulElement.setAttribute("command", cmd);
  117.     
  118.     switch (aCommandName) {
  119.     case NC_NS_CMD + "bm_expandfolder":
  120.       var shouldCollapse = true;
  121.       for (var i=0; i<aSelection.length; ++i)
  122.         if (!aSelection.isExpanded[i])
  123.           shouldCollapse = false;
  124.       aDisplayName =  shouldCollapse? BookmarksUtils.getLocaleString("cmd_bm_collapsefolder") : aDisplayName;
  125.     break;
  126.     }
  127.     xulElement.setAttribute("label", aDisplayName);
  128.     return xulElement;
  129.   },
  130.  
  131.   /////////////////////////////////////////////////////////////////////////////
  132.   // Fill a context menu popup with menuitems that are appropriate for the current
  133.   // selection.
  134.   createContextMenu: function (aEvent, aSelection)
  135.   {
  136.     var popup = aEvent.target;
  137.     // clear out the old context menu contents (if any)
  138.     while (popup.hasChildNodes()) 
  139.       popup.removeChild(popup.firstChild);
  140.         
  141.     var commonCommands = [];
  142.     for (var i = 0; i < aSelection.length; ++i) {
  143.       var commands = this.getCommands(aSelection.item[i]);
  144.       if (!commands) {
  145.         aEvent.preventDefault();
  146.         return;
  147.       }
  148.       commands = this.flattenEnumerator(commands);
  149.       if (!commonCommands.length) commonCommands = commands;
  150.       commonCommands = this.findCommonNodes(commands, commonCommands);
  151.     }
  152.  
  153.     if (!commonCommands.length) {
  154.       aEvent.preventDefault();
  155.       return;
  156.     }
  157.     
  158.     // Now that we should have generated a list of commands that is valid
  159.     // for the entire selection, build a context menu.
  160.     for (i = 0; i < commonCommands.length; ++i) {
  161.       var currCommand = commonCommands[i].QueryInterface(kRDFRSCIID).Value;
  162.       var element = null;
  163.       if (currCommand != NC_NS_CMD + "bm_separator") {
  164.         var commandName = this.getCommandName(currCommand);
  165.         element = this.createMenuItem(commandName, currCommand, aSelection);
  166.       }
  167.       else if (i != 0 && i < commonCommands.length-1) {
  168.         // Never append a separator as the first or last element in a context
  169.         // menu.
  170.         element = document.createElementNS(XUL_NS, "menuseparator");
  171.       }
  172.       if (element) 
  173.         popup.appendChild(element);
  174.     }
  175.     switch (popup.firstChild.getAttribute("command")) {
  176.     case "cmd_bm_open":
  177.     case "cmd_bm_expandfolder":
  178.       popup.firstChild.setAttribute("default", "true");
  179.     }
  180.   },
  181.   
  182.   /////////////////////////////////////////////////////////////////////////////
  183.   // Given two unique arrays, return an array that contains only the elements
  184.   // common to both. 
  185.   findCommonNodes: function (aNewArray, aOldArray)
  186.   {
  187.     var common = [];
  188.     for (var i = 0; i < aNewArray.length; ++i) {
  189.       for (var j = 0; j < aOldArray.length; ++j) {
  190.         if (common.length > 0 && common[common.length-1] == aNewArray[i])
  191.           continue;
  192.         if (aNewArray[i] == aOldArray[j])
  193.           common.push(aNewArray[i]);
  194.       }
  195.     }
  196.     return common;
  197.   },
  198.  
  199.   flattenEnumerator: function (aEnumerator)
  200.   {
  201.     if ("_index" in aEnumerator)
  202.       return aEnumerator._inner;
  203.     
  204.     var temp = [];
  205.     while (aEnumerator.hasMoreElements()) 
  206.       temp.push(aEnumerator.getNext());
  207.     return temp;
  208.   },
  209.   
  210.   /////////////////////////////////////////////////////////////////////////////
  211.   // For a given URI (a unique identifier of a resource in the graph) return 
  212.   // an enumeration of applicable commands for that URI. 
  213.   getCommands: function (aNodeID)
  214.   {
  215.     var type = BookmarksUtils.resolveType(aNodeID);
  216.     if (!type)
  217.       return null;
  218.     var commands = [];
  219.     // menu order:
  220.     // 
  221.     // bm_expandfolder
  222.     // bm_open, bm_openfolder
  223.     // bm_openinnewwindow
  224.     // bm_openinnewtab
  225.     // ---------------------
  226.     // bm_newfolder
  227.     // ---------------------
  228.     // bm_cut
  229.     // bm_copy
  230.     // bm_paste
  231.     // ---------------------
  232.     // bm_delete
  233.     // ---------------------
  234.     // bm_properties
  235.     switch (type) {
  236.     case "BookmarkSeparator":
  237.       commands = ["bm_newfolder", "bm_separator", 
  238.                   "bm_cut", "bm_copy", "bm_paste", "bm_separator",
  239.                   "bm_delete"];
  240.       break;
  241.     case "Bookmark":
  242.       commands = ["bm_open", "bm_openinnewwindow", "bm_openinnewtab", "bm_separator",
  243.                   "bm_newfolder", "bm_separator",
  244.                   "bm_cut", "bm_copy", "bm_paste", "bm_separator",
  245.                   "bm_delete", "bm_separator",
  246.                   "bm_properties"];
  247.       break;
  248.     case "Folder":
  249.       commands = ["bm_expandfolder", "bm_openfolder", "bm_managefolder", "bm_separator", 
  250.                   "bm_newfolder", "bm_separator",
  251.                   "bm_cut", "bm_copy", "bm_paste", "bm_separator",
  252.                   "bm_delete", "bm_separator",
  253.                   "bm_properties"];
  254.       break;
  255.     case "FolderGroup":
  256.       commands = ["bm_openfolder", "bm_expandfolder", "bm_separator",
  257.                   "bm_newfolder", "bm_separator",
  258.                   "bm_cut", "bm_copy", "bm_paste", "bm_separator",
  259.                   "bm_delete", "bm_separator",
  260.                   "bm_properties"];
  261.       break;
  262.     case "PersonalToolbarFolder":
  263.       commands = ["bm_newfolder", "bm_separator",
  264.                   "bm_cut", "bm_copy", "bm_paste", "bm_separator",
  265.                   "bm_delete", "bm_separator",
  266.                   "bm_properties"];
  267.       break;
  268.     case "IEFavoriteFolder":
  269.       commands = ["bm_expandfolder", "bm_separator", "bm_delete"];
  270.       break;
  271.     case "IEFavorite":
  272.       commands = ["bm_open", "bm_openinnewwindow", "bm_openinnewtab", "bm_separator",
  273.                   "bm_copy"];
  274.       break;
  275.     case "FileSystemObject":
  276.       commands = ["bm_open", "bm_openinnewwindow", "bm_openinnewtab", "bm_separator",
  277.                   "bm_copy"];
  278.       break;
  279.     default: 
  280.       commands = [];
  281.     }
  282.  
  283.     return new CommandArrayEnumerator(commands);
  284.   },
  285.   
  286.   /////////////////////////////////////////////////////////////////////////////
  287.   // Retrieve the human-readable name for a particular command. Used when 
  288.   // manufacturing a UI to invoke commands.
  289.   getCommandName: function (aCommand) 
  290.   {
  291.     var cmdName = aCommand.substring(NC_NS_CMD.length);
  292.     //try {
  293.       // Note: this will succeed only if there's a string in the bookmarks
  294.       //       string bundle for this command name. Otherwise, <xul:stringbundle/>
  295.       //       will throw, we'll catch & stifle the error, and look up the command
  296.       //       name in the datasource. 
  297.       return BookmarksUtils.getLocaleString ("cmd_" + cmdName);
  298.     //}
  299.     //catch (e) {
  300.     //}   
  301.     // XXX - WORK TO DO HERE! (rjc will cry if we don't fix this) 
  302.     // need to ask the ds for the commands for this node, however we don't
  303.     // have the right params. This is kind of a problem. 
  304.     dump("*** BAD! EVIL! WICKED! NO! ACK! ARGH! ORGH!"+aCommand+"\n");
  305.     const rName = RDF.GetResource(NC_NS + "Name");
  306.     const rSource = RDF.GetResource(aNodeID);
  307.     return BMDS.GetTarget(rSource, rName, true).Value;
  308.   },
  309.     
  310.   ///////////////////////////////////////////////////////////////////////////
  311.   // Execute a command with the given source and arguments
  312.   doBookmarksCommand: function (aSource, aCommand, aArgumentsArray)
  313.   {
  314.     var rCommand = RDF.GetResource(aCommand);
  315.   
  316.     var kSuppArrayContractID = "@mozilla.org/supports-array;1";
  317.     var kSuppArrayIID = Components.interfaces.nsISupportsArray;
  318.     var sourcesArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  319.     if (aSource) {
  320.       sourcesArray.AppendElement(aSource);
  321.     }
  322.   
  323.     var argsArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  324.     var length = aArgumentsArray?aArgumentsArray.length:0;
  325.     for (var i = 0; i < length; ++i) {
  326.       var rArc = RDF.GetResource(aArgumentsArray[i].property);
  327.       argsArray.AppendElement(rArc);
  328.       var rValue = null;
  329.       if ("resource" in aArgumentsArray[i]) { 
  330.         rValue = RDF.GetResource(aArgumentsArray[i].resource);
  331.       }
  332.       else
  333.         rValue = RDF.GetLiteral(aArgumentsArray[i].literal);
  334.       argsArray.AppendElement(rValue);
  335.     }
  336.  
  337.     // Exec the command in the Bookmarks datasource. 
  338.     BMDS.DoCommand(sourcesArray, rCommand, argsArray);
  339.   },
  340.  
  341.   undoBookmarkTransaction: function ()
  342.   {
  343.     gBMtxmgr.undoTransaction();
  344.     BookmarksUtils.flushDataSource();
  345.   },
  346.  
  347.   redoBookmarkTransaction: function ()
  348.   {
  349.     gBMtxmgr.redoTransaction();
  350.     BookmarksUtils.flushDataSource();
  351.   },
  352.  
  353.   manageFolder: function (aSelection)
  354.   {
  355.     openDialog("chrome://browser/content/bookmarks/bookmarksManager.xul", 
  356.                "", "chrome,all,dialog=no", aSelection.item[0].Value);
  357.   },
  358.   
  359.   cutBookmark: function (aSelection)
  360.   {
  361.     this.copyBookmark(aSelection);
  362.     BookmarksUtils.removeSelection("cut", aSelection);
  363.   },
  364.  
  365.   copyBookmark: function (aSelection)
  366.   {
  367.  
  368.     const kSuppArrayContractID = "@mozilla.org/supports-array;1";
  369.     const kSuppArrayIID = Components.interfaces.nsISupportsArray;
  370.     var itemArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  371.  
  372.     const kSuppWStringContractID = "@mozilla.org/supports-string;1";
  373.     const kSuppWStringIID = Components.interfaces.nsISupportsString;
  374.     var bmstring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
  375.     var unicodestring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
  376.     var htmlstring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
  377.   
  378.     var sBookmarkItem = ""; var sTextUnicode = ""; var sTextHTML = "";
  379.     for (var i = 0; i < aSelection.length; ++i) {
  380.       var url  = BookmarksUtils.getProperty(aSelection.item[i], NC_NS+"URL" );
  381.       var name = BookmarksUtils.getProperty(aSelection.item[i], NC_NS+"Name");
  382.       sBookmarkItem += aSelection.item[i].Value + "\n";
  383.       sTextUnicode += url + "\n";
  384.       sTextHTML += "<A HREF=\"" + url + "\">" + name + "</A>";
  385.     }
  386.     
  387.     const kXferableContractID = "@mozilla.org/widget/transferable;1";
  388.     const kXferableIID = Components.interfaces.nsITransferable;
  389.     var xferable = Components.classes[kXferableContractID].createInstance(kXferableIID);
  390.  
  391.     xferable.addDataFlavor("moz/bookmarkclipboarditem");
  392.     bmstring.data = sBookmarkItem;
  393.     xferable.setTransferData("moz/bookmarkclipboarditem", bmstring, sBookmarkItem.length*2)
  394.     
  395.     xferable.addDataFlavor("text/html");
  396.     htmlstring.data = sTextHTML;
  397.     xferable.setTransferData("text/html", htmlstring, sTextHTML.length*2)
  398.     
  399.     xferable.addDataFlavor("text/unicode");
  400.     unicodestring.data = sTextUnicode;
  401.     xferable.setTransferData("text/unicode", unicodestring, sTextUnicode.length*2)
  402.     
  403.     const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
  404.     const kClipboardIID = Components.interfaces.nsIClipboard;
  405.     var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
  406.     clipboard.setData(xferable, null, kClipboardIID.kGlobalClipboard);
  407.   },
  408.  
  409.   pasteBookmark: function (aTarget)
  410.   {
  411.     const kXferableContractID = "@mozilla.org/widget/transferable;1";
  412.     const kXferableIID = Components.interfaces.nsITransferable;
  413.     var xferable = Components.classes[kXferableContractID].createInstance(kXferableIID);
  414.     xferable.addDataFlavor("moz/bookmarkclipboarditem");
  415.     xferable.addDataFlavor("text/x-moz-url");
  416.     xferable.addDataFlavor("text/unicode");
  417.  
  418.     const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
  419.     const kClipboardIID = Components.interfaces.nsIClipboard;
  420.     var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
  421.     clipboard.getData(xferable, kClipboardIID.kGlobalClipboard);
  422.     
  423.     var flavour = { };
  424.     var data    = { };
  425.     var length  = { };
  426.     xferable.getAnyTransferData(flavour, data, length);
  427.     var items, name;
  428.     data = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
  429.     switch (flavour.value) {
  430.     case "moz/bookmarkclipboarditem":
  431.       items = data.split("\n");
  432.       // since data are ended by \n, remove the last empty node
  433.       items.pop(); 
  434.       for (var i=0; i<items.length; ++i)
  435.         items[i] = RDF.GetResource(items[i]);
  436.       break;
  437.     case "text/x-moz-url":
  438.       // there should be only one item in this case
  439.       var ix = data.indexOf("\n");
  440.       items = data.substring(0, ix != -1 ? ix : data.length);
  441.       name  = data.substring(ix);
  442.       if (!BMSVC.IsBookmarked(items))
  443.         // XXX: we should infer the best charset
  444.         BookmarksUtils.createBookmark(null, items, null, name);
  445.       items = [items];
  446.       break;
  447.     default: 
  448.       return;
  449.     }
  450.    
  451.     var selection = {item: items, parent:Array(items.length), length: items.length}
  452.     BookmarksUtils.checkSelection(selection);
  453.     BookmarksUtils.insertSelection("paste", selection, aTarget, true);
  454.   },
  455.   
  456.   deleteBookmark: function (aSelection)
  457.   {
  458.     BookmarksUtils.removeSelection("delete", aSelection);
  459.   },
  460.  
  461.   moveBookmark: function (aSelection)
  462.   {
  463.     var rv = { selectedFolder: null };      
  464.     openDialog("chrome://browser/content/bookmarks/addBookmark.xul", "", 
  465.                "centerscreen,chrome,modal=yes,dialog=yes,resizable=yes", null, null, null, null, "selectFolder", rv);
  466.     if (!rv.selectedFolder)
  467.       return;
  468.     
  469.     var target = RDF.GetResource(rv.selectedFolder);
  470.     target = BookmarksUtils.getSelectionFromResource(target);
  471.     target = BookmarksUtils.getTargetFromSelection(target);
  472.     BookmarksUtils.moveSelection("move", aSelection, target);
  473.   },
  474.  
  475.   openBookmark: function (aSelection, aTargetBrowser, aDS) 
  476.   {
  477.     if (!aTargetBrowser)
  478.       return;
  479.     for (var i=0; i<aSelection.length; ++i) {
  480.       var type = aSelection.type[i];
  481.       if (aTargetBrowser == "save") {
  482.         var item = aSelection.item[i];
  483.         saveURL(item.Value, BookmarksUtils.getProperty(item, "Name"), null, true);
  484.       }      
  485.       else if (aTargetBrowser == "properties")
  486.         openDialog("chrome://browser/content/bookmarks/bookmarksProperties.xul", "", "centerscreen,chrome,resizable=no", aSelection.item[i].Value);      
  487.       else if (type == "Bookmark" || type == "")
  488.         this.openOneBookmark(aSelection.item[i].Value, aTargetBrowser, aDS);
  489.       else if (type == "FolderGroup" || type == "Folder" || type == "PersonalToolbarFolder")
  490.         this.openGroupBookmark(aSelection.item[i].Value, aTargetBrowser);
  491.     }
  492.   },
  493.  
  494.   // requires utilityOverlay.js if opening in new window for getTopWin()
  495.   openOneBookmark: function (aURI, aTargetBrowser, aDS)
  496.   {
  497.     var url = BookmarksUtils.getProperty(aURI, NC_NS+"URL", aDS)
  498.     // Ignore "NC:" and empty urls.
  499.     if (url == "")
  500.       return;
  501.     var w = aTargetBrowser == "window"? null:getTopWin();
  502.     if (!w) {
  503.       openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", url);
  504.       return;
  505.     }
  506.     var browser = w.document.getElementById("content");
  507.     switch (aTargetBrowser) {
  508.     case "current":
  509.       browser.loadURI(url);
  510.       w._content.focus();
  511.       break;
  512.     case "tab":
  513.       var tab = browser.addTab(url);
  514.       browser.selectedTab = tab;
  515.       browser.focus();
  516.       break;
  517.     }
  518.   },
  519.  
  520.   openGroupBookmark: function (aURI, aTargetBrowser)
  521.   {
  522.     if (aTargetBrowser == "current" || aTargetBrowser == "tab") {
  523.       var w        = getTopWin();
  524.       var browser  = w.document.getElementById("content");
  525.       var resource = RDF.GetResource(aURI);
  526.       var urlArc   = RDF.GetResource(NC_NS+"URL");
  527.       RDFC.Init(BMDS, resource);
  528.       var containerChildren = RDFC.GetElements();
  529.       var tabPanels = browser.mPanelContainer.childNodes;
  530.       var tabCount  = tabPanels.length;
  531.       var doReplace = PREF.getBoolPref("browser.tabs.loadFolderAndReplace");
  532.       var index0;
  533.       if (doReplace)
  534.         index0 = 0;
  535.       else {
  536.         for (index0=tabCount-1; index0>=0; --index0)
  537.           if (browser.browsers[index0].webNavigation.currentURI.spec != "about:blank")
  538.             break;
  539.         ++index0;
  540.       }
  541.  
  542.       var index  = index0;
  543.       while (containerChildren.hasMoreElements()) {
  544.         var res = containerChildren.getNext().QueryInterface(kRDFRSCIID);
  545.         var target = BMDS.GetTarget(res, urlArc, true);
  546.         if (target) {
  547.           var uri = target.QueryInterface(kRDFLITIID).Value;
  548.           if (index < tabCount)
  549.             tabPanels[index].loadURI(uri);
  550.           else
  551.             browser.addTab(uri);
  552.           ++index;
  553.         }
  554.       }
  555.  
  556.       // If the bookmark group was completely invalid, just bail.
  557.       if (index == index0)
  558.         return;
  559.  
  560.       // Select the first tab in the group.
  561.       var tabs = browser.mTabContainer.childNodes;
  562.       browser.selectedTab = tabs[index0];
  563.  
  564.       // Close any remaining open tabs that are left over.
  565.       // (Always skipped when we append tabs)
  566.       for (var i = tabCount-1; i >= index; --i)
  567.         browser.removeTab(tabs[i]);
  568.  
  569.       // and focus the content
  570.       browser.focus();
  571.  
  572.     } else {
  573.       dump("Open Group in new window: not implemented...\n");
  574.     }
  575.   },
  576.  
  577.   findBookmark: function ()
  578.   {
  579.     openDialog("chrome://browser/content/bookmarks/findBookmark.xul",
  580.                "FindBookmarksWindow",
  581.                "centerscreen,resizable=no,chrome,dependent");
  582.   },
  583.  
  584.   createNewFolder: function (aTarget)
  585.   {
  586.     var name      = BookmarksUtils.getLocaleString("ile_newfolder");
  587.     var rFolder   = BMSVC.createFolder(name);
  588.     var selection = BookmarksUtils.getSelectionFromResource(rFolder);
  589.     var ok        = BookmarksUtils.insertSelection("newfolder", selection, aTarget);
  590.     if (ok)
  591.       this.openBookmark(selection, "properties");
  592.   },
  593.  
  594.   createNewSeparator: function (aTarget)
  595.   {
  596.     var rSeparator  = BMSVC.createSeparator();
  597.     var selection   = BookmarksUtils.getSelectionFromResource(rSeparator);
  598.     BookmarksUtils.insertSelection("newseparator", selection, aTarget)
  599.   },
  600.  
  601.   importBookmarks: function ()
  602.   {
  603.     ///transaction...
  604.     try {
  605.       const kFilePickerContractID = "@mozilla.org/filepicker;1";
  606.       const kFilePickerIID = Components.interfaces.nsIFilePicker;
  607.       const kFilePicker = Components.classes[kFilePickerContractID].createInstance(kFilePickerIID);
  608.     
  609.       const kTitle = BookmarksUtils.getLocaleString("SelectImport");
  610.       kFilePicker.init(window, kTitle, kFilePickerIID["modeOpen"]);
  611.       kFilePicker.appendFilters(kFilePickerIID.filterHTML | kFilePickerIID.filterAll);
  612.       var fileName;
  613.       if (kFilePicker.show() != kFilePickerIID.returnCancel) {
  614.         fileName = kFilePicker.fileURL.spec;
  615.         if (!fileName) return;
  616.       }
  617.       else return;
  618.     }
  619.     catch (e) {
  620.       return;
  621.     }
  622.     rTarget = RDF.GetResource("NC:BookmarksRoot");
  623.     RDFC.Init(BMDS, rTarget);
  624.     var countBefore = parseInt(BookmarksUtils.getProperty(rTarget, RDF_NS+"nextVal"));
  625.     var args = [{ property: NC_NS+"URL", literal: fileName}];
  626.     this.doBookmarksCommand(rTarget, NC_NS_CMD+"import", args);
  627.     var countAfter = parseInt(BookmarksUtils.getProperty(rTarget, RDF_NS+"nextVal"));
  628.  
  629.     var transaction = new BookmarkImportTransaction("import");
  630.     for (var index = countBefore; index < countAfter; index++) {
  631.       var nChildArc = RDFCU.IndexToOrdinalResource(index);
  632.       var rChild    = BMDS.GetTarget(rTarget, nChildArc, true);
  633.       transaction.item   .push(rChild);
  634.       transaction.parent .push(rTarget);
  635.       transaction.index  .push(index);
  636.       transaction.isValid.push(true);
  637.     }
  638.     var isCancelled = !BookmarksUtils.any(transaction.isValid)
  639.     if (!isCancelled) {
  640.       gBMtxmgr.doTransaction(transaction);
  641.       BookmarksUtils.flushDataSource();
  642.     }    
  643.   },
  644.  
  645.   exportBookmarks: function ()
  646.   {
  647.     try {
  648.       const kFilePickerContractID = "@mozilla.org/filepicker;1";
  649.       const kFilePickerIID = Components.interfaces.nsIFilePicker;
  650.       const kFilePicker = Components.classes[kFilePickerContractID].createInstance(kFilePickerIID);
  651.       
  652.       const kTitle = BookmarksUtils.getLocaleString("EnterExport");
  653.       kFilePicker.init(window, kTitle, kFilePickerIID["modeSave"]);
  654.       kFilePicker.appendFilters(kFilePickerIID.filterHTML | kFilePickerIID.filterAll);
  655.       kFilePicker.defaultString = "bookmarks.html";
  656.       var fileName;
  657.       if (kFilePicker.show() != kFilePickerIID.returnCancel) {
  658.         fileName = kFilePicker.fileURL.spec;
  659.         if (!fileName) return;
  660.       }
  661.       else return;
  662.     }
  663.     catch (e) {
  664.       return;
  665.     }
  666.     var selection = RDF.GetResource("NC:BookmarksRoot");
  667.     var args = [{ property: NC_NS+"URL", literal: fileName}];
  668.     this.doBookmarksCommand(selection, NC_NS_CMD+"export", args);
  669.   }
  670.  
  671. }
  672.  
  673.   /////////////////////////////////////////////////////////////////////////////
  674.   // Command handling & Updating.
  675. var BookmarksController = {
  676.  
  677.   supportsCommand: function (aCommand)
  678.   {
  679.     var isCommandSupported;
  680.     switch(aCommand) {
  681.     case "cmd_undo":
  682.     case "cmd_redo":
  683.     case "cmd_bm_undo":
  684.     case "cmd_bm_redo":
  685.     case "cmd_bm_cut":
  686.     case "cmd_bm_copy":
  687.     case "cmd_bm_paste":
  688.     case "cmd_bm_delete":
  689.     case "cmd_bm_selectAll":
  690.     case "cmd_bm_open":
  691.     case "cmd_bm_openinnewwindow":
  692.     case "cmd_bm_openinnewtab":
  693.     case "cmd_bm_expandfolder":
  694.     case "cmd_bm_openfolder":
  695.     case "cmd_bm_managefolder":
  696.     case "cmd_bm_newbookmark":
  697.     case "cmd_bm_newfolder":
  698.     case "cmd_bm_newseparator":
  699.     case "cmd_bm_find":
  700.     case "cmd_bm_properties":
  701.     case "cmd_bm_rename":
  702.     case "cmd_bm_setnewbookmarkfolder":
  703.     case "cmd_bm_setpersonaltoolbarfolder":
  704.     case "cmd_bm_setnewsearchfolder":
  705.     case "cmd_bm_import":
  706.     case "cmd_bm_export":
  707.     case "cmd_bm_movebookmark":
  708.       isCommandSupported = true;
  709.       break;
  710.     default:
  711.       isCommandSupported = false;
  712.     }
  713.     //if (!isCommandSupported)
  714.     //  dump("Bookmark command '"+aCommand+"' is not supported!\n");
  715.     return isCommandSupported;
  716.   },
  717.  
  718.   isCommandEnabled: function (aCommand, aSelection, aTarget)
  719.   {
  720.     var length   = aSelection.length;
  721.     if (length == 0)
  722.       return false;
  723.     var isValidTarget = BookmarksUtils.isValidTargetContainer(aTarget.parent)
  724.     var item0    = aSelection.item[0].Value;
  725.     var type0    = aSelection.type[0];
  726.     var isNotRef = !aSelection.isRef;
  727.     var i;
  728.  
  729.     switch(aCommand) {
  730.     case "cmd_undo":
  731.     case "cmd_redo":
  732.     case "cmd_bm_undo":
  733.     case "cmd_bm_redo":
  734.       return true;
  735.     case "cmd_bm_paste":
  736.       if (!isValidTarget)
  737.         return false;
  738.       const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
  739.       const kClipboardIID = Components.interfaces.nsIClipboard;
  740.       var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
  741.       const kSuppArrayContractID = "@mozilla.org/supports-array;1";
  742.       const kSuppArrayIID = Components.interfaces.nsISupportsArray;
  743.       var flavourArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  744.       const kSuppStringContractID = "@mozilla.org/supports-cstring;1";
  745.       const kSuppStringIID = Components.interfaces.nsISupportsCString;
  746.     
  747.       var flavours = ["moz/bookmarkclipboarditem", "text/x-moz-url"];
  748.       for (i = 0; i < flavours.length; ++i) {
  749.         const kSuppString = Components.classes[kSuppStringContractID].createInstance(kSuppStringIID);
  750.         kSuppString.data = flavours[i];
  751.         flavourArray.AppendElement(kSuppString);
  752.       }
  753.       var hasFlavours = clipboard.hasDataMatchingFlavors(flavourArray, kClipboardIID.kGlobalClipboard);
  754.       return hasFlavours;
  755.     case "cmd_bm_copy":
  756.       return isNotRef;
  757.     case "cmd_bm_cut":
  758.     case "cmd_bm_delete":
  759.       return isNotRef && aSelection.containsMutable;
  760.     case "cmd_bm_selectAll":
  761.       return true;
  762.     case "cmd_bm_open":
  763.     case "cmd_bm_expandfolder":
  764.     case "cmd_bm_managefolder":
  765.       return isNotRef && length == 1;
  766.     case "cmd_bm_openinnewwindow":
  767.     case "cmd_bm_openinnewtab":
  768.       return true;
  769.     case "cmd_bm_openfolder":
  770.       for (i=0; i<aSelection.length; ++i) {
  771.         if (aSelection.type[i] == ""         ||
  772.             aSelection.type[i] == "Bookmark" ||
  773.             aSelection.type[i] == "BookmarkSeparator")
  774.           return false;
  775.         RDFC.Init(BMDS, aSelection.item[i]);
  776.         var children = RDFC.GetElements();
  777.         while (children.hasMoreElements()) {
  778.           if (BookmarksUtils.resolveType(children.getNext()) == "Bookmark")
  779.             return true;
  780.         }
  781.       }
  782.       return false;
  783.     case "cmd_bm_find":
  784.     case "cmd_bm_import":
  785.     case "cmd_bm_export":
  786.       return true;
  787.     case "cmd_bm_newbookmark":
  788.     case "cmd_bm_newfolder":
  789.     case "cmd_bm_newseparator":
  790.       return isValidTarget && length == 1;
  791.     case "cmd_bm_properties":
  792.     case "cmd_bm_rename":
  793.       return isNotRef && length == 1;
  794.     case "cmd_bm_setnewbookmarkfolder":
  795.       if (!isNotRef || length != 1) 
  796.         return false;
  797.       return item0 != "NC:NewBookmarkFolder"     &&
  798.              (type0 == "Folder" || type0 == "PersonalToolbarFolder");
  799.     case "cmd_bm_setpersonaltoolbarfolder":
  800.       if (!isNotRef || length != 1)
  801.         return false
  802.       return item0 != "NC:PersonalToolbarFolder" && type0 == "Folder";
  803.     case "cmd_bm_setnewsearchfolder":
  804.       if (!isNotRef || length != 1)
  805.         return false
  806.       return item0 != "NC:NewSearchFolder"       && 
  807.              (type0 == "Folder" || type0 == "PersonalToolbarFolder");
  808.     case "cmd_bm_movebookmark":
  809.       return isNotRef;
  810.     default:
  811.       return false;
  812.     }
  813.   },
  814.  
  815.   doCommand: function (aCommand, aSelection, aTarget)
  816.   {
  817.     switch (aCommand) {
  818.     case "cmd_undo":
  819.     case "cmd_bm_undo":
  820.       BookmarksCommand.undoBookmarkTransaction();
  821.       break;
  822.     case "cmd_redo":
  823.     case "cmd_bm_redo":
  824.       BookmarksCommand.redoBookmarkTransaction();
  825.       break;
  826.     case "cmd_bm_open":
  827.       BookmarksCommand.openBookmark(aSelection, "current");
  828.       break;
  829.     case "cmd_bm_openinnewwindow":
  830.       BookmarksCommand.openBookmark(aSelection, "window");
  831.       break;
  832.     case "cmd_bm_openinnewtab":
  833.       BookmarksCommand.openBookmark(aSelection, "tab");
  834.       break;
  835.     case "cmd_bm_openfolder":
  836.       BookmarksCommand.openBookmark(aSelection, "current");
  837.       break;
  838.     case "cmd_bm_managefolder":
  839.       BookmarksCommand.manageFolder(aSelection);
  840.       break;
  841.     case "cmd_bm_setnewbookmarkfolder":
  842.     case "cmd_bm_setpersonaltoolbarfolder":
  843.     case "cmd_bm_setnewsearchfolder":
  844.       BookmarksCommand.doBookmarksCommand(aSelection.item[0], NC_NS_CMD+aCommand.substring("cmd_bm_".length), []);
  845.       break;
  846.     case "cmd_bm_rename":
  847.     case "cmd_bm_properties":
  848.       BookmarksCommand.openBookmark(aSelection, "properties");
  849.       break;
  850.     case "cmd_bm_find":
  851.       BookmarksCommand.findBookmark();
  852.       break;
  853.     case "cmd_bm_cut":
  854.       BookmarksCommand.cutBookmark(aSelection);
  855.       break;
  856.     case "cmd_bm_copy":
  857.       BookmarksCommand.copyBookmark(aSelection);
  858.       break;
  859.     case "cmd_bm_paste":
  860.       BookmarksCommand.pasteBookmark(aTarget);
  861.       break;
  862.     case "cmd_bm_delete":
  863.       BookmarksCommand.deleteBookmark(aSelection);
  864.       break;
  865.     case "cmd_bm_movebookmark":
  866.       BookmarksCommand.moveBookmark(aSelection);
  867.       break;
  868.     case "cmd_bm_newfolder":
  869.       BookmarksCommand.createNewFolder(aTarget);
  870.       break;
  871.     case "cmd_bm_newbookmark":
  872.       var folder = aTarget.parent.Value;
  873.       var rv = { newBookmark: null };
  874.       openDialog("chrome://browser/content/bookmarks/addBookmark.xul", "", 
  875.                  "centerscreen,chrome,modal=yes,dialog=yes,resizable=no", null, null, folder, null, "newBookmark", rv);
  876.       break;
  877.     case "cmd_bm_newseparator":
  878.       BookmarksCommand.createNewSeparator(aTarget);
  879.       break;
  880.     case "cmd_bm_import":
  881.       BookmarksCommand.importBookmarks();
  882.       break;
  883.     case "cmd_bm_export":
  884.       BookmarksCommand.exportBookmarks();
  885.       break;
  886.     default: 
  887.       dump("Bookmark command "+aCommand+" not handled!\n");
  888.     }
  889.  
  890.   },
  891.  
  892.   onCommandUpdate: function (aSelection, aTarget)
  893.   {
  894.     var commands = ["cmd_bm_newbookmark", "cmd_bm_newfolder", "cmd_bm_newseparator", 
  895.                     "cmd_bm_properties", "cmd_bm_rename", 
  896.                     "cmd_bm_copy", "cmd_bm_paste", "cmd_bm_cut", "cmd_bm_delete",
  897.                     "cmd_bm_setpersonaltoolbarfolder", 
  898.                     "cmd_bm_setnewbookmarkfolder",
  899.                     "cmd_bm_setnewsearchfolder", "cmd_bm_movebookmark", 
  900.                     "cmd_bm_openfolder", "cmd_bm_managefolder"];
  901.     for (var i = 0; i < commands.length; ++i) {
  902.       var enabled = this.isCommandEnabled(commands[i], aSelection, aTarget);
  903.       var commandNode = document.getElementById(commands[i]);
  904.      if (commandNode) { 
  905.         if (enabled) 
  906.           commandNode.removeAttribute("disabled");
  907.         else 
  908.           commandNode.setAttribute("disabled", "true");
  909.       }
  910.     }
  911.   }
  912. }
  913.  
  914. function CommandArrayEnumerator (aCommandArray)
  915. {
  916.   this._inner = [];
  917.   for (var i = 0; i < aCommandArray.length; ++i)
  918.     this._inner.push(RDF.GetResource(NC_NS_CMD + aCommandArray[i]));
  919.     
  920.   this._index = 0;
  921. }
  922.  
  923. CommandArrayEnumerator.prototype = {
  924.   getNext: function () 
  925.   {
  926.     return this._inner[this._index];
  927.   },
  928.   
  929.   hasMoreElements: function ()
  930.   {
  931.     return this._index < this._inner.length;
  932.   }
  933. };
  934.  
  935. var BookmarksUtils = {
  936.  
  937.   DROP_BEFORE: Components.interfaces.nsITreeView.inDropBefore,
  938.   DROP_ON    : Components.interfaces.nsITreeView.inDropOn,
  939.   DROP_AFTER : Components.interfaces.nsITreeView.inDropAfter,
  940.  
  941.   any: function (aArray)
  942.   {
  943.     for (var i=0; i<aArray.length; ++i)
  944.       if (aArray[i]) return true;
  945.     return false;
  946.   },
  947.  
  948.   all: function (aArray)
  949.   {
  950.     for (var i=0; i<aArray.length; ++i)
  951.       if (!aArray[i]) return false;
  952.     return true;
  953.   },
  954.  
  955.   _bundle        : null,
  956.   _brandShortName: null,
  957.  
  958.   /////////////////////////////////////////////////////////////////////////////////////
  959.   // returns a property from chrome://browser/locale/bookmarks/bookmarks.properties
  960.   getLocaleString: function (aStringKey, aReplaceString)
  961.   {
  962.     if (!this._bundle) {
  963.       // for those who would xblify Bookmarks.js, there is a need to create string bundle 
  964.       // manually instead of using <xul:stringbundle/> see bug 63370 for details
  965.       var LOCALESVC = Components.classes["@mozilla.org/intl/nslocaleservice;1"]
  966.                                 .getService(Components.interfaces.nsILocaleService);
  967.       var BUNDLESVC = Components.classes["@mozilla.org/intl/stringbundle;1"]
  968.                                 .getService(Components.interfaces.nsIStringBundleService);
  969.       var bookmarksBundle  = "chrome://browser/locale/bookmarks/bookmarks.properties";
  970.       this._bundle         = BUNDLESVC.createBundle(bookmarksBundle, LOCALESVC.GetApplicationLocale());
  971.       var brandBundle      = "chrome://global/locale/brand.properties";
  972.       this._brandShortName = BUNDLESVC.createBundle(brandBundle,     LOCALESVC.GetApplicationLocale())
  973.                                       .GetStringFromName("brandShortName");
  974.     }
  975.    
  976.     var bundle;
  977.     try {
  978.       if (!aReplaceString)
  979.         bundle = this._bundle.GetStringFromName(aStringKey);
  980.       else if (typeof(aReplaceString) == "string")
  981.         bundle = this._bundle.formatStringFromName(aStringKey, [aReplaceString], 1);
  982.       else
  983.         bundle = this._bundle.formatStringFromName(aStringKey, aReplaceString, aReplaceString.length);
  984.     } catch (e) {
  985.       SOUND.beep();
  986.       dump("Bookmark bundle "+aStringKey+" not found!\n")
  987.       bundle = "";
  988.     }
  989.  
  990.     bundle = bundle.replace(/%brandShortName%/, this._brandShortName);
  991.     return bundle;
  992.   },
  993.     
  994.   /////////////////////////////////////////////////////////////////////////////
  995.   // returns the literal targeted by the URI aArcURI for a resource or uri
  996.   getProperty: function (aInput, aArcURI, aDS)
  997.   {
  998.     var node;
  999.     var arc  = RDF.GetResource(aArcURI);
  1000.     if (typeof(aInput) == "string") 
  1001.       aInput = RDF.GetResource(aInput);
  1002.     if (!aDS)
  1003.       node = BMDS.GetTarget(aInput, arc, true);
  1004.     else
  1005.       node = aDS .GetTarget(aInput, arc, true);
  1006.     try {
  1007.       return node.QueryInterface(kRDFRSCIID).Value;
  1008.     }
  1009.     catch (e) {
  1010.       return node? node.QueryInterface(kRDFLITIID).Value : "";
  1011.     }    
  1012.   },
  1013.  
  1014.   /////////////////////////////////////////////////////////////////////////////
  1015.   // Determine the rdf:type property for the given resource.
  1016.   resolveType: function (aResource)
  1017.   {
  1018.     var type = this.getProperty(aResource, RDF_NS+"type")
  1019.     if (type != "")
  1020.       type = type.split("#")[1]
  1021.     if (type == "Folder") {
  1022.       if (this.isPersonalToolbarFolder(aResource))
  1023.         type = "PersonalToolbarFolder";
  1024.       else if (this.isFolderGroup(aResource))
  1025.         type = "FolderGroup";
  1026.     }
  1027.     return type;
  1028.   },
  1029.  
  1030.   /////////////////////////////////////////////////////////////////////////////
  1031.   // Returns true if aResource is a folder group
  1032.   isFolderGroup: function (aResource) {
  1033.     return this.getProperty(aResource, NC_NS+"FolderGroup") == "true"
  1034.   },
  1035.  
  1036.   /////////////////////////////////////////////////////////////////////////////
  1037.   // Returns true if aResource is the Personal Toolbar Folder
  1038.   isPersonalToolbarFolder: function (aResource) {
  1039.     return this.getProperty(aResource, NC_NS+"FolderType") == "NC:PersonalToolbarFolder"
  1040.   },
  1041.  
  1042.   /////////////////////////////////////////////////////////////////////////////
  1043.   // Returns the folder which 'FolderType' is aProperty
  1044.   getSpecialFolder: function (aProperty)
  1045.   {
  1046.     var sources = BMDS.GetSources(RDF.GetResource(NC_NS+"FolderType"),
  1047.                                   RDF.GetResource(aProperty), true);
  1048.     var folder = null;
  1049.     if (sources.hasMoreElements())
  1050.       folder = sources.getNext();
  1051.     else 
  1052.       folder = RDF.GetResource("NC:BookmarksRoot");
  1053.     return folder;
  1054.   },
  1055.  
  1056.   /////////////////////////////////////////////////////////////////////////////
  1057.   // Returns the New Bookmark Folder
  1058.   getNewBookmarkFolder: function()
  1059.   {
  1060.     return this.getSpecialFolder("NC:NewBookmarkFolder");
  1061.   },
  1062.  
  1063.   /////////////////////////////////////////////////////////////////////////////
  1064.   // Returns the New Search Folder
  1065.   getNewSearchFolder: function()
  1066.   {
  1067.     return this.getSpecialFolder("NC:NewSearchFolder");
  1068.   },
  1069.  
  1070.   /////////////////////////////////////////////////////////////////////////////
  1071.   // Returns the container of a given container
  1072.   getParentOfContainer: function(aChild)
  1073.   {
  1074.     var arcsIn = BMDS.ArcLabelsIn(aChild);
  1075.     var containerArc;
  1076.     while (arcsIn.hasMoreElements()) {
  1077.       containerArc = arcsIn.getNext();
  1078.       if (RDFCU.IsOrdinalProperty(containerArc)) {
  1079.         return BMDS.GetSources(containerArc, aChild, true).getNext()
  1080.                    .QueryInterface(kRDFRSCIID);
  1081.       }
  1082.     }
  1083.     return null;
  1084.   },
  1085.  
  1086.   /////////////////////////////////////////////////////////////////////////////
  1087.   // Returns a cloned folder a aFolder (with a different uri). folderType and 
  1088.   // other properties are recursively dropped except the 'folder group' one.
  1089.   cloneFolder: function (aFolder) 
  1090.   {
  1091.     var rName = this.getProperty(aFolder, NC_NS+"Name");    
  1092.     var newFolder;
  1093.     if (this.isFolderGroup(aFolder))
  1094.       newFolder = BMSVC.createGroup (rName)
  1095.     else
  1096.       newFolder = BMSVC.createFolder(rName);
  1097.     
  1098.     // Now need to append kiddies. 
  1099.     try {
  1100.       RDFC.Init(BMDS, aFolder);
  1101.       var elts = RDFC.GetElements();
  1102.       RDFC.Init(BMDS, newFolder);
  1103.  
  1104.       while (elts.hasMoreElements()) {
  1105.         var curr = elts.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1106.         if (RDFCU.IsContainer(BMDS, curr))
  1107.           curr = BookmarksUtils.cloneFolder(curr);
  1108.         RDFC.AppendElement(curr);
  1109.       }
  1110.     }
  1111.     catch (e) {
  1112.     }
  1113.     return newFolder;
  1114.   },
  1115.   
  1116.   /////////////////////////////////////////////////////////////////////////////
  1117.   // Caches frequently used informations about the selection
  1118.   checkSelection: function (aSelection)
  1119.   {
  1120.     aSelection.type            = new Array(aSelection.length);
  1121.     aSelection.protocol        = new Array(aSelection.length);
  1122.     aSelection.isContainer     = new Array(aSelection.length);
  1123.     aSelection.isImmutable     = new Array(aSelection.length);
  1124.     aSelection.isValid         = new Array(aSelection.length);
  1125.     aSelection.containsMutable = false;
  1126.     aSelection.containsPTF     = false;
  1127.     var index, item, parent, type, protocol, isContainer, isImmutable, isValid;
  1128.     for (var i=0; i<aSelection.length; ++i) {
  1129.       item        = aSelection.item[i];
  1130.       parent      = aSelection.parent[i];
  1131.       type        = BookmarksUtils.resolveType(item);
  1132.       protocol    = item.Value.split(":")[0];
  1133.       isContainer = RDFCU.IsContainer(BMDS, item) ||
  1134.                     protocol == "find" || protocol == "file";
  1135.       isValid     = true;
  1136.       isImmutable = false;
  1137.       if (item == "NC:BookmarksRoot")
  1138.         isImmutable = true;
  1139.       else if (type != "Bookmark" && type != "BookmarkSeparator" && 
  1140.                type != "Folder"   && type != "FolderGroup" && 
  1141.                type != "PersonalToolbarFolder")
  1142.         isImmutable = true;
  1143.       else if (parent) {
  1144.         var parentProtocol = parent.Value.split(":")[0];
  1145.         if (parentProtocol == "find" || parentProtocol == "file")
  1146.           aSelection.parent[i] = null;
  1147.       }
  1148.       if (!isImmutable && aSelection.parent[i])
  1149.         aSelection.containsMutable = true;
  1150.  
  1151.       aSelection.type       [i] = type;
  1152.       aSelection.protocol   [i] = protocol;
  1153.       aSelection.isContainer[i] = isContainer;
  1154.       aSelection.isImmutable[i] = isImmutable;
  1155.       aSelection.isValid    [i] = isValid;
  1156.     }
  1157.     if (this.isContainerChildOrSelf(RDF.GetResource("NC:PersonalToolbarFolder"), aSelection))
  1158.       aSelection.containsPTF = true;
  1159.   },
  1160.  
  1161.   isSelectionValidForInsertion: function (aSelection, aTarget, aAction)
  1162.   {
  1163.     var isValid = new Array(aSelection.length);
  1164.     for (var i=0; i<aSelection.length; ++i)
  1165.       isValid[i] = false;
  1166.     if (!BookmarksUtils.isValidTargetContainer(aTarget.parent, aSelection))
  1167.       return isValid;
  1168.     for (i=0; i<aSelection.length; ++i) {
  1169.       if (!aSelection.isValid[i])
  1170.         continue;
  1171.       if (!BookmarksUtils.isChildOfContainer(aSelection.item[i], aTarget.parent) ||
  1172.           aAction == "move" && aSelection.parent[i] == aTarget.parent            ||
  1173.           aSelection.isContainer[i])
  1174.         isValid[i] = true;
  1175.     }
  1176.     return isValid;
  1177.   },
  1178.  
  1179.   isSelectionValidForDeletion: function (aSelection)
  1180.   {
  1181.     var isValid = new Array(aSelection.length);
  1182.     for (i=0; i<aSelection.length; ++i) {
  1183.       if (!aSelection.isValid[i] || aSelection.isImmutable[i] || 
  1184.           !aSelection.parent [i] || aSelection.containsPTF)
  1185.         isValid[i] = false;
  1186.       else
  1187.         isValid[i] = true;
  1188.     }
  1189.     return isValid;
  1190.   },
  1191.  
  1192.   /////////////////////////////////////////////////////////////////////////////
  1193.   // Returns true is aContainer is a member or a child of the selection
  1194.   isContainerChildOrSelf: function (aContainer, aSelection)
  1195.   {
  1196.     var folder = aContainer;
  1197.     do {
  1198.       for (var i=0; i<aSelection.length; ++i) {
  1199.         if (aSelection.isContainer[i] && aSelection.item[i] == folder)
  1200.           return true;
  1201.       }
  1202.       folder = BookmarksUtils.getParentOfContainer(folder);
  1203.       if (!folder)
  1204.         return false; // sanity check
  1205.     } while (folder.Value != "NC:BookmarksRoot")
  1206.     return false;
  1207.   },
  1208.  
  1209.   /////////////////////////////////////////////////////////////////////////////
  1210.   // Returns true if aSelection can be inserted in aFolder
  1211.   isValidTargetContainer: function (aFolder, aSelection)
  1212.   {
  1213.  
  1214.     if (!aFolder)
  1215.       return false;
  1216.     if (aFolder.Value == "NC:BookmarksRoot")
  1217.       return true;
  1218.  
  1219.     // don't insert items in an invalid container
  1220.     // 'file:' and 'find:' items have a 'Bookmark' type
  1221.     var type = BookmarksUtils.resolveType(aFolder);
  1222.     if (type != "Folder" && type != "FolderGroup" && type != "PersonalToolbarFolder")
  1223.       return false;
  1224.  
  1225.     // bail if we just check the container
  1226.     if (!aSelection)
  1227.       return true;
  1228.  
  1229.     // don't insert folders in a group folder except for 'file:' pseudo folders
  1230.     if (type == "FolderGroup")
  1231.       for (var i=0; i<aSelection.length; ++i)
  1232.         if (aSelection.isContainer[i] && aSelection.protocol[i] != "file")
  1233.           return false;
  1234.  
  1235.     // check that the selected folder is not the selected item nor its child
  1236.     if (this.isContainerChildOrSelf(aFolder, aSelection))
  1237.       return false;
  1238.  
  1239.     return true;
  1240.   },
  1241.  
  1242.   /////////////////////////////////////////////////////////////////////////////
  1243.   // Returns true is aItem is a child of aContainer
  1244.   isChildOfContainer: function (aItem, aContainer)
  1245.   {
  1246.     RDFC.Init(BMDS, aContainer)
  1247.     var rChildren = RDFC.GetElements();
  1248.     while (rChildren.hasMoreElements()) {
  1249.       if (aItem == rChildren.getNext())
  1250.         return true;
  1251.     }
  1252.     return false;
  1253.   },
  1254.  
  1255.   /////////////////////////////////////////////////////////////////////////////
  1256.   removeSelection: function (aAction, aSelection)
  1257.   {
  1258.     var transaction     = new BookmarkRemoveTransaction(aAction);
  1259.     transaction.item    = new Array(aSelection.length);
  1260.     transaction.parent  = new Array(aSelection.length);
  1261.     transaction.index   = new Array(aSelection.length);
  1262.     transaction.isValid = BookmarksUtils.isSelectionValidForDeletion(aSelection);
  1263.     for (var i = 0; i < aSelection.length; ++i) {
  1264.       transaction.item  [i] = aSelection.item  [i];
  1265.       transaction.parent[i] = aSelection.parent[i];
  1266.       if (transaction.isValid[i]) {
  1267.         RDFC.Init(BMDS, aSelection.parent[i]);
  1268.         transaction.index[i]  = RDFC.IndexOf(aSelection.item[i]);
  1269.       }
  1270.     }
  1271.     if (aAction != "move" && !BookmarksUtils.all(transaction.isValid))
  1272.       SOUND.beep();
  1273.     var isCancelled = !BookmarksUtils.any(transaction.isValid)
  1274.     if (!isCancelled) {
  1275.       gBMtxmgr.doTransaction(transaction)
  1276.       if (aAction != "move")
  1277.         BookmarksUtils.flushDataSource();
  1278.     }
  1279.     return !isCancelled;
  1280.   },
  1281.       // If the current bookmark is the IE Favorites folder, we have a little
  1282.       // extra work to do - set the pref |browser.bookmarks.import_system_favorites|
  1283.       // to ensure that we don't re-import next time. 
  1284.       //if (aSelection[count].getAttribute("type") == (NC_NS + "IEFavoriteFolder")) {
  1285.       //  const kPrefSvcContractID = "@mozilla.org/preferences-service;1";
  1286.       //  const kPrefSvcIID = Components.interfaces.nsIPrefBranch;
  1287.       //  const kPrefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
  1288.       //  kPrefSvc.setBoolPref("browser.bookmarks.import_system_favorites", false);
  1289.       //}
  1290.         
  1291.   insertSelection: function (aAction, aSelection, aTarget, aDoCopy)
  1292.   {
  1293.     var transaction     = new BookmarkInsertTransaction(aAction);
  1294.     transaction.item    = new Array(aSelection.length);
  1295.     transaction.parent  = new Array(aSelection.length);
  1296.     transaction.index   = new Array(aSelection.length);
  1297.     if (aAction != "move")
  1298.       transaction.isValid = BookmarksUtils.isSelectionValidForInsertion(aSelection, aTarget, aAction);
  1299.     else // isValid has already been determined in moveSelection
  1300.       transaction.isValid = aSelection.isValid;
  1301.     var index = aTarget.index;
  1302.     for (var i=0; i<aSelection.length; ++i) {
  1303.       var rSource = aSelection.item[i];
  1304.       if (transaction.isValid[i]) {
  1305.         if (aDoCopy && aSelection.isContainer[i])
  1306.           rSource = BookmarksUtils.cloneFolder(rSource);
  1307.         transaction.item  [i] = rSource;
  1308.         transaction.parent[i] = aTarget.parent;
  1309.         transaction.index [i] = index++;
  1310.       } else {
  1311.         transaction.item  [i] = rSource;
  1312.         transaction.parent[i] = aSelection.parent[i];
  1313.       } 
  1314.     }
  1315.     if (!BookmarksUtils.all(transaction.isValid))
  1316.       SOUND.beep();
  1317.     var isCancelled = !BookmarksUtils.any(transaction.isValid)
  1318.     if (!isCancelled) {
  1319.       gBMtxmgr.doTransaction(transaction);
  1320.       BookmarksUtils.flushDataSource();
  1321.     }
  1322.     return !isCancelled;
  1323.   },
  1324.  
  1325.   moveSelection: function (aAction, aSelection, aTarget)
  1326.   {
  1327.     aSelection.isValid = BookmarksUtils.isSelectionValidForInsertion(aSelection, aTarget, "move");
  1328.     var transaction = new BookmarkMoveTransaction(aAction, aSelection, aTarget);
  1329.     var isCancelled = !BookmarksUtils.any(transaction.isValid);
  1330.     if (!isCancelled) {
  1331.       gBMtxmgr.doTransaction(transaction);
  1332.     } else
  1333.       SOUND.beep();
  1334.     return !isCancelled;
  1335.   }, 
  1336.  
  1337.   getXferDataFromSelection: function (aSelection)
  1338.   {
  1339.     if (aSelection.length == 0)
  1340.       return null;
  1341.     var dataSet = new TransferDataSet();
  1342.     var data, item, parent, name;
  1343.     for (var i=0; i<aSelection.length; ++i) {
  1344.       data   = new TransferData();
  1345.       item   = aSelection.item[i].Value;
  1346.       parent = aSelection.parent[i].Value;
  1347.       name   = BookmarksUtils.getProperty(item, "Name");
  1348.       data.addDataForFlavour("moz/rdfitem",    item+"\n"+(parent?parent:""));
  1349.       data.addDataForFlavour("text/x-moz-url", item+"\n"+name);
  1350.       data.addDataForFlavour("text/html",      "<A HREF='"+item+"'>"+name+"</A>");
  1351.       data.addDataForFlavour("text/unicode",   item);
  1352.       dataSet.push(data);
  1353.     }
  1354.     return dataSet;
  1355.   },
  1356.  
  1357.   getSelectionFromXferData: function (aDragSession)
  1358.   {
  1359.     var selection    = {};
  1360.     selection.item   = [];
  1361.     selection.parent = [];
  1362.     var trans = Components.classes["@mozilla.org/widget/transferable;1"]
  1363.                           .createInstance(Components.interfaces.nsITransferable);
  1364.     trans.addDataFlavor("moz/rdfitem");
  1365.     trans.addDataFlavor("text/x-moz-url");
  1366.     trans.addDataFlavor("text/unicode");
  1367.     var uri, extra, rSource, rParent, parent;
  1368.     for (var i = 0; i < aDragSession.numDropItems; ++i) {
  1369.       var bestFlavour = {}, dataObj = {}, len = {};
  1370.       aDragSession.getData(trans, i);
  1371.       trans.getAnyTransferData(bestFlavour, dataObj, len);
  1372.       dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  1373.       if (!dataObj)
  1374.         continue;
  1375.       dataObj = dataObj.data.substring(0, len.value).split("\n");
  1376.       uri     = dataObj[0];
  1377.       if (dataObj.length > 1 && dataObj[1] != "")
  1378.         extra = dataObj[1];
  1379.       else
  1380.         extra = null;
  1381.       switch (bestFlavour.value) {
  1382.       case "moz/rdfitem":
  1383.         rSource = RDF.GetResource(uri);
  1384.         parent  = extra;
  1385.         break;
  1386.       case "text/x-moz-url":
  1387.       case "text/unicode":
  1388.         if (!BMSVC.IsBookmarked(uri)) {
  1389.           var charSet = aDragSession.sourceDocument ? 
  1390.                         aDragSession.sourceDocument.characterSet : null;
  1391.           rSource = BookmarksUtils.createBookmark(null, uri, charSet, extra);
  1392.         } else {
  1393.           rSource = RDF.GetResource(uri);
  1394.         }
  1395.         parent = null;
  1396.         break;
  1397.       }
  1398.       selection.item.push(rSource);
  1399.       if (parent)
  1400.         rParent = RDF.GetResource(parent);
  1401.       else
  1402.         rParent = null;
  1403.       selection.parent.push(rParent);
  1404.     }
  1405.     selection.length = selection.item.length;
  1406.     BookmarksUtils.checkSelection(selection);
  1407.     return selection;
  1408.   },
  1409.  
  1410.   getTargetFromSelection: function (aSelection, aOrientation)
  1411.   {
  1412.     var parent, index;
  1413.     var orientation = aOrientation !== undefined? aOrientation:
  1414.                       aSelection.isContainer[0]? BookmarksUtils.DROP_ON:BookmarksUtils.DROP_BEFORE;
  1415.     var item = aSelection.item[0];
  1416.     if (orientation == BookmarksUtils.DROP_ON) {
  1417.       parent = item;
  1418.       if (aSelection.protocol == "file" || aSelection.protocol == "find")
  1419.         parent = null;
  1420.     } else
  1421.       parent = aSelection.parent[0];
  1422.  
  1423.     if (!parent)
  1424.       // for file: or find: containers or children
  1425.       index = -1;
  1426.     else {
  1427.       RDFC.Init(BMDS, parent);
  1428.       if (orientation == BookmarksUtils.DROP_ON)
  1429.         index = parseInt(this.getProperty(parent, RDF_NS+"nextVal"));
  1430.       else {
  1431.         if (orientation != this.DROP_ON) {
  1432.           index = RDFC.IndexOf(item);
  1433.           if (orientation == this.DROP_AFTER)
  1434.             ++index;
  1435.         }
  1436.       }
  1437.     }
  1438.     var target = { parent: parent, index: index };
  1439.     return target;
  1440.   },
  1441.  
  1442.   getSelectionFromResource: function (aItem, aParent)
  1443.   {
  1444.     var selection    = {};
  1445.     selection.length = 1;
  1446.     selection.item   = [aItem  ];
  1447.     selection.parent = [aParent];
  1448.     this.checkSelection(selection);
  1449.     return selection;
  1450.   },
  1451.  
  1452.   createBookmark: function (aName, aURL, aCharSet, aDefaultName)
  1453.   {
  1454.     if (!aName) {
  1455.       // look up in the history ds to retrieve the name
  1456.       var rSource = RDF.GetResource(aURL);
  1457.       var HISTDS  = RDF.GetDataSource("rdf:history");
  1458.       var nameArc = RDF.GetResource(NC_NS+"Name");
  1459.       var rName   = HISTDS.GetTarget(rSource, nameArc, true);
  1460.       aName       = rName ? rName.QueryInterface(kRDFLITIID).Value : aDefaultName;
  1461.       if (!aName)
  1462.         aName = aURL;
  1463.     }
  1464.     if (!aCharSet) {
  1465.       var fw = document.commandDispatcher.focusedWindow;
  1466.       if (fw)
  1467.         aCharSet = fw.document.characterSet;
  1468.     }
  1469.     return BMSVC.createBookmark(aName, aURL, aCharSet);
  1470.   },
  1471.  
  1472.   flushDataSource: function ()
  1473.   {
  1474.     var remoteDS = BMDS.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  1475.     setTimeout(function () {dump("Flushing Bookmark Datasource...\n"); remoteDS.Flush()}, 100);
  1476.   },
  1477.  
  1478.   getTransactionManager: function ()
  1479.   {
  1480.     var windows = WINDOWSVC.getEnumerator(null);
  1481.     while(windows.hasMoreElements()) {
  1482.       var w = windows.getNext();
  1483.       if (w.gBMtxmgr)
  1484.         return w.gBMtxmgr;
  1485.     }
  1486.  
  1487.     // Create a TransactionManager object:
  1488.     dump("creating transaction manager...\n")
  1489.     gBMtxmgr = Components.classes["@mozilla.org/transactionmanager;1"].createInstance();
  1490.     // Now get the nsITransactionManager interface from the object:
  1491.     gBMtxmgr = gBMtxmgr.QueryInterface(Components.interfaces.nsITransactionManager);
  1492.     if (!gBMtxmgr) {
  1493.       dump("Failed to create the Bookmark Transaction Manager!\n");
  1494.       return null;
  1495.     }
  1496.     this.dispatchTransactionManager();
  1497.     return gBMtxmgr;
  1498.   },
  1499.  
  1500.   dispatchTransactionManager: function ()
  1501.   {
  1502.     var windows = WINDOWSVC.getEnumerator(null);
  1503.     while(windows.hasMoreElements()) {
  1504.       var w = windows.getNext();
  1505.       w.gBMtxmgr = gBMtxmgr;
  1506.     }
  1507.   },
  1508.  
  1509.   addBookmarkForTabBrowser: function( aTabBrowser, aSelect )
  1510.   {
  1511.     var tabsInfo = [];
  1512.     var currentTabInfo = { name: "", url: "", charset: null };
  1513.  
  1514.     const activeBrowser = aTabBrowser.selectedBrowser;
  1515.     const browsers = aTabBrowser.browsers;
  1516.     for (var i = 0; i < browsers.length; ++i) {
  1517.       var webNav = browsers[i].webNavigation;
  1518.       var url = webNav.currentURI.spec;
  1519.       var name = "";
  1520.       var charset;
  1521.       try {
  1522.         var doc = webNav.document;
  1523.         name = doc.title || url;
  1524.         charset = doc.characterSet;
  1525.       } catch (e) {
  1526.         name = url;
  1527.       }
  1528.  
  1529.       tabsInfo[i] = { name: name, url: url, charset: charset };
  1530.  
  1531.       if (browsers[i] == activeBrowser)
  1532.         currentTabInfo = tabsInfo[i];
  1533.     }
  1534.  
  1535.     openDialog("chrome://browser/content/bookmarks/addBookmark2.xul", "",
  1536.                "centerscreen,chrome,dialog=yes,resizable=no,dependent",
  1537.                currentTabInfo.name, currentTabInfo.url, null,
  1538.                currentTabInfo.charset, "addGroup" + (aSelect ? ",group" : ""), tabsInfo);
  1539.   },
  1540.  
  1541.   addBookmarkForBrowser: function (aDocShell, aShowDialog)
  1542.   {
  1543.     // Bug 52536: We obtain the URL and title from the nsIWebNavigation 
  1544.     //            associated with a <browser/> rather than from a DOMWindow.
  1545.     //            This is because when a full page plugin is loaded, there is
  1546.     //            no DOMWindow (?) but information about the loaded document
  1547.     //            may still be obtained from the webNavigation. 
  1548.     var url = aDocShell.currentURI.spec;
  1549.     var title, docCharset = null;
  1550.     try {
  1551.       title = aDocShell.document.title || url;
  1552.       docCharset = aDocShell.document.characterSet;
  1553.     }
  1554.     catch (e) {
  1555.       title = url;
  1556.     }
  1557.  
  1558.     openDialog("chrome://browser/content/bookmarks/addBookmark2.xul", "", 
  1559.                "centerscreen,chrome,dialog=yes,resizable=no,dependent", title,
  1560.                url, null, docCharset);
  1561.   }, 
  1562.  
  1563.   // should update the caller, aShowDialog is no more necessary
  1564.   addBookmark: function (aURL, aTitle, aCharSet, aShowDialog)                   
  1565.   {                                                                             
  1566.     openDialog("chrome://browser/content/bookmarks/addBookmark2.xul", "",     
  1567.                "centerscreen,chrome,dialog=yes,resizable=no,dependent", aTitle, aURL, null, aCharSet);
  1568.   },                                                                          
  1569.  
  1570.   getBrowserTargetFromEvent: function (aEvent)
  1571.   {
  1572.     var button = (aEvent.type == "command" || aEvent.type == "keypress") ? 0 :aEvent.button;
  1573.     if (button == 1)
  1574.       return PREF.getBoolPref("browser.tabs.opentabfor.middleclick")? "tab":"window"
  1575.     else if (aEvent.shiftKey)      
  1576.       return "window";
  1577.     else if (aEvent.ctrlKey)
  1578.       return "tab";
  1579.     else if (aEvent.altKey)
  1580.       return "save"
  1581.     else
  1582.       return "current";
  1583.   },
  1584.  
  1585.   loadBookmarkBrowserMiddleClick: function (aEvent, aDS)
  1586.   {
  1587.     if (aEvent.button != 1)
  1588.       return;
  1589.     // unlike for command events, we have to close the menus manually
  1590.     personalToolbarDNDObserver.mCurrentDragOverTarget = null;
  1591.     personalToolbarDNDObserver.onDragCloseTarget();
  1592.     this.loadBookmarkBrowser(aEvent, aEvent.originalTarget, aDS);
  1593.   },
  1594.  
  1595.   loadBookmarkBrowser: function (aEvent, aTarget, aDS)
  1596.   {
  1597.     var rSource   = RDF.GetResource(aTarget.id);
  1598.     var selection = BookmarksUtils.getSelectionFromResource(rSource);
  1599.     var browserTarget = BookmarksUtils.getBrowserTargetFromEvent(aEvent);
  1600.     BookmarksCommand.openBookmark(selection, browserTarget, aDS)
  1601.   }
  1602.  
  1603. }
  1604.  
  1605.  
  1606. function BookmarkInsertTransaction (aAction)
  1607. {
  1608.   this.wrappedJSObject = this;
  1609.   this.txmgr   = BookmarksUtils.getTransactionManager();
  1610.   this.type    = "insert";
  1611.   this.action  = aAction;
  1612.   this.item    = null;
  1613.   this.parent  = null;
  1614.   this.index   = null;
  1615.   this.isValid = null;
  1616. }
  1617.  
  1618. BookmarkInsertTransaction.prototype =
  1619. {
  1620.  
  1621.   isTransient: false,
  1622.   RDFC       : RDFC,
  1623.   BMDS       : BMDS,
  1624.  
  1625.   doTransaction: function ()
  1626.   {
  1627.     dump("do ")
  1628.     dumpTXN(this)
  1629.     for (var i=0; i<this.item.length; ++i) {
  1630.       if (this.isValid[i]) {
  1631.         this.RDFC.Init(this.BMDS, this.parent[i]);
  1632.         this.RDFC.InsertElementAt(this.item[i], this.index[i], true);
  1633.       }
  1634.     }
  1635.   },
  1636.  
  1637.   undoTransaction: function ()
  1638.   {
  1639.     dump("undo ")
  1640.     dumpTXN(this)
  1641.     for (var i=this.item.length-1; i>=0; i--) {
  1642.       if (this.isValid[i]) {
  1643.         this.RDFC.Init(this.BMDS, this.parent[i]);
  1644.         this.RDFC.RemoveElementAt(this.index[i], true);
  1645.       }
  1646.     }
  1647.   },
  1648.    
  1649.   redoTransaction: function ()
  1650.   {
  1651.     this.doTransaction();
  1652.   },
  1653.  
  1654.   merge               : function (aTransaction) {return false},
  1655.   QueryInterface      : function (aUID)         {return this},
  1656.   getHelperForLanguage: function (aCount)       {return null},
  1657.   getInterfaces       : function (aCount)       {return null},
  1658.   canCreateWrapper    : function (aIID)         {return "AllAccess"}
  1659.  
  1660. }
  1661.  
  1662. function BookmarkRemoveTransaction (aAction)
  1663. {
  1664.   this.wrappedJSObject = this;
  1665.   this.txmgr   = BookmarksUtils.getTransactionManager();
  1666.   this.type    = "remove";
  1667.   this.action  = aAction;
  1668.   this.item    = null;
  1669.   this.parent  = null;
  1670.   this.index   = null;
  1671.   this.isValid = null;
  1672. }
  1673.  
  1674. BookmarkRemoveTransaction.prototype =
  1675. {
  1676.  
  1677.   isTransient: false,
  1678.   RDFC       : RDFC,
  1679.   BMDS       : BMDS,
  1680.  
  1681.   doTransaction: function ()
  1682.   {
  1683.     dump("do ")
  1684.     dumpTXN(this)
  1685.     for (var i=0; i<this.item.length; ++i) {
  1686.       if (this.isValid[i]) {
  1687.         this.RDFC.Init(this.BMDS, this.parent[i]);
  1688.         this.RDFC.RemoveElementAt(this.index[i], false);
  1689.       }
  1690.     }
  1691.   },
  1692.  
  1693.   undoTransaction: function ()
  1694.   {
  1695.     dump("undo ")
  1696.     dumpTXN(this);
  1697.     for (var i=this.item.length-1; i>=0; i--) {
  1698.       if (this.isValid[i]) {
  1699.         this.RDFC.Init(this.BMDS, this.parent[i]);
  1700.         this.RDFC.InsertElementAt(this.item[i], this.index[i], false);
  1701.       }
  1702.     }
  1703.   },
  1704.    
  1705.   redoTransaction: function ()
  1706.   {
  1707.     this.doTransaction();
  1708.   },
  1709.  
  1710.   merge               : function (aTransaction) {return false},
  1711.   QueryInterface      : function (aUID)         {return this},
  1712.   getHelperForLanguage: function (aCount)       {return null},
  1713.   getInterfaces       : function (aCount)       {return null},
  1714.   canCreateWrapper    : function (aIID)         {return "AllAccess"}
  1715.  
  1716. }
  1717.  
  1718. function BookmarkMoveTransaction (aAction, aSelection, aTarget)
  1719. {
  1720.   this.wrappedJSObject = this;
  1721.   this.txmgr     = BookmarksUtils.getTransactionManager();
  1722.   this.type      = "move";
  1723.   this.action    = aAction;
  1724.   this.selection = aSelection;
  1725.   this.target    = aTarget;
  1726.   this.isValid   = aSelection.isValid;
  1727. }
  1728.  
  1729. BookmarkMoveTransaction.prototype =
  1730. {
  1731.  
  1732.   isTransient: false,
  1733.  
  1734.   doTransaction: function ()
  1735.   {
  1736.     BookmarksUtils.removeSelection("move", this.selection);
  1737.     BookmarksUtils.insertSelection("move", this.selection, this.target);
  1738.   },
  1739.  
  1740.   undoTransaction     : function () {},
  1741.   redoTransaction     : function () {},
  1742.   merge               : function (aTransaction) {return false},
  1743.   QueryInterface      : function (aUID)         {return this},
  1744.   getHelperForLanguage: function (aCount)       {return null},
  1745.   getInterfaces       : function (aCount)       {return null},
  1746.   canCreateWrapper    : function (aIID)         {return "AllAccess"}
  1747.  
  1748. }
  1749.  
  1750. function BookmarkImportTransaction (aAction)
  1751. {
  1752.   this.wrappedJSObject = this;
  1753.   this.txmgr   = BookmarksUtils.getTransactionManager();
  1754.   this.type    = "import";
  1755.   this.action  = aAction;
  1756.   this.item    = [];
  1757.   this.parent  = [];
  1758.   this.index   = [];
  1759.   this.isValid = [];
  1760. }
  1761.  
  1762. BookmarkImportTransaction.prototype =
  1763. {
  1764.  
  1765.   isTransient: false,
  1766.   RDFC       : RDFC,
  1767.   BMDS       : BMDS,
  1768.  
  1769.   doTransaction: function ()
  1770.   {
  1771.   },
  1772.  
  1773.   undoTransaction: function ()
  1774.   {
  1775.     dump("undo ")
  1776.     dumpTXN(this)
  1777.     for (var i=this.item.length-1; i>=0; i--) {
  1778.       if (this.isValid[i]) {
  1779.         this.RDFC.Init(this.BMDS, this.parent[i]);
  1780.         this.RDFC.RemoveElementAt(this.index[i], true);
  1781.       }
  1782.     }
  1783.   },
  1784.    
  1785.   redoTransaction: function ()
  1786.   {
  1787.     for (var i=0; i<this.item.length; ++i) {
  1788.       if (this.isValid[i]) {
  1789.         this.RDFC.Init(this.BMDS, this.parent[i]);
  1790.         this.RDFC.InsertElementAt(this.item[i], this.index[i], true);
  1791.       }
  1792.     }
  1793.   },
  1794.  
  1795.   merge               : function (aTransaction) {return false},
  1796.   QueryInterface      : function (aUID)         {return this},
  1797.   getHelperForLanguage: function (aCount)       {return null},
  1798.   getInterfaces       : function (aCount)       {return null},
  1799.   canCreateWrapper    : function (aIID)         {return "AllAccess"}
  1800.  
  1801. }
  1802.  
  1803. var BookmarkMenuTransactionListener =
  1804. {
  1805.  
  1806.   didDo: function (aTxmgr, aTxn)
  1807.   {
  1808.     this.updateMenuItem(aTxmgr, aTxn);
  1809.   },
  1810.  
  1811.   didUndo: function (aTxmgr, aTxn)
  1812.   {
  1813.     this.updateMenuItem(aTxmgr, aTxn);
  1814.   },
  1815.  
  1816.   didRedo: function (aTxmgr, aTxn)
  1817.   {
  1818.     this.updateMenuItem(aTxmgr, aTxn);
  1819.   },
  1820.  
  1821.   didMerge       : function (aTxmgr, aTxn) {},
  1822.   didBeginBatch  : function (aTxmgr, aTxn) {},
  1823.   didEndBatch    : function (aTxmgr, aTxn) {},
  1824.   willDo         : function (aTxmgr, aTxn) {},
  1825.   willUndo       : function (aTxmgr, aTxn) {},
  1826.   willRedo       : function (aTxmgr, aTxn) {},
  1827.   willMerge      : function (aTxmgr, aTxn) {},
  1828.   willBeginBatch : function (aTxmgr, aTxn) {},
  1829.   willEndBatch   : function (aTxmgr, aTxn) {},
  1830.  
  1831.   updateMenuItem: function (aTxmgr, aTxn) {
  1832.     if (aTxn) {
  1833.       aTxn = aTxn.wrappedJSObject;
  1834.       if ((aTxn.type == "remove" || aTxn.type == "insert") && aTxn.action == "move")
  1835.       return;
  1836.     }
  1837.     var node, transactionNumber, transactionList, transactionLabel, action;
  1838.     node = document.getElementById("cmd_undo");
  1839.     transactionNumber = aTxmgr.numberOfUndoItems;
  1840.     dump("N UNDO: "+transactionNumber+"\n")
  1841.     if (transactionNumber == 0) {
  1842.       node.setAttribute("disabled", "true");
  1843.       transactionLabel = BookmarksUtils.getLocaleString("cmd_bm_undo");
  1844.     } else {
  1845.       node.removeAttribute("disabled");
  1846.       transactionList  = aTxmgr.getUndoList();
  1847.       action           = transactionList.getItem(transactionNumber-1).wrappedJSObject.action;
  1848.       transactionLabel = BookmarksUtils.getLocaleString("cmd_bm_"+action+"_undo")
  1849.     }
  1850.     node.setAttribute("label", transactionLabel);
  1851.       
  1852.     node = document.getElementById("cmd_redo");
  1853.     transactionNumber = aTxmgr.numberOfRedoItems;
  1854.     dump("N REDO: "+transactionNumber+"\n")
  1855.     if (transactionNumber == 0) {
  1856.       node.setAttribute("disabled", "true");
  1857.       transactionLabel = BookmarksUtils.getLocaleString("cmd_bm_redo");
  1858.     } else {
  1859.       node.removeAttribute("disabled");
  1860.       transactionList  = aTxmgr.getRedoList();
  1861.       action           = transactionList.getItem(transactionNumber-1).wrappedJSObject.action;
  1862.       transactionLabel = BookmarksUtils.getLocaleString("cmd_bm_"+action+"_redo")
  1863.     }
  1864.     node.setAttribute("label", transactionLabel);
  1865.   }
  1866.  
  1867. }
  1868.  
  1869. function dumpOBJ (aObj) 
  1870. {
  1871.   for (var i in aObj)
  1872.     dump("*** aObj[" + i + "] = " + aObj[i] + "\n");
  1873. }
  1874.  
  1875. function dumpRDF (aDS, aRDFNode)
  1876. {
  1877.   dumpRDFout(aDS, aRDFNode);
  1878.   dump("\n");
  1879.   dumpRDFin (aDS, aRDFNode);
  1880. }
  1881.  
  1882. function dumpRDFout (aDS, aRDFNode)
  1883. {
  1884.   dump("Arcs Out for "+aRDFNode.Value+"\n");
  1885.   var arcsout=aDS.ArcLabelsOut(aRDFNode);
  1886.   while (arcsout.hasMoreElements()) {
  1887.     var arc = arcsout.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1888.     var targets = aDS.GetTargets(aRDFNode, arc, true);
  1889.     while (targets.hasMoreElements()) {
  1890.       var target = targets.getNext();
  1891.       try {
  1892.         target = target.QueryInterface(Components.interfaces.nsIRDFResource).Value;
  1893.       } catch (e) {
  1894.         try {
  1895.           target = target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1896.         } catch (e) {}
  1897.       }
  1898.       dump("=>"+arc.Value+"=>"+target+"\n");
  1899.     }
  1900.   }
  1901. }
  1902. function dumpRDFin (aDS, aRDFNode)
  1903. {
  1904.   dump("Arcs In for "+aRDFNode.Value+"\n");
  1905.   var arcs=aDS.ArcLabelsIn(aRDFNode);
  1906.   while (arcs.hasMoreElements()) {
  1907.     var arc = arcs.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1908.     var sources = aDS.GetSources(arc, aRDFNode, true);
  1909.     while (sources.hasMoreElements()) {
  1910.       var source = sources.getNext();
  1911.       try {
  1912.         source = source.QueryInterface(Components.interfaces.nsIRDFResource).Value;
  1913.       } catch (e) {
  1914.         try {
  1915.           source = source.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1916.         } catch (e) {}
  1917.       }
  1918.       dump("<="+arc.Value+"<="+source+"\n");
  1919.     }
  1920.   }
  1921. }
  1922.  
  1923. function dumpTXN(aTxn)
  1924. {
  1925.   aTxn = aTxn.wrappedJSObject;
  1926.   dump(aTxn.type+", "+aTxn.action+"\n");
  1927.   if (aTxn.type == "insert" || aTxn.type == "remove") {
  1928.     for (var i=0; i<aTxn.item.length; ++i) {
  1929.       if (aTxn.isValid[i])
  1930.         dump(i+": "+aTxn.item[i].Value+" in "+BookmarksUtils.getProperty(aTxn.parent[i], NC_NS+"Name")+", i:"+aTxn.index[i]+"\n");
  1931.     }
  1932.   }
  1933. }
  1934.   
  1935.