home *** CD-ROM | disk | FTP | other *** search
/ ftp.swcp.com / ftp.swcp.com.zip / ftp.swcp.com / mac / mozilla-macos9-1.3.1.sea.bin / Mozilla1.3.1 / Chrome / comm.jar / content / communicator / bookmarks / bookmarksDD.js < prev    next >
Text File  |  2003-06-08  |  15KB  |  398 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is 
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 1998
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Ben Goodger <ben@netscape.com> (Original Author, v2.0)
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the NPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the NPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. var NC_NS  = "http://home.netscape.com/NC-rdf#";
  40. var RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  41.  
  42. var gSpringLoadTracker = {
  43.   timeout: 0,
  44.   element: null,
  45.   
  46.   open: function (aRDFNode) 
  47.   {
  48.     if (this.element)
  49.       this.element.setAttribute("open", "true");
  50.     clearTimeout(this.timeout);
  51.   }
  52. };
  53.  
  54. var bookmarksDNDObserver = {
  55.   _RDF: null,
  56.   get RDF ()
  57.   {
  58.     if (!this._RDF) {
  59.       const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
  60.       const kRDFIID = Components.interfaces.nsIRDFService;
  61.       this._RDF = Components.classes[kRDFContractID].getService(kRDFIID);
  62.     }
  63.     return this._RDF;
  64.   },
  65.  
  66.   // XXX I belong somewhere shared. 
  67.   getResource: function(aString)
  68.   {
  69.     return this.RDF.GetResource(aString, true);
  70.   },
  71.   
  72.   getTarget: function(aDS, aSourceID, aPropertyID)
  73.   {
  74.     var source = this.getResource(aSourceID);
  75.     var property = this.getResource(aPropertyID);
  76.     return aDS.GetTarget(source, property, true);
  77.   },
  78.   
  79.   onDragStart: function (aEvent, aXferData, aDragAction)
  80.   {
  81.     var bookmarksTree = document.getElementById("bookmarksTree");
  82.     if (aEvent.target == bookmarksTree || aEvent.target.localName == "treechildren" || 
  83.         aEvent.target.localName == "splitter" || aEvent.target.localName == "menu")
  84.       throw Components.results.NS_OK; // not a draggable item.
  85.     if (aEvent.target.parentNode && aEvent.target.parentNode.parentNode &&
  86.         aEvent.target.parentNode.parentNode.localName == "treehead")
  87.       throw Components.results.NS_OK; // don't drag treehead cells.
  88.     if (bookmarksTree.getAttribute("sortActive") == "true")
  89.       throw Components.results.NS_OK; 
  90.           
  91.     var selItems = null;
  92.     if (bookmarksTree.selectedItems.length <= 0)
  93.       selItems = [aEvent.target.parentNode.parentNode];
  94.     else 
  95.       selItems = bookmarksTree.selectedItems;
  96.     aXferData.data = new TransferDataSet();
  97.     for (var i = 0; i < selItems.length; ++i) {
  98.       var currItem = selItems[i];
  99.       var currURI = NODE_ID(currItem);
  100.     
  101.       var parentItem = currItem.parentNode.parentNode;
  102.       var parentURI = NODE_ID(parentItem);
  103.     
  104.       var type = this.getTarget(bookmarksTree.database, currURI, RDF_NS + "type");
  105.       type = type.QueryInterface(Components.interfaces.nsIRDFResource).Value;
  106.       if (!type || (type != (NC_NS + "BookmarkSeparator") && 
  107.                     type != (NC_NS + "Bookmark") && 
  108.                     type != (NC_NS + "Folder"))) 
  109.         throw Components.results.NS_OK;
  110.       var name = this.getTarget(bookmarksTree.database, currURI, NC_NS + "Name");        
  111.       var data = new TransferData();
  112.       if (name) {
  113.         name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  114.         data.addDataForFlavour("text/x-moz-url", currURI + "\n" + name);
  115.       }
  116.       else {
  117.         data.addDataForFlavour("text/x-moz-url", currURI);
  118.       }
  119.       data.addDataForFlavour("moz/rdfitem", currURI + "\n" + parentURI);
  120.  
  121.       data.addDataForFlavour("text/unicode", currURI);
  122.       aXferData.data.push(data);
  123.     }
  124.  
  125.     if (aEvent.ctrlKey) {
  126.       const kDSIID = Components.interfaces.nsIDragService;
  127.       aDragAction.action = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
  128.     }
  129.   },
  130.   
  131.   onDragOver: function (aEvent, aFlavour, aDragSession)
  132.   {
  133.     var bookmarksTree = document.getElementById("bookmarksTree");
  134.     var rowGroup = aEvent.target.parentNode.parentNode;
  135.     if (rowGroup)
  136.       rowGroup.setAttribute("dd-triggerrepaint" + 
  137.                             (bookmarksTree.getAttribute("sortActive") == "true" ? "sorted" : ""), 0);
  138.     
  139.     var rdfNode = gBookmarksShell.findRDFNode(aEvent.target, true);
  140.     var rdfParent = rdfNode.parentNode.parentNode;
  141.     var isContainer = false;
  142.     if (rdfParent && rdfParent.getAttribute("container") == "true") {
  143.       var rDragOverContainer = this.RDF.GetResource(NODE_ID(rdfParent));
  144.  
  145.       const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
  146.       const kRDFCUtilsContractID = "@mozilla.org/rdf/container-utils;1";
  147.       const kRDFCUtilsIID = Components.interfaces.nsIRDFContainerUtils;
  148.       const kRDFCUtils = Components.classes[kRDFCUtilsContractID].getService(kRDFCUtilsIID);
  149.       isContainer = kRDFCUtils.IsContainer(kBMDS, rDragOverContainer);
  150.     }     
  151.     
  152.     if (!isContainer || rowGroup.id == "headRow") {
  153.       // Not a container, or dropping onto something that isn't designed to take drops 
  154.       // (e.g. the tree header) 
  155.       aDragSession.canDrop = false;
  156.       return;
  157.     }        
  158.     
  159.     // Springloaded folders.
  160.     /* XXX - not yet. 
  161.     if (rdfNode && rdfNode.getAttribute("container") == "true" &&
  162.         rdfNode.getAttribute("open") != "true") {
  163.       if (!gSpringLoadTracker.element || gSpringLoadTracker.element.id != rdfNode.id) {
  164.         // XXX - this is not good enough. We need to keep track of nesting and close up
  165.         // folders after the user has dragged out of them otherwise we end up with 
  166.         // everything open and a big mess!
  167.         if (gSpringLoadTracker.timeout)
  168.           clearTimeout(gSpringLoadTracker.timeout);
  169.         gSpringLoadTracker.element = rdfNode;
  170.         gSpringLoadTracker.timeout = setTimeout("gSpringLoadTracker.open()", 100);
  171.       }
  172.     }
  173.     */
  174.   },  
  175.   
  176.   _flavourSet: null,
  177.   getSupportedFlavours: function ()
  178.   {
  179.     if (!this._flavourSet) {
  180.       this._flavourSet = new FlavourSet();
  181.       this._flavourSet.appendFlavour("moz/rdfitem");
  182.       this._flavourSet.appendFlavour("text/x-moz-url");
  183.       this._flavourSet.appendFlavour("text/unicode");
  184.     }
  185.     return this._flavourSet;
  186.   },
  187.   
  188.   canHandleMultipleItems: true,
  189.   
  190.   onDrop: function (aEvent, aXferData, aDragSession)
  191.   {
  192.     var bookmarksTree = document.getElementById("bookmarksTree");
  193.     // XXX lame
  194.     if (bookmarksTree.getAttribute("sortActive") == "true") return;
  195.  
  196.     const kRDFCContractID = "@mozilla.org/rdf/container;1";
  197.     const kRDFIID = Components.interfaces.nsIRDFContainer;
  198.     var RDFC = Components.classes[kRDFCContractID].getService(kRDFIID);
  199.     
  200.     const kBMDS = this.RDF.GetDataSource("rdf:bookmarks");
  201.     
  202.     var dropItem = aEvent.target.parentNode.parentNode;
  203.     if (aEvent.target.localName == "treechildren") 
  204.       dropItem = aEvent.target.parentNode; // handle drop on blank space. 
  205.  
  206.     // In the default view, the root node is the NC root, and we don't want to append
  207.     // to that. Adjust accordingly...
  208.     if (NODE_ID(dropItem) == "NC:NavCenter")
  209.       dropItem = document.getElementById("treechildren-bookmarks").firstChild;
  210.     
  211.     // Don't allow drops on the header row & prevent catastrophe
  212.     if (dropItem.id == "headRow" || !dropItem) return;
  213.       
  214.     // XXX we could probably compute this ourselves, but let the tree do this 
  215.     //     automagically for now.
  216.     var dropBefore = dropItem.getAttribute("dd-droplocation");
  217.     var dropOn = dropItem.getAttribute("dd-dropon");
  218.  
  219.     var dropAction = dropBefore == "true" ? "before" : dropOn == "true" ? "on" : "after";
  220.     if (aEvent.target.localName == "treechildren")  
  221.       dropAction = "on"; // handle drop on blank space. 
  222.     var containerItem = dropAction == "on" ? dropItem : dropItem.parentNode.parentNode;
  223.  
  224.       // XXX magical fix for bug # 33546: handle dropping after open container
  225.     if (dropItem.getAttribute("container") && dropItem.getAttribute("open") &&
  226.         dropAction == "after") {    
  227.       dropAction = "before";
  228.       containerItem = dropItem;
  229.  
  230.       dropItem = null;
  231.       for (var i = 0; i < containerItem.childNodes.length; ++i) {
  232.         if (containerItem.childNodes[i].localName == "treechildren") {
  233.           dropItem = containerItem.childNodes[i].firstChild;
  234.           break;
  235.         }
  236.       }
  237.       if (!dropItem) {
  238.         dropAction = "on";
  239.         dropItem = containerItem.parentNode.parentNode;
  240.       }
  241.     }
  242.     
  243.     var rTarget = this.getResource(NODE_ID(dropItem));
  244.     var rContainer = this.getResource(NODE_ID(containerItem));    
  245.  
  246.     const kRDFCUtilsContractID = "@mozilla.org/rdf/container-utils;1";
  247.     const kRDFCUtilsIID = Components.interfaces.nsIRDFContainerUtils;
  248.     const kRDFCUtils = Components.classes[kRDFCUtilsContractID].getService(kRDFCUtilsIID);
  249.     var isContainer = kRDFCUtils.IsContainer(kBMDS, rContainer);
  250.  
  251.     // XXX
  252.     var rType = this.getResource(RDF_NS + "type");
  253.     var rBookmark = this.getResource(NC_NS + "Bookmark");
  254.     
  255.     var dirty = false;
  256.     var additiveFlag = false;
  257.     var numObjects = aXferData.dataList.length;
  258.     /*
  259.     if (numObjects > 1) {
  260.       var bo = bookmarksTree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);
  261.       bo.beginBatch();
  262.     }
  263.     */
  264.  
  265.     var sourceID = [], parentID = [], nameRequired = [], name = [];
  266.     var flavourData;
  267.     for (i = 0; i < numObjects; ++i) {
  268.       flavourData = aXferData.dataList[i].first;
  269.       nameRequired[i] = false;
  270.       name[i] = null;
  271.       var data = flavourData.data;
  272.       switch (flavourData.flavour.contentType) {
  273.       case "moz/rdfitem":
  274.         var ix = data.indexOf("\n");
  275.         sourceID[i] = ix >= 0 ? (parentID[i] = data.substr(ix+1), data.substr(0, ix)) : data;
  276.         break;
  277.       case "text/x-moz-url":
  278.         ix = data.indexOf("\n");
  279.         sourceID[i] = ix >= 0 ? (name[i] = data.substr(ix+1), data.substr(0, ix)) : data;
  280.         break;
  281.       case "text/unicode":
  282.         sourceID[i] = data;
  283.         nameRequired[i] = true;
  284.         break;
  285.       default: 
  286.         continue;
  287.       }
  288.       
  289.       var rSource = this.getResource(sourceID[i]);
  290.       var rParent = parentID[i] ? this.getResource(parentID[i]) : null;
  291.  
  292.       // Prevent dropping node on itself, before or after itself, on its parent 
  293.       // container, or a weird situation when an open container is dropped into
  294.       // itself (which results in data loss!).
  295.       // Also prevent dropping into a folder that isn't actually a container 
  296.       // (and is thus probably a pseudo-container from an aggregated datasource,
  297.       //  see bug 68656 fir details).
  298.       if (rSource == rTarget || (dropAction == "on" && rContainer == rParent) ||
  299.           rContainer == rSource || !isContainer)
  300.         return;
  301.  
  302.       // Prevent dropping node into one of its own subfolders
  303.       var dropItem2 = dropItem;
  304.       do {
  305.         var targetAncestor = NODE_ID(dropItem2);
  306.         dropItem2 = dropItem2.parentNode;
  307.       } while (targetAncestor != "NC:BookmarksRoot" && targetAncestor != sourceID[i]);
  308.       if (targetAncestor == sourceID[i]) {
  309.         return;
  310.       }
  311.     }
  312.  
  313.     for (i = 0; i < numObjects; ++i) {
  314.       flavourData = aXferData.dataList[i].first;
  315.       rSource = this.getResource(sourceID[i]);
  316.       rParent = parentID[i] ? this.getResource(parentID[i]) : null;
  317.  
  318.       // XXX if any of the following fails, the nodes are gone for good!
  319.       const kDSIID = Components.interfaces.nsIDragService;
  320.       const kCopyAction = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
  321.       if (rParent) {
  322.         if (!(aDragSession.dragAction & kCopyAction)) {
  323.           try {
  324.             RDFC.Init(kBMDS, rParent);
  325.             ix = RDFC.IndexOf(rSource);
  326.             if (ix >= 1) 
  327.               RDFC.RemoveElementAt(ix, true);
  328.           }
  329.           catch (ex) { }
  330.         }
  331.       }
  332.  
  333.       RDFC.Init(kBMDS, rContainer);
  334.       
  335.       // If this item already exists in this container, don't paste, as 
  336.       // this will result in the creation of multiple copies in the datasource
  337.       // but will not result in an update of the UI. (In Short: we don't
  338.       // handle multiple bookmarks well)
  339.       ix = RDFC.IndexOf(rSource);
  340.       if (ix != -1)
  341.         continue;
  342.       
  343.       var bmType = this.getTarget(bookmarksTree.database, sourceID[i], RDF_NS + "type");
  344.       if (!bmType) 
  345.         kBMDS.Assert(rSource, rType, rBookmark, true);
  346.       if (bmType == NC_NS + "Folder") {
  347.         // If we're going to copy a folder type, we need to clone the folder 
  348.         // rather than just asserting the new node as a child of the drop folder.
  349.         if (aDragSession.dragAction & kCopyAction)
  350.           rSource = BookmarksUtils.cloneFolder(rSource, rContainer, rTarget);
  351.       }
  352.       
  353.       if (dropAction == "before" || dropAction == "after") {
  354.         var dropIx = RDFC.IndexOf(rTarget);
  355.         RDFC.InsertElementAt(rSource, dropAction == "after" ? ++dropIx : dropIx, true);
  356.       }
  357.       else
  358.         RDFC.AppendElement(rSource); // drop on
  359.  
  360.       dirty = true;
  361.  
  362.       if (rParent) {
  363.         gBookmarksShell.selectFolderItem(rContainer.Value, sourceID[i], additiveFlag);
  364.         if (!additiveFlag) additiveFlag = true;
  365.       }
  366.  
  367.       // If a name is supplied, we want to assert this information into the 
  368.       // graph. E.g. user drags an internet shortcut to the app, we want to 
  369.       // preserve not only the URL but the name of the shortcut. The other case
  370.       // where we need to assert a name is when the node does not already exist
  371.       // in the graph, in this case we'll just use the URL as the name.
  372.       if (name[i] || nameRequired[i]) {
  373.         var currentName = this.getTarget(bookmarksTree.database, sourceID[i], NC_NS + "Name");
  374.         if (!currentName) {
  375.           var rDefaultName = this.RDF.GetLiteral(name[i] || sourceID[i]);
  376.           if (rDefaultName) {
  377.             var rName = this.RDF.GetResource(NC_NS + "Name");
  378.             kBMDS.Assert(rSource, rName, rDefaultName, true);
  379.           }
  380.         }
  381.       }
  382.     }
  383.  
  384.     /*
  385.     if (numObjects > 1) {
  386.       var bo = bookmarksTree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);
  387.       bo.endBatch();
  388.     }
  389.     */
  390.     
  391.     if (dirty) {
  392.       var remoteDS = kBMDS.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  393.       remoteDS.Flush();
  394.     }    
  395.   }
  396. }
  397.  
  398.