home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2005 October / Gamestar_77_2005-10_dvd.iso / Programy / nsb-install-8-0.exe / chrome / browser.jar / content / browser / bookmarks / bookmarksMenu.js < prev    next >
Encoding:
Text File  |  2005-07-29  |  35.7 KB  |  1,099 lines

  1.  
  2. var BookmarksMenu = {
  3.   _selection:null,
  4.   _target:null,
  5.   _orientation:null,
  6.   currentSelection : null,
  7.   currentTarget:null,
  8.   currentDS:null,
  9.  
  10.   /////////////////////////////////////////////////////////////////////////////
  11.   // prepare the bookmarks menu for display
  12.   onShowMenu: function (aTarget)
  13.   {
  14.     this.showOpenInTabsMenuItem(aTarget);
  15.     this.showEmptyItem(aTarget);
  16.   },
  17.  
  18.   onSelectBookmark: function (aEvent, aTarget, aDS)
  19.   {  
  20.      this.currentSelection = null;
  21.      this.currentTarget = null;
  22.      this.currentDS = null;
  23.        
  24.      if (document.popupNode && document.popupNode.open)
  25.       document.popupNode.lastChild.hidePopup();
  26.  
  27.      //_content.focus(); //JMC - Maybe will fix failure to repaint background in trident?
  28.      // BLT# 143389
  29.  
  30.      if (aTarget.getAttribute("class") == "openintabs-menuitem")
  31.       aTarget = aTarget.parentNode.parentNode;
  32.  
  33.      if (!this.isBTBookmark(aTarget.id))
  34.       return;
  35.      var rSource   = RDF.GetResource(aTarget.id);
  36.      var selection = BookmarksUtils.getSelectionFromResource(rSource);
  37.      var browserTarget = whereToOpenLink(aEvent);       
  38.      
  39.      this.currentSelection = selection;
  40.      this.currentTarget = browserTarget;
  41.      this.currentDS = aDS;
  42.           
  43.      setTimeout('BookmarksCommand.openBookmark(BookmarksMenu.currentSelection, BookmarksMenu.currentTarget, BookmarksMenu.currentDS);', 0);
  44.   },
  45.   /////////////////////////////////////////////////////////////////////////////
  46.   // remove arbitary elements created in this.onShowMenu()
  47.   onHideMenu: function (aTarget)
  48.   {
  49.     this.hideOpenInTabsMenuItem(aTarget);
  50.     this.hideEmptyItem(aTarget);
  51.   },
  52.  
  53.   /////////////////////////////////////////////////////////////////////////////
  54.   // shows the 'Open in Tabs' menu item if validOpenInTabsMenuItem is true -->
  55.   showOpenInTabsMenuItem: function (aTarget)
  56.   {
  57.     if (!this.validOpenInTabsMenuItem(aTarget) ||
  58.         aTarget.lastChild.getAttribute("class") == "openintabs-menuitem")
  59.       return; 
  60.     var element = document.createElementNS(XUL_NS, "menuseparator");
  61.     element.setAttribute("class", "openintabs-menuseparator");
  62.     aTarget.appendChild(element);
  63.     element = document.createElementNS(XUL_NS, "menuitem");
  64.     element.setAttribute("class", "openintabs-menuitem");
  65.     element.setAttribute("label", BookmarksUtils.getLocaleString("cmd_bm_openfolder"));
  66.     element.setAttribute("accesskey", BookmarksUtils.getLocaleString("cmd_bm_openfolder_accesskey"));
  67.     aTarget.appendChild(element);
  68.   },
  69.  
  70.   realHideOpenInTabsMenuItem: function (aParent)
  71.   {
  72.        
  73.     if (!aParent.hasChildNodes())
  74.       return;
  75.     child = aParent.lastChild;
  76.     var removed = 0;
  77.     while (child) {
  78.       var cclass = child.getAttribute("class");
  79.       if (cclass == "openintabs-menuitem" || cclass == "openintabs-menuseparator") {
  80.         var prevchild = child.previousSibling;
  81.         aParent.removeChild(child);
  82.         child = prevchild;
  83.         removed++;
  84.         if (removed == 2)
  85.           break;
  86.       } else {
  87.         child = child.previousSibling;
  88.       }
  89.     }
  90.   },
  91.  
  92.   /////////////////////////////////////////////////////////////////////////////
  93.   // hides the 'Open in Tabs' on popuphidden so that we won't duplicate it -->
  94.   hideOpenInTabsMenuItem: function (aTarget)
  95.   {
  96.     BookmarksMenu.realHideOpenInTabsMenuItem(aTarget);
  97.   },
  98.  
  99.   /////////////////////////////////////////////////////////////////////////////
  100.   // returns false if...
  101.   // - the parent is the bookmark menu or the chevron
  102.   // - the menupopup contains ony one bookmark
  103.   validOpenInTabsMenuItem: function (aTarget)
  104.   {
  105.        
  106.     var rParent = RDF.GetResource(aTarget.parentNode.id)
  107.     var type = BookmarksUtils.resolveType(rParent);
  108.     if (type != "Folder" && type != "PersonalToolbarFolder" && type != "Livemark")
  109.       return false;
  110.     var count = 0;
  111.     if (!aTarget.hasChildNodes())
  112.       return false;
  113.     var curr = aTarget.firstChild;
  114.     do {
  115.       type = BookmarksUtils.resolveType(curr.id);
  116.       if (type == "Bookmark" && ++count == 2)
  117.         return true;
  118.       curr = curr.nextSibling;
  119.     } while (curr);
  120.     return false;
  121.   },
  122.  
  123.   /////////////////////////////////////////////////////////////////////////////
  124.   // show an empty item if the menu is empty
  125.   showEmptyItem: function (aTarget)
  126.   {
  127.     if(aTarget.hasChildNodes())
  128.       return;
  129.  
  130.     var EmptyMsg = BookmarksUtils.getLocaleString("emptyFolder");
  131.     var emptyElement = document.createElementNS(XUL_NS, "menuitem");
  132.     emptyElement.setAttribute("id", "empty-menuitem");
  133.     emptyElement.setAttribute("label", EmptyMsg);
  134.     emptyElement.setAttribute("disabled", "true");
  135.  
  136.     aTarget.appendChild(emptyElement);
  137.   },
  138.  
  139.   /////////////////////////////////////////////////////////////////////////////
  140.   // remove the empty element
  141.   hideEmptyItem: function (aTarget)
  142.   {
  143.        
  144.     if (!aTarget.hasChildNodes())
  145.       return;
  146.  
  147.     // if the user drags to the menu while it's open (i.e. on the toolbar),
  148.     // the bookmark gets added either before or after the Empty menu item
  149.     // before the menu is hidden.  So we need to test both first and last.
  150.     if (aTarget.firstChild.id == "empty-menuitem")
  151.       aTarget.removeChild(aTarget.firstChild);
  152.     else if (aTarget.lastChild.id == "empty-menuitem")
  153.       aTarget.removeChild(aTarget.lastChild);
  154.   },
  155.  
  156.   //////////////////////////////////////////////////////////////////////////
  157.   // Fill a context menu popup with menuitems appropriate for the current
  158.   // selection.
  159.   createContextMenu: function (aEvent)
  160.   {
  161.        
  162.     var target = document.popupNode;
  163.  
  164.     if (!this.isBTBookmark(target.id)) {
  165.       target.removeAttribute("open");
  166.       return false;
  167.     }
  168.  
  169.     var targettype = BookmarksUtils.resolveType(target.id);
  170.  
  171.     if (targettype == "ImmutableFolder") {
  172.       // no context; see bug#... (popups getting stuck because "open"
  173.       // attribute doesn't get removed)
  174.       target.removeAttribute("open");
  175.       return false;
  176.     }
  177.  
  178.     var bt = document.getElementById("bookmarks-ptf");
  179.     bt.focus(); // buttons in the bt have -moz-user-focus: ignore
  180.  
  181.     this._selection   = this.getBTSelection(target);
  182.     this._orientation = this.getBTOrientation(aEvent, target);
  183.     if (targettype != "ImmutableBookmark")
  184.       this._target = this.getBTTarget(target, this._orientation);
  185.  
  186.     // walk up the tree until we find a database node
  187.     var p = target;
  188.     while (p && !p.database)
  189.       p = p.parentNode;
  190.     if (p)
  191.       this._db = p.database;
  192.  
  193.     BookmarksCommand.createContextMenu(aEvent, this._selection, this._db);
  194.     this.onCommandUpdate();
  195.     aEvent.target.addEventListener("mousemove", BookmarksMenuController.onMouseMove, false);
  196.     return true;
  197.   },
  198.  
  199.   /////////////////////////////////////////////////////////////////////////
  200.   // Clean up after closing the context menu popup
  201.   destroyContextMenu: function (aEvent)
  202.   {
  203.        
  204.     if (content)
  205.       content.focus();
  206.     // XXXpch: see bug 210910, it should be done properly in the backend
  207.     BookmarksMenuDNDObserver.mCurrentDragOverTarget = null;
  208.     BookmarksMenuDNDObserver.onDragCloseTarget();
  209.  
  210.     BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
  211.     aEvent.target.removeEventListener("mousemove", BookmarksMenuController.onMouseMove, false)
  212.   },
  213.  
  214.   /////////////////////////////////////////////////////////////////////////////
  215.   // returns the formatted selection from aNode
  216.   getBTSelection: function (aNode)
  217.   {
  218.        
  219.     var item;
  220.     switch (aNode.id) {
  221.     case "bookmarks-ptf":
  222.       item = BMSVC.getBookmarksToolbarFolder().Value;
  223.       break;
  224.     case "bookmarks-menu":
  225.       item = "NC:BookmarksRoot";
  226.       break;
  227.     default:
  228.       item = aNode.id;
  229.       if (!this.isBTBookmark(item))
  230.         return {length:0};
  231.     }
  232.     var parent           = this.getBTContainer(aNode);
  233.     var isExpanded       = aNode.hasAttribute("open") && aNode.open;
  234.     var selection        = {};
  235.     selection.item       = [RDF.GetResource(item)];
  236.     selection.parent     = [RDF.GetResource(parent)];
  237.     selection.isExpanded = [isExpanded];
  238.     selection.length     = selection.item.length;
  239.     BookmarksUtils.checkSelection(selection);
  240.     return selection;
  241.   },
  242.  
  243.   /////////////////////////////////////////////////////////////////////////
  244.   // returns the insertion target from aNode
  245.   getBTTarget: function (aNode, aOrientation)
  246.   {
  247.     var item, parent, index;
  248.     switch (aNode.id) {
  249.     case "bookmarks-ptf":
  250.       parent = BMSVC.getBookmarksToolbarFolder().Value;
  251.       item = BookmarksToolbar.getLastVisibleBookmark();
  252.       break;
  253.     case "bookmarks-menu":
  254.       parent = "NC:BookmarksRoot";
  255.       break;
  256.     case "bookmarks-chevron":
  257.       parent = BMSVC.getBookmarksToolbarFolder().Value;
  258.       break;
  259.     default:
  260.       if (aOrientation == BookmarksUtils.DROP_ON)
  261.         parent = aNode.id
  262.       else {
  263.         parent = this.getBTContainer(aNode);
  264.         item = aNode;
  265.       }
  266.     }
  267.  
  268.     parent = RDF.GetResource(parent);
  269.     if (aOrientation == BookmarksUtils.DROP_ON)
  270.       return BookmarksUtils.getTargetFromFolder(parent);
  271.  
  272.     item = RDF.GetResource(item.id);
  273.     RDFC.Init(BMDS, parent);
  274.     index = RDFC.IndexOf(item);
  275.     if (aOrientation == BookmarksUtils.DROP_AFTER)
  276.       ++index;
  277.  
  278.     return { parent: parent, index: index };
  279.   },
  280.  
  281.   /////////////////////////////////////////////////////////////////////////
  282.   // returns the parent resource of a node in the personal toolbar.
  283.   // this is determined by inspecting the source element and walking up the
  284.   // DOM tree to find the appropriate containing node.
  285.   getBTContainer: function (aNode)
  286.   {
  287.        
  288.     var parent;
  289.     var item = aNode.id;
  290.     if (!this.isBTBookmark(item))
  291.       return "NC:BookmarksRoot"
  292.     parent = aNode.parentNode.parentNode;
  293.     parent = parent.id;
  294.     switch (parent) {
  295.     case "bookmarks-chevron":
  296.     case "bookmarks-stack":
  297.     case "bookmarks-toolbar":
  298.       return BMSVC.getBookmarksToolbarFolder().Value;
  299.     case "bookmarks-menu":
  300.       return "NC:BookmarksRoot";
  301.     default:
  302.       return parent;
  303.     }
  304.   },
  305.  
  306.   ///////////////////////////////////////////////////////////////////////////
  307.   // returns true if the node is a bookmark, a folder or a bookmark separator
  308.   isBTBookmark: function (aURI)
  309.   {
  310.     if (!aURI)
  311.       return false;
  312.     var type = BookmarksUtils.resolveType(aURI);
  313.     return (type == "BookmarkSeparator"     ||
  314.             type == "Bookmark"              ||
  315.             type == "Folder"                ||
  316.             type == "PersonalToolbarFolder" ||
  317.             type == "Livemark"              ||
  318.             type == "ImmutableBookmark"     ||
  319.             type == "ImmutableFolder"       ||
  320.             aURI == "bookmarks-ptf")
  321.   },
  322.  
  323.   /////////////////////////////////////////////////////////////////////////
  324.   // returns true if the node is a container. -->
  325.   isBTContainer: function (aTarget)
  326.   {
  327.     return  aTarget.localName == "menu" || (aTarget.localName == "toolbarbutton" &&
  328.            (aTarget.getAttribute("container") == "true"));
  329.   },
  330.  
  331.   /////////////////////////////////////////////////////////////////////////
  332.   // returns BookmarksUtils.DROP_BEFORE, DROP_ON or DROP_AFTER accordingly
  333.   // to the event coordinates. Skin authors could break us, we'll cross that
  334.   // bridge when they turn us 90degrees.  -->
  335.   getBTOrientation: function (aEvent, aTarget)
  336.   {
  337.     var target
  338.     if (!aTarget)
  339.       target = aEvent.target;
  340.     else
  341.       target = aTarget;
  342.     if (target.localName == "menu"                 &&
  343.         target.parentNode.localName != "menupopup" ||
  344.         target.id == "bookmarks-chevron")
  345.       return BookmarksUtils.DROP_ON;
  346.     if (target.id == "bookmarks-ptf") {
  347.       return target.hasChildNodes()?
  348.              BookmarksUtils.DROP_AFTER:BookmarksUtils.DROP_ON;
  349.     }
  350.  
  351.     var overButtonBoxObject = target.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
  352.     var overParentBoxObject = target.parentNode.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
  353.  
  354.     var size, border;
  355.     var coordValue, clientCoordValue;
  356.     switch (target.localName) {
  357.       case "toolbarseparator":
  358.       case "toolbarbutton":
  359.         size = overButtonBoxObject.width;
  360.         coordValue = overButtonBoxObject.x;
  361.         clientCoordValue = aEvent.clientX;
  362.         break;
  363.       case "menuseparator":
  364.       case "menu":
  365.       case "menuitem":
  366.         size = overButtonBoxObject.height;
  367.         coordValue = overButtonBoxObject.screenY;
  368.         clientCoordValue = aEvent.screenY;
  369.         break;
  370.       default: return BookmarksUtils.DROP_ON;
  371.     }
  372.     if (this.isBTContainer(target))
  373.       if (target.localName == "toolbarbutton") {
  374.         // the DROP_BEFORE area excludes the label
  375.         var iconNode = document.getAnonymousElementByAttribute(target, "class", "toolbarbutton-icon");
  376.         border = parseInt(document.defaultView.getComputedStyle(target,"").getPropertyValue("padding-left")) +
  377.                  parseInt(document.defaultView.getComputedStyle(iconNode     ,"").getPropertyValue("width"));
  378.         border = Math.min(size/5,Math.max(border,4));
  379.       } else
  380.         border = size/5;
  381.     else
  382.       border = size/2;
  383.  
  384.     // in the first region?
  385.     if (clientCoordValue-coordValue < border)
  386.       return BookmarksUtils.DROP_BEFORE;
  387.     // in the last region?
  388.     else if (clientCoordValue-coordValue >= size-border)
  389.       return BookmarksUtils.DROP_AFTER;
  390.     else // must be in the middle somewhere
  391.       return BookmarksUtils.DROP_ON;
  392.   },
  393.  
  394.   /////////////////////////////////////////////////////////////////////////
  395.   // expand the folder targeted by the context menu.
  396.   expandBTFolder: function ()
  397.   {
  398.     var target = document.popupNode.lastChild;
  399.     if (document.popupNode.open)
  400.       target.hidePopup();
  401.     else
  402.       target.showPopup(document.popupNode);
  403.   },
  404.  
  405.   onCommandUpdate: function ()
  406.   {
  407.        
  408.     var selection = this._selection;
  409.     var target    = this._target;
  410.     BookmarksController.onCommandUpdate(selection, target);
  411.     if (document.popupNode.id == "bookmarks-ptf") {
  412.       // disabling 'cut' and 'copy' on the empty area of the personal toolbar
  413.       var commandNode = document.getElementById("cmd_cut");
  414.       commandNode.setAttribute("disabled", "true");
  415.       commandNode = document.getElementById("cmd_copy");
  416.       commandNode.setAttribute("disabled", "true");
  417.     }
  418.   },
  419.  
  420.   ///////////////////////////////////////////////////////////////
  421.   // Load a bookmark in menus or toolbar buttons
  422.   // aTarget may not the aEvent target (see Open in tabs command)
  423.   loadBookmark: function (aEvent, aTarget, aDS)
  424.   {
  425.        
  426.     if (aTarget.getAttribute("class") == "openintabs-menuitem")
  427.       aTarget = aTarget.parentNode.parentNode;
  428.  
  429.    
  430.     // Check for invalid bookmarks (most likely a static menu item like "Manage Bookmarks")
  431.     if (!this.isBTBookmark(aTarget.id))
  432.       return;
  433.     
  434.     var rSource   = RDF.GetResource(aTarget.id);
  435.     var selection = BookmarksUtils.getSelectionFromResource(rSource);
  436.     var browserTarget = whereToOpenLink(aEvent);
  437.     
  438.     BookmarksCommand.openBookmark(selection, browserTarget, aDS);
  439.    // aEvent.preventBubble();
  440.   },
  441.   
  442.   ////////////////////////////////////////////////
  443.   // loads a bookmark with the mouse middle button
  444.   loadBookmarkMiddleClick: function (aEvent, aDS)
  445.   {
  446.  
  447.     if (aEvent.button != 1)
  448.       return;
  449.     // unlike for command events, we have to close the menus manually
  450.     BookmarksMenuDNDObserver.mCurrentDragOverTarget = null;
  451.     BookmarksMenuDNDObserver.onDragCloseTarget();
  452.     this.loadBookmark(aEvent, aEvent.target, aDS);
  453.   }
  454. }
  455.  
  456. var BookmarksMenuController = {
  457.  
  458.   supportsCommand: BookmarksController.supportsCommand,
  459.  
  460.   isCommandEnabled: function (aCommand)
  461.   {
  462.     var selection = BookmarksMenu._selection;
  463.     var target    = BookmarksMenu._target;
  464.     if (selection)
  465.       return BookmarksController.isCommandEnabled(aCommand, selection, target);
  466.     return false;
  467.   },
  468.  
  469.   doCommand: function (aCommand)
  470.   {
  471.       
  472.     if (content)
  473.       content.focus();
  474.  
  475.     BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
  476.  
  477.     var element = document.popupNode.firstChild;
  478.     if (element && element.localName == "menupopup")
  479.       element.hidePopup();
  480.  
  481.     var selection = BookmarksMenu._selection;
  482.     var target    = BookmarksMenu._target;
  483.     var db        = BookmarksMenu._db;
  484.     switch (aCommand) {
  485.     case "cmd_bm_expandfolder":
  486.       BookmarksMenu.expandBTFolder();
  487.       break;
  488.     default:
  489.       BookmarksController.doCommand(aCommand, selection, target, db);
  490.     }
  491.   },
  492.  
  493.   onMouseMove: function (aEvent)
  494.   {
  495.     var command = aEvent.target.getAttribute("command");
  496.     var isDisabled = aEvent.target.getAttribute("disabled")
  497.     if (isDisabled != "true" && (command == "cmd_bm_newfolder" || command == "cmd_paste")) {
  498.       BookmarksMenuDNDObserver.onDragSetFeedBack(document.popupNode, BookmarksMenu._orientation);
  499.     } else {
  500.       BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
  501.     }
  502.   }
  503. }
  504.  
  505. var BookmarksMenuDNDObserver = {
  506.  
  507.   ////////////////////
  508.   // Public methods //
  509.   ////////////////////
  510.  
  511.   onDragStart: function (aEvent, aXferData, aDragAction)
  512.   {
  513.  
  514.     var target = aEvent.target;
  515.  
  516.     // Prevent dragging from invalid regions
  517.  
  518.     // can't drag from the empty areas
  519.     if (target.id == "bookmarks-menu" ||
  520.         target.id == "bookmarks-chevron" ||
  521.         target.id == "bookmarks-ptf")
  522.       return false;
  523.  
  524.     if (!BookmarksMenu.isBTBookmark(target.id))
  525.       return false;
  526.  
  527.     // Prevent dragging out of menupopups on non Win32 platforms.
  528.     // a) on Mac drag from menus is generally regarded as being satanic
  529.     // b) on Linux, this causes an X-server crash, (bug 151336)
  530.     // c) on Windows, there is no hang or crash associated with this, so we'll leave
  531.     // the functionality there.
  532.     if (navigator.platform != "Win32" && target.localName != "toolbarbutton")
  533.       return;
  534.  
  535.     // a drag start is fired when leaving an open toolbarbutton(type=menu)
  536.     // (see bug 143031)
  537.     if (this.isContainer(target)) {
  538.       if (this.isPlatformNotSupported)
  539.         return;
  540.       if (!aEvent.shiftKey && !aEvent.altKey && !aEvent.ctrlKey)
  541.         return;
  542.       // menus open on mouse down
  543.       target.firstChild.hidePopup();
  544.     }
  545.     var selection  = BookmarksMenu.getBTSelection(target);
  546.     aXferData.data = BookmarksUtils.getXferDataFromSelection(selection);
  547.   },
  548.  
  549.   onDragOver: function(aEvent, aFlavour, aDragSession)
  550.   {
  551.     
  552.     var orientation = BookmarksMenu.getBTOrientation(aEvent)
  553.     if (aDragSession.canDrop)
  554.       this.onDragSetFeedBack(aEvent.target, orientation);
  555.     if (orientation != this.mCurrentDropPosition) {
  556.       // emulating onDragExit and onDragEnter events since the drop region
  557.       // has changed on the target.
  558.       this.onDragExit(aEvent, aDragSession);
  559.       this.onDragEnter(aEvent, aDragSession);
  560.     }
  561.     if (this.isPlatformNotSupported)
  562.       return;
  563.     if (this.isTimerSupported)
  564.       return;
  565.     this.onDragOverCheckTimers();
  566.   },
  567.  
  568.   onDragEnter: function (aEvent, aDragSession)
  569.   {
  570.     var target = aEvent.target;
  571.     var orientation = BookmarksMenu.getBTOrientation(aEvent);
  572.     if (target.localName == "menupopup" || target.id == "bookmarks-ptf")
  573.       target = target.parentNode;
  574.     if (aDragSession.canDrop) {
  575.       this.onDragSetFeedBack(target, orientation);
  576.       this.onDragEnterSetTimer(target, aDragSession);
  577.     }
  578.     this.mCurrentDragOverTarget = target;
  579.     this.mCurrentDropPosition   = orientation;
  580.   },
  581.  
  582.   onDragExit: function (aEvent, aDragSession)
  583.   {
  584.    
  585.     var target = aEvent.target;
  586.     if (target.localName == "menupopup" || target.id == "bookmarks-ptf")
  587.       target = target.parentNode;
  588.     this.onDragRemoveFeedBack(target);
  589.     this.onDragExitSetTimer(target, aDragSession);
  590.     this.mCurrentDragOverTarget = null;
  591.     this.mCurrentDropPosition = null;
  592.   },
  593.  
  594.   onDrop: function (aEvent, aXferData, aDragSession)
  595.   {
  596.       
  597.     var target = aEvent.target;
  598.     this.onDragRemoveFeedBack(target);
  599.  
  600.     var selection = BookmarksUtils.getSelectionFromXferData(aDragSession);
  601.  
  602.     var orientation = BookmarksMenu.getBTOrientation(aEvent);
  603.  
  604.     // For RTL PersonalBar bookmarks buttons, orientation should be inverted (only in drop case)
  605.     var PBStyle = window.getComputedStyle(document.getElementById("PersonalToolbar"),'');
  606.     var isHorizontal = (target.localName == "toolbarbutton");
  607.     if ((PBStyle.direction == 'rtl') && isHorizontal) {
  608.       if (orientation == BookmarksUtils.DROP_AFTER)
  609.         orientation = BookmarksUtils.DROP_BEFORE;
  610.       else if (orientation == BookmarksUtils.DROP_BEFORE)
  611.         orientation = BookmarksUtils.DROP_AFTER;
  612.     }
  613.  
  614.     var selTarget   = BookmarksMenu.getBTTarget(target, orientation);
  615.  
  616.     // we can only test for kCopyAction if the source is a bookmark
  617.     var checkCopy = aDragSession.isDataFlavorSupported("moz/rdfitem");
  618.  
  619.     const kDSIID      = Components.interfaces.nsIDragService;
  620.     const kCopyAction = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
  621.  
  622.     // hide the 'open in tab' menuseparator because bookmarks
  623.     // can be inserted after it if they are dropped after the last bookmark
  624.     // a more comprehensive fix would be in the menupopup template builder
  625.     var menuSeparator = null;
  626.     var menuTarget = (target.localName == "toolbarbutton" ||
  627.                       target.localName == "menu")         &&
  628.                      orientation == BookmarksUtils.DROP_ON?
  629.                      target.lastChild:target.parentNode;
  630.     if (menuTarget.hasChildNodes() &&
  631.         menuTarget.lastChild.getAttribute("class") == "openintabs-menuitem") {
  632.       menuSeparator = menuTarget.lastChild.previousSibling;
  633.       menuTarget.removeChild(menuSeparator);
  634.     }
  635.  
  636.     // doCopy defaults to true; check if we should make it false.
  637.     // we make it false only if all the selection items have valid parent
  638.     // bookmark DS containers (i.e. aren't generated via aggregation)
  639.     var doCopy = true;
  640.     if (checkCopy && !(aDragSession.dragAction & kCopyAction))
  641.       doCopy = BookmarksUtils.shouldCopySelection("drag", selection);
  642.  
  643.     if (doCopy)
  644.       BookmarksUtils.insertAndCheckSelection("drag", selection, selTarget);
  645.     else
  646.       BookmarksUtils.moveAndCheckSelection("drag", selection, selTarget);
  647.  
  648.     // show again the menuseparator
  649.     if (menuSeparator)
  650.       menuTarget.insertBefore(menuSeparator, menuTarget.lastChild);
  651.  
  652.   },
  653.  
  654.   canDrop: function (aEvent, aDragSession)
  655.   {
  656.     var target = aEvent.target;
  657.     if (!BookmarksMenu.isBTBookmark(target.id))
  658.       return false;
  659.  
  660.     var btype = BookmarksUtils.resolveType(target.id);
  661.  
  662.     return target.id == "bookmarks-menu"               ||
  663.            target.id == "bookmarks-chevron"            ||
  664.            target.id == "bookmarks-ptf" ||
  665.           (target.id != "NC:SystemBookmarksStaticRoot" &&
  666.            btype == "Folder" ||
  667.            btype == "Bookmark");
  668.   },
  669.  
  670.   canHandleMultipleItems: true,
  671.  
  672.   getSupportedFlavours: function ()
  673.   {
  674.     var flavourSet = new FlavourSet();
  675.     flavourSet.appendFlavour("moz/rdfitem");
  676.     flavourSet.appendFlavour("text/x-moz-url");
  677.     flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  678.     flavourSet.appendFlavour("text/unicode");
  679.     return flavourSet;
  680.   },
  681.  
  682.  
  683.   ////////////////////////////////////
  684.   // Private methods and properties //
  685.   ////////////////////////////////////
  686.  
  687.   springLoadedMenuDelay: 350, // milliseconds
  688.   isPlatformNotSupported: navigator.platform.indexOf("Mac") != -1, // see bug 136524
  689.   isTimerSupported: navigator.platform.indexOf("Win") == -1,
  690.  
  691.   mCurrentDragOverTarget: null,
  692.   mCurrentDropPosition: null,
  693.   loadTimer  : null,
  694.   closeTimer : null,
  695.   loadTarget : null,
  696.   closeTarget: null,
  697.  
  698.   _observers : null,
  699.   get mObservers ()
  700.   {
  701.     if (!this._observers) {
  702.       this._observers = [
  703.         document.getElementById("bookmarks-ptf"),
  704.         document.getElementById("bookmarks-menu").firstChild,
  705.         document.getElementById("bookmarks-chevron").parentNode
  706.       ]
  707.     }
  708.     return this._observers;
  709.   },
  710.  
  711.   getObserverForNode: function (aNode)
  712.   {
  713.     if (!aNode)
  714.       return null;
  715.     var node = aNode;
  716.     var observer;
  717.     while (node) {
  718.       for (var i=0; i < this.mObservers.length; i++) {
  719.         observer = this.mObservers[i];
  720.         if (observer == node)
  721.           return observer;
  722.       }
  723.       node = node.parentNode;
  724.     }
  725.     return null;
  726.   },
  727.  
  728.   onDragCloseMenu: function (aNode)
  729.   {
  730.     
  731.     var children = aNode.childNodes;
  732.     for (var i = 0; i < children.length; i++) {
  733.       if (this.isContainer(children[i]) &&
  734.           children[i].getAttribute("open") == "true") {
  735.         this.onDragCloseMenu(children[i].lastChild);
  736.         if (children[i] != this.mCurrentDragOverTarget || this.mCurrentDropPosition != BookmarksUtils.DROP_ON)
  737.           children[i].lastChild.hidePopup();
  738.       }
  739.     }
  740.   },
  741.  
  742.   onDragCloseTarget: function ()
  743.   {
  744.       
  745.     var currentObserver = this.getObserverForNode(this.mCurrentDragOverTarget);
  746.     // close all the menus not hovered by the mouse
  747.     for (var i=0; i < this.mObservers.length; i++) {
  748.       if (currentObserver != this.mObservers[i]) {
  749.         this.onDragCloseMenu(this.mObservers[i]);
  750.         if (this.mObservers[i].parentNode.id == "bookmarks-menu")
  751.           this.mObservers[i].hidePopup();
  752.       } else
  753.         this.onDragCloseMenu(this.mCurrentDragOverTarget.parentNode);
  754.     }
  755.   },
  756.  
  757.   onDragLoadTarget: function (aTarget)
  758.   {
  759.      
  760.     if (!this.mCurrentDragOverTarget)
  761.       return;
  762.     // Load the current menu
  763.     if (this.mCurrentDropPosition == BookmarksUtils.DROP_ON &&
  764.         this.isContainer(aTarget))
  765.       aTarget.lastChild.showPopup(aTarget);
  766.   },
  767.  
  768.   onDragOverCheckTimers: function ()
  769.   {
  770.     
  771.     var now = new Date().getTime();
  772.     if (this.closeTimer && now-this.springLoadedMenuDelay>this.closeTimer) {
  773.       this.onDragCloseTarget();
  774.       this.closeTimer = null;
  775.     }
  776.     if (this.loadTimer && (now-this.springLoadedMenuDelay>this.loadTimer)) {
  777.       this.onDragLoadTarget(this.loadTarget);
  778.       this.loadTimer = null;
  779.     }
  780.   },
  781.  
  782.   onDragEnterSetTimer: function (aTarget, aDragSession)
  783.   {
  784.     
  785.     if (this.isPlatformNotSupported)
  786.       return;
  787.     if (this.isTimerSupported) {
  788.       var targetToBeLoaded = aTarget;
  789.       clearTimeout(this.loadTimer);
  790.       if (aTarget == aDragSession.sourceNode)
  791.         return;
  792.       var This = this;
  793.       this.loadTimer=setTimeout(function () {This.onDragLoadTarget(targetToBeLoaded)}, This.springLoadedMenuDelay);
  794.     } else {
  795.       var now = new Date().getTime();
  796.       this.loadTimer  = now;
  797.       this.loadTarget = aTarget;
  798.     }
  799.   },
  800.  
  801.   onDragExitSetTimer: function (aTarget, aDragSession)
  802.   {
  803.      
  804.     if (this.isPlatformNotSupported)
  805.       return;
  806.     var This = this;
  807.     if (this.isTimerSupported) {
  808.       clearTimeout(this.closeTimer)
  809.       this.closeTimer=setTimeout(function () {This.onDragCloseTarget()}, This.springLoadedMenuDelay);
  810.     } else {
  811.       var now = new Date().getTime();
  812.       this.closeTimer  = now;
  813.       this.closeTarget = aTarget;
  814.       this.loadTimer = null;
  815.  
  816.       // If user isn't rearranging within the menu, close it
  817.       // To do so, we exploit a Mac bug: timeout set during
  818.       // drag and drop on Windows and Mac are fired only after that the drop is released.
  819.       // timeouts will pile up, we may have a better approach but for the moment, this one
  820.       // correctly close the menus after a drop/cancel outside the personal toolbar.
  821.       // The if statement in the function has been introduced to deal with rare but reproducible
  822.       // missing Exit events.
  823.       if (aDragSession.sourceNode.localName != "menuitem" && aDragSession.sourceNode.localName != "menu")
  824.         setTimeout(function () { if (This.mCurrentDragOverTarget) {This.onDragRemoveFeedBack(This.mCurrentDragOverTarget); This.mCurrentDragOverTarget=null} This.loadTimer=null; This.onDragCloseTarget() }, 0);
  825.     }
  826.   },
  827.  
  828.   onDragSetFeedBack: function (aTarget, aOrientation)
  829.   {
  830.     
  831.    switch (aTarget.localName) {
  832.       case "toolbarseparator":
  833.       case "toolbarbutton":
  834.         switch (aOrientation) {
  835.           case BookmarksUtils.DROP_BEFORE:
  836.             aTarget.setAttribute("dragover-left", "true");
  837.             break;
  838.           case BookmarksUtils.DROP_AFTER:
  839.             aTarget.setAttribute("dragover-right", "true");
  840.             break;
  841.           case BookmarksUtils.DROP_ON:
  842.             aTarget.setAttribute("dragover-top"   , "true");
  843.             aTarget.setAttribute("dragover-bottom", "true");
  844.             aTarget.setAttribute("dragover-left"  , "true");
  845.             aTarget.setAttribute("dragover-right" , "true");
  846.             break;
  847.         }
  848.         break;
  849.       case "menuseparator":
  850.       case "menu":
  851.       case "menuitem":
  852.         switch (aOrientation) {
  853.           case BookmarksUtils.DROP_BEFORE:
  854.             aTarget.setAttribute("dragover-top", "true");
  855.             break;
  856.           case BookmarksUtils.DROP_AFTER:
  857.             aTarget.setAttribute("dragover-bottom", "true");
  858.             break;
  859.           case BookmarksUtils.DROP_ON:
  860.             break;
  861.         }
  862.         break;
  863.       case "hbox"     :
  864.         // hit between the last visible bookmark and the chevron
  865.         var newTarget = BookmarksToolbar.getLastVisibleBookmark();
  866.         if (newTarget)
  867.           newTarget.setAttribute("dragover-right", "true");
  868.         break;
  869.       case "stack"    :
  870.       case "menupopup": break;
  871.      default: dump("No feedback for: "+aTarget.localName+"\n");
  872.     }
  873.   },
  874.  
  875.   onDragRemoveFeedBack: function (aTarget)
  876.   {
  877.     
  878.     var newTarget;
  879.     var bt;
  880.     if (aTarget.id == "bookmarks-ptf") {
  881.       // hit when dropping in the bt or between the last visible bookmark
  882.       // and the chevron
  883.       newTarget = BookmarksToolbar.getLastVisibleBookmark();
  884.       if (newTarget)
  885.         newTarget.removeAttribute("dragover-right");
  886.     } else if (aTarget.id == "bookmarks-stack") {
  887.       newTarget = BookmarksToolbar.getLastVisibleBookmark();
  888.       newTarget.removeAttribute("dragover-right");
  889.     } else {
  890.       aTarget.removeAttribute("dragover-left");
  891.       aTarget.removeAttribute("dragover-right");
  892.       aTarget.removeAttribute("dragover-top");
  893.       aTarget.removeAttribute("dragover-bottom");
  894.     }
  895.   },
  896.  
  897.   onDropSetFeedBack: function (aTarget)
  898.   {
  899.     //XXX Not yet...
  900.        
  901.   },
  902.  
  903.   isContainer: function (aTarget)
  904.   {
  905.     return aTarget.localName == "menu"          ||
  906.            aTarget.localName == "toolbarbutton" &&
  907.            aTarget.getAttribute("type") == "menu";
  908.   }
  909. }
  910.  
  911. var BookmarksToolbar =
  912. {
  913.   /////////////////////////////////////////////////////////////////////////////
  914.   // make bookmarks toolbar act like menus
  915.   openedMenuButton:null,
  916.   autoOpenMenu: function (aEvent)
  917.   {
  918.     
  919.     var target = aEvent.target;
  920.     if (BookmarksToolbar.openedMenuButton != target &&
  921.         target.nodeName == "toolbarbutton" &&
  922.         target.type == "menu") {
  923.       BookmarksToolbar.openedMenuButton.open = false;
  924.       target.open = true;
  925.     }
  926.   },
  927.   setOpenedMenu: function (aEvent)
  928.   {
  929.     if (aEvent.target.parentNode.localName == 'toolbarbutton') {
  930.       if (!this.openedMenuButton)
  931.         aEvent.currentTarget.addEventListener("mouseover", this.autoOpenMenu, true);
  932.       this.openedMenuButton = aEvent.target.parentNode;
  933.     }
  934.   },
  935.   unsetOpenedMenu: function (aEvent)
  936.   {
  937.     if (aEvent.target.parentNode.localName == 'toolbarbutton') {
  938.       aEvent.currentTarget.removeEventListener("mouseover", this.autoOpenMenu, true);
  939.       this.openedMenuButton = null;
  940.     }
  941.   },
  942.  
  943.   /////////////////////////////////////////////////////////////////////////////
  944.   // returns the node of the last visible bookmark on the toolbar -->
  945.   getLastVisibleBookmark: function ()
  946.   {
  947.     var buttons = document.getElementById("bookmarks-ptf");
  948.     var button = buttons.firstChild;
  949.     if (!button)
  950.       return null; // empty bookmarks toolbar
  951.     do {
  952.       if (button.collapsed)
  953.         return button.previousSibling;
  954.       button = button.nextSibling;
  955.     } while (button)
  956.     return buttons.lastChild;
  957.   },
  958.  
  959.   updateOverflowMenu: function (aMenuPopup)
  960.   {
  961.     var hbox = document.getElementById("bookmarks-ptf");
  962.     for (var i = 0; i < hbox.childNodes.length; i++) {
  963.       var button = hbox.childNodes[i];
  964.       var menu = aMenuPopup.childNodes[i];
  965.       if (menu.collapsed == button.collapsed)
  966.         menu.collapsed = !menu.collapsed;
  967.     }
  968.   },
  969.  
  970.   resizeFunc: function(event)
  971.   {
  972.     
  973.     var buttons = document.getElementById("bookmarks-ptf");
  974.     if (!buttons)
  975.       return;
  976.     var chevron = document.getElementById("bookmarks-chevron");
  977.     var width = window.innerWidth;
  978.     var myToolbar = buttons.parentNode.parentNode.parentNode;
  979.         var tNode=myToolbar.parentNode;
  980.         while (tNode) {
  981.             if (tNode.localName=="multibar") {
  982.                 var rightSide = document.getAnonymousElementByAttribute(tNode,'class','multibar-background-right');
  983.                 if (rightSide) {
  984.                     width = width - rightSide.boxObject.width;
  985.                 }
  986.                 break;
  987.             }
  988.             tNode=tNode.parentNode;
  989.         }
  990.     for (var i = myToolbar.childNodes.length-1; i >= 0; i--){
  991.       var anItem = myToolbar.childNodes[i];
  992.       if (anItem.id == "personal-bookmarks") {
  993.         break;
  994.       }
  995.       width -= anItem.boxObject.width;
  996.     }
  997.     var chevronWidth = 0;
  998.     chevron.collapsed = false;
  999.     chevronWidth = chevron.boxObject.width;
  1000.     chevron.collapsed = true;
  1001.     var overflowed = false;
  1002.  
  1003.     var isLTR=window.getComputedStyle(document.getElementById("PersonalToolbar"),'').direction=='ltr';
  1004.  
  1005.     for (var i=0; i<buttons.childNodes.length; i++) {
  1006.       var button = buttons.childNodes[i];
  1007.       button.collapsed = overflowed;
  1008.  
  1009.       if (i == buttons.childNodes.length - 1) // last ptf item...
  1010.         chevronWidth = 0;
  1011.       var offset = isLTR ? button.boxObject.x
  1012.                          : width - button.boxObject.x;
  1013.       if (offset + button.boxObject.width + chevronWidth > width) {
  1014.          overflowed = true;
  1015.         // This button doesn't fit. Show it in the menu. Hide it in the toolbar.
  1016.         if (!button.collapsed)
  1017.           button.collapsed = true;
  1018.         if (chevron.collapsed) {
  1019.           chevron.collapsed = false;
  1020.           var overflowPadder = document.getElementById("overflow-padder");
  1021.           offset = isLTR ? buttons.boxObject.x
  1022.                          : width - buttons.boxObject.x - buttons.boxObject.width;
  1023.           overflowPadder.width = width - chevron.boxObject.width - offset;
  1024.         }
  1025.       }
  1026.     }
  1027.     BookmarksToolbarRDFObserver._overflowTimerInEffect = false;
  1028.   },
  1029.  
  1030.   // Fill in tooltips for personal toolbar
  1031.   fillInBTTooltip: function (tipElement)
  1032.   {
  1033.  
  1034.     var title = tipElement.label;
  1035.     var url = tipElement.statusText;
  1036.  
  1037.     if (!title && !url) {
  1038.       // bail out early if there is nothing to show
  1039.       return false;
  1040.     }
  1041.  
  1042.     var tooltipTitle = document.getElementById("btTitleText");
  1043.     var tooltipUrl = document.getElementById("btUrlText");
  1044.     if (title && title != url) {
  1045.       tooltipTitle.removeAttribute("hidden");
  1046.       tooltipTitle.setAttribute("value", title);
  1047.     } else  {
  1048.       tooltipTitle.setAttribute("hidden", "true");
  1049.     }
  1050.     if (url) {
  1051.       tooltipUrl.removeAttribute("hidden");
  1052.       tooltipUrl.setAttribute("value", url);
  1053.     } else {
  1054.       tooltipUrl.setAttribute("hidden", "true");
  1055.     }
  1056.     return true; // show tooltip
  1057.   }
  1058. }
  1059.  
  1060. // Implement nsIRDFObserver so we can update our overflow state when items get
  1061. // added/removed from the toolbar
  1062. var BookmarksToolbarRDFObserver =
  1063. {
  1064.   onAssert: function (aDataSource, aSource, aProperty, aTarget)
  1065.   {
  1066.     if (aProperty.Value == NC_NS+"BookmarksToolbarFolder") {
  1067.       var bt = document.getElementById("bookmarks-ptf");
  1068.       if (bt) {
  1069.         bt.ref = aSource.Value;
  1070.         document.getElementById("bookmarks-chevron").ref = aSource.Value;
  1071.       }
  1072.     }
  1073.     this.setOverflowTimeout(aSource, aProperty);
  1074.   },
  1075.   onUnassert: function (aDataSource, aSource, aProperty, aTarget)
  1076.   {
  1077.     this.setOverflowTimeout(aSource, aProperty);
  1078.   },
  1079.   onChange: function (aDataSource, aSource, aProperty, aOldTarget, aNewTarget) {},
  1080.   onMove: function (aDataSource, aOldSource, aNewSource, aProperty, aTarget) {},
  1081.   onBeginUpdateBatch: function (aDataSource) {},
  1082.   onEndUpdateBatch: function (aDataSource)
  1083.   {
  1084.     this._overflowTimerInEffect = true;
  1085.     setTimeout(BookmarksToolbar.resizeFunc, 0);
  1086.   },
  1087.   _overflowTimerInEffect: false,
  1088.   setOverflowTimeout: function (aSource, aProperty)
  1089.   {
  1090.     if (this._overflowTimerInEffect)
  1091.       return;
  1092.     if (aSource != BMSVC.getBookmarksToolbarFolder()
  1093.         || aProperty.Value == NC_NS+"LastModifiedDate")
  1094.       return;
  1095.     this._overflowTimerInEffect = true;
  1096.     setTimeout(BookmarksToolbar.resizeFunc, 0);
  1097.   }
  1098. }
  1099.