refactor codes for drag and drop

This commit is contained in:
Piro / SHIMODA Hiroshi 2010-12-02 09:00:39 +09:00
parent f661405af0
commit 78dc5ef23c
4 changed files with 515 additions and 503 deletions

View File

@ -421,7 +421,7 @@ TreeStyleTabService.overrideExtensionsOnInitAfter = function TSTService_override
eval('TabDNDObserver.clearDragmark = '+
TabDNDObserver.clearDragmark.toSource().replace(
/(\})(\))?$/,
'gBrowser.treeStyleTab.clearDropPosition(); $1$2'
'gBrowser.treeStyleTab.tabbarDNDObserver.clearDropPosition(); $1$2'
)
);
if (TabDNDObserver.canDrop) {
@ -475,13 +475,13 @@ TreeStyleTabService.overrideExtensionsOnInitAfter = function TSTService_override
).replace(
/(var newIndex =)/,
<![CDATA[
if (isTabReorder && TSTTabBrowser.treeStyleTab.performDrop(dropActionInfo, aDragSession.sourceNode))
if (isTabReorder && TSTTabBrowser.treeStyleTab.tabbarDNDObserver.performDrop(dropActionInfo, aDragSession.sourceNode))
return;
]]>
).replace(
/(aTab = gBrowser.addTab\(url\));/,
<![CDATA[
TSTTabBrowser.treeStyleTab.performDrop(dropActionInfo, $1);
TSTTabBrowser.treeStyleTab.tabbarDNDObserver.performDrop(dropActionInfo, $1);
return;
]]>
).replace(
@ -493,7 +493,7 @@ TreeStyleTabService.overrideExtensionsOnInitAfter = function TSTService_override
dropActionInfo.position != TreeStyleTabService.kDROP_ON ||
(TreeStyleTabService.dropLinksOnTabBehavior() & TreeStyleTabService.kDROPLINK_NEWTAB)
) {
TSTTabBrowser.treeStyleTab.performDrop(dropActionInfo, TSTTabBrowser.loadOneTab(url, null, null, null, bgLoad, false));
TSTTabBrowser.treeStyleTab.tabbarDNDObserver.performDrop(dropActionInfo, TSTTabBrowser.loadOneTab(url, null, null, null, bgLoad, false));
return;
}
]]>

View File

@ -1581,7 +1581,7 @@ var TreeStyleTabService = {
if (remoteService.hasChildTabs(remoteTab) ||
(remoteMultipleTabService && remoteMultipleTabService.isSelected(remoteTab))) {
var remoteBrowser = remoteService.getTabBrowserFromChild(remoteTab);
if (remoteBrowser.treeStyleTab.isDraggingAllTabs(remoteTab)) {
if (remoteBrowser.treeStyleTab.tabbarDNDObserver.isDraggingAllTabs(remoteTab)) {
window.close();
}
else {
@ -1590,7 +1590,7 @@ var TreeStyleTabService = {
};
window.setTimeout(function() {
var blankTab = gBrowser.selectedTab;
gBrowser.treeStyleTab.performDrop(actionInfo, remoteTab);
gBrowser.treeStyleTab.tabbarDNDObserver.performDrop(actionInfo, remoteTab);
window.setTimeout(function() {
gBrowser.removeTab(blankTab);

View File

@ -85,6 +85,16 @@ TreeStyleTabBrowser.prototype = {
return (this._tabStripPlaceHolder = value);
},
get tabbarDNDObserver()
{
return this._tabbarDNDObserver || (this._tabbarDNDObserver = new TreeStyleTabBrowserTabbarDNDObserver(this));
},
get panelDNDObserver()
{
return this._panelDNDObserver || (this._panelDNDObserver = new TreeStyleTabBrowserTabpanelDNDObserver(this));
},
/* utils */
/* get tab contents */
@ -2024,7 +2034,7 @@ TreeStyleTabBrowser.prototype = {
aEvent.preventDefault();
return;
// cancel tab dragging by Multiple Tab
// cancel tab dragging by Multiple Tab
case 'MultipleTabHandler:TabsDragStart':
return aEvent.preventDefault();
}
@ -3296,487 +3306,6 @@ TreeStyleTabBrowser.prototype = {
this.removeTabbrowserAttribute(this.kPRINT_PREVIEW);
},
/* drag and drop */
get tabbarDNDObserver()
{
return this._tabbarDNDObserver || (this._tabbarDNDObserver = new TreeStyleTabBrowserTabbarDNDObserver(this));
},
get panelDNDObserver()
{
return this._panelDNDObserver || (this._panelDNDObserver = new TreeStyleTabBrowserTabpanelDNDObserver(this));
},
getCurrentDragSession : function TSTBrowser_getCurrentDragSession()
{
return Components
.classes['@mozilla.org/widget/dragservice;1']
.getService(Components.interfaces.nsIDragService)
.getCurrentSession();
},
getDropAction : function TSTBrowser_getDropAction(aEvent, aDragSession)
{
if (!aDragSession)
aDragSession = this.getCurrentDragSession();
var tab = aDragSession ? this.getTabFromChild(aDragSession.sourceNode) : null ;
this.ensureTabInitialized(tab);
var info = this.getDropActionInternal(aEvent, tab);
info.canDrop = true;
info.source = tab;
if (tab) {
var isCopy = this.isCopyAction(aEvent);
if (isCopy && 'duplicateTab' in this.mTabBrowser) {
info.action |= this.kACTION_DUPLICATE;
}
if (
!isCopy &&
this.getTabBrowserFromChild(tab) != this.mTabBrowser &&
(
('duplicateTab' in this.mTabBrowser) ||
('swapBrowsersAndCloseOther' in this.mTabBrowser)
)
) {
info.action |= this.kACTION_IMPORT;
}
if (info.action & this.kACTIONS_FOR_DESTINATION) {
if (info.action & this.kACTION_MOVE) info.action ^= this.kACTION_MOVE;
if (info.action & this.kACTION_STAY) info.action ^= this.kACTION_STAY;
}
if (info.action & this.kACTION_ATTACH) {
if (info.parent == tab) {
info.canDrop = false;
}
else {
var orig = tab;
tab = info.target;
while (tab = this.getParentTab(tab))
{
if (tab != orig) continue;
info.canDrop = false;
break;
}
}
}
}
return info;
},
getDropActionInternal : function TSTBrowser_getDropActionInternal(aEvent, aSourceTab)
{
var tab = aEvent.target;
var b = this.mTabBrowser;
var tabs = this.getTabsArray(b);
var firstTab = this.getFirstNormalTab(b);
var lastTabIndex = tabs.length -1;
var isInverted = this.isVertical ? false : window.getComputedStyle(b.parentNode, null).direction == 'rtl';
var info = {
target : null,
position : null,
action : null,
parent : null,
insertBefore : null,
event : aEvent
};
var isTabMoveFromOtherWindow = aSourceTab && aSourceTab.ownerDocument != document;
var isNewTabAction = !aSourceTab || aSourceTab.ownerDocument != document;
if (tab.localName != 'tab') {
var action = isTabMoveFromOtherWindow ? this.kACTION_STAY : (this.kACTION_MOVE | this.kACTION_PART) ;
if (isNewTabAction) action |= this.kACTION_NEWTAB;
if (aEvent[this.positionProp] < firstTab.boxObject[this.positionProp]) {
info.target = info.parent = info.insertBefore = firstTab;
info.position = isInverted ? this.kDROP_AFTER : this.kDROP_BEFORE ;
info.action = action;
return info;
}
else if (aEvent[this.positionProp] > tabs[lastTabIndex].boxObject[this.positionProp] + tabs[lastTabIndex].boxObject[this.sizeProp]) {
info.target = info.parent = tabs[lastTabIndex];
info.position = isInverted ? this.kDROP_BEFORE : this.kDROP_AFTER ;
info.action = action;
return info;
}
else {
let index = b.getNewIndex ?
b.getNewIndex(aEvent) :
b.tabContainer._getDropIndex(aEvent) ;
info.target = tabs[Math.min(index, lastTabIndex)];
}
}
else {
this.ensureTabInitialized(tab);
info.target = tab;
}
var positionProp = this.isVertical && tab.getAttribute('pinned') == 'true' ? this.invertedPositionProp : this.positionProp ;
var sizeProp = this.isVertical && tab.getAttribute('pinned') == 'true' ? this.invertedSizeProp : this.sizeProp ;
var boxPos = tab.boxObject[positionProp];
var boxUnit = Math.round(tab.boxObject[sizeProp] / 3);
if (aEvent[positionProp] < boxPos + boxUnit) {
info.position = isInverted ? this.kDROP_AFTER : this.kDROP_BEFORE ;
}
else if (aEvent[positionProp] > boxPos + boxUnit + boxUnit) {
info.position = isInverted ? this.kDROP_BEFORE : this.kDROP_AFTER ;
}
else {
info.position = this.kDROP_ON;
}
switch (info.position)
{
case this.kDROP_ON:
info.action = this.kACTION_STAY | this.kACTION_ATTACH;
info.parent = tab;
var visible = this.getNextVisibleTab(tab);
info.insertBefore = this.getTreePref('insertNewChildAt') == this.kINSERT_FISRT ?
(this.getFirstChildTab(tab) || visible) :
(this.getNextSiblingTab(tab) || this.getNextTab(this.getLastDescendantTab(tab)) || visible);
break;
case this.kDROP_BEFORE:
/*
[TARGET ] <EFBFBD>ªpart from parent, and move
[ ]
[TARGET ] <EFBFBD>ªattach to the parent of the target, and move
[ ]
[TARGET ] <EFBFBD>ªattach to the parent of the target, and move
[ ]
[TARGET] <EFBFBD>ªattach to the parent of the target (previous tab), and move
*/
var prevTab = this.getPreviousVisibleTab(tab);
if (!prevTab) {
info.action = this.kACTION_MOVE | this.kACTION_PART;
info.insertBefore = firstTab;
}
else {
var prevLevel = Number(prevTab.getAttribute(this.kNEST));
var targetNest = Number(tab.getAttribute(this.kNEST));
info.parent = (prevLevel < targetNest) ? prevTab : this.getParentTab(tab) ;
info.action = this.kACTION_MOVE | (info.parent ? this.kACTION_ATTACH : this.kACTION_PART );
info.insertBefore = tab;
}
break;
case this.kDROP_AFTER:
/*
[TARGET ] <EFBFBD>«if the target has a parent, attach to it and and move
[TARGET] <EFBFBD>«attach to the parent of the target, and move
[ ]
[TARGET ] <EFBFBD>«attach to the parent of the target, and move
[ ]
[TARGET ] <EFBFBD>«attach to the target, and move
[ ]
*/
var nextTab = this.getNextVisibleTab(tab);
if (!nextTab) {
info.action = this.kACTION_MOVE | this.kACTION_ATTACH;
info.parent = this.getParentTab(tab);
}
else {
var targetNest = Number(tab.getAttribute(this.kNEST));
var nextLevel = Number(nextTab.getAttribute(this.kNEST));
info.parent = (targetNest < nextLevel) ? tab : this.getParentTab(tab) ;
info.action = this.kACTION_MOVE | (info.parent ? this.kACTION_ATTACH : this.kACTION_PART );
info.insertBefore = nextTab;
/*
[TARGET ] <EFBFBD>«attach dragged tab to the parent of the target as its next sibling
[DRAGGED]
*/
if (aSourceTab == nextTab && this.getDescendantTabs(info.parent).length == 1) {
info.action = this.kACTION_MOVE | this.kACTION_ATTACH;
info.parent = this.getParentTab(tab);
info.insertBefore = this.getNextTab(nextTab);
}
}
break;
}
if (isNewTabAction) action |= this.kACTION_NEWTAB;
return info;
},
performDrop : function TSTBrowser_performDrop(aInfo, aDraggedTab)
{
var tabsInfo = this.getDraggedTabsInfoFromOneTab(aInfo, aDraggedTab);
if (!tabsInfo.draggedTab) return false;
aDraggedTab = tabsInfo.draggedTab;
var draggedTabs = tabsInfo.draggedTabs;
var draggedRoots = tabsInfo.draggedRoots;
var targetBrowser = this.mTabBrowser;
var tabs = this.getTabsArray(targetBrowser);
var sourceWindow = aDraggedTab.ownerDocument.defaultView;
var sourceBrowser = this.getTabBrowserFromChild(aDraggedTab);
var draggedWholeTree = [].concat(draggedRoots);
draggedRoots.forEach(function(aRoot) {
draggedWholeTree = draggedWholeTree.concat(this.getDescendantTabs(aRoot));
}, this);
while (aInfo.insertBefore && draggedWholeTree.indexOf(aInfo.insertBefore) > -1)
{
aInfo.insertBefore = this.getNextTab(aInfo.insertBefore);
}
if (aInfo.action & this.kACTIONS_FOR_SOURCE) {
if (aInfo.action & this.kACTION_PART) {
this.partTabsOnDrop(draggedRoots);
}
else if (aInfo.action & this.kACTION_ATTACH) {
this.attachTabsOnDrop(draggedRoots, aInfo.parent);
}
else {
return false;
}
if ( // if this move will cause no change...
sourceBrowser == targetBrowser &&
sourceBrowser.treeStyleTab.getNextVisibleTab(draggedTabs[draggedTabs.length-1]) == aInfo.insertBefore
) {
// then, do nothing
return true;
}
}
// prevent Multiple Tab Handler feature
targetBrowser.duplicatingSelectedTabs = true;
targetBrowser.movingSelectedTabs = true;
var newRoots = [];
var shouldClose = (
aInfo.action & this.kACTION_IMPORT &&
this.getAllTabsArray(sourceBrowser).length == draggedTabs.length
);
var oldTabs = [];
var newTabs = [];
var treeStructure = draggedTabs.map(function(aTab) {
var parent = sourceBrowser.treeStyleTab.getParentTab(aTab);
return parent ? draggedTabs.indexOf(parent) : -1 ;
});
var parentTabsArray = draggedTabs.map(function(aTab) {
return (aInfo.action & this.kACTIONS_FOR_DESTINATION) ?
sourceBrowser.treeStyleTab.getParentTab(aTab) : null ;
}, this);
// Firefox fails to "move" collapsed tabs. So, expand them first
// and collapse them after they are moved.
var collapseExpandState = [];
if (aInfo.action & this.kACTION_IMPORT &&
'swapBrowsersAndCloseOther' in targetBrowser) {
draggedWholeTree.forEach(function(aTab) {
collapseExpandState.push(this.getTabValue(aTab, this.kSUBTREE_COLLAPSED) == 'true');
this.collapseExpandSubtree(aTab, false, true);
this.collapseExpandTab(aTab, false, true);
}, this);
}
var lastTabIndex = tabs.length -1;
draggedTabs.forEach(function(aTab, aIndex) {
var tab = aTab;
if (aInfo.action & this.kACTIONS_FOR_DESTINATION) {
var parent = parentTabsArray[aIndex];
if (tabsInfo.isMultipleMove && 'MultipleTabService' in sourceWindow)
sourceWindow.MultipleTabService.setSelection(aTab, false);
if (aInfo.action & this.kACTION_IMPORT &&
'swapBrowsersAndCloseOther' in targetBrowser) {
tab = targetBrowser.addTab();
tab.linkedBrowser.stop();
tab.linkedBrowser.docShell;
targetBrowser.swapBrowsersAndCloseOther(tab, aTab);
targetBrowser.setTabTitle(tab);
}
else {
tab = targetBrowser.duplicateTab(aTab);
this.deleteTabValue(tab, this.kCHILDREN);
this.deleteTabValue(tab, this.kPARENT);
if (aInfo.action & this.kACTION_IMPORT)
oldTabs.push(aTab);
}
newTabs.push(tab);
if (tabsInfo.isMultipleMove && 'MultipleTabService' in window)
MultipleTabService.setSelection(tab, true);
if (!parent || draggedTabs.indexOf(parent) < 0)
newRoots.push(tab);
lastTabIndex++;
}
var newIndex = aInfo.insertBefore ? aInfo.insertBefore._tPos : lastTabIndex ;
if (aInfo.insertBefore && newIndex > tab._tPos) newIndex--;
this.internallyTabMovingCount++;
targetBrowser.moveTabTo(tab, newIndex);
this.collapseExpandTab(tab, false, true);
this.internallyTabMovingCount--;
}, this);
// close imported tabs from the source browser
oldTabs.forEach(function(aTab) {
sourceBrowser.removeTab(aTab, { animate : true });
});
if (shouldClose) this.closeOwner(sourceBrowser);
// restore tree structure for newly opened tabs
newTabs.forEach(function(aTab, aIndex) {
var index = treeStructure[aIndex];
if (index < 0) return;
targetBrowser.treeStyleTab.attachTabTo(aTab, newTabs[index]);
});
newTabs.reverse();
collapseExpandState.reverse();
collapseExpandState.forEach(function(aCollapsed, aIndex) {
this.collapseExpandSubtree(newTabs[aIndex], aCollapsed, true);
}, this);
if (aInfo.action & this.kACTIONS_FOR_DESTINATION &&
aInfo.action & this.kACTION_ATTACH)
this.attachTabsOnDrop(newRoots, aInfo.parent);
// Multiple Tab Handler
targetBrowser.movingSelectedTabs = false;
targetBrowser.duplicatingSelectedTabs = false;
return true;
},
getDraggedTabsInfoFromOneTab : function TSTBrowser_getDraggedTabsInfoFromOneTab(aInfo, aTab)
{
aTab = this.getTabFromChild(aTab);
if (!aTab)
return {
draggedTab : null,
draggedTabs : [],
draggedRoots : [],
isMultipleMove : false
};
var targetBrowser = this.mTabBrowser;
var tabs = this.getTabsArray(targetBrowser);
var sourceWindow = aTab.ownerDocument.defaultView;
var sourceBrowser = this.getTabBrowserFromChild(aTab);
var draggedTabs = window['piro.sakura.ne.jp'].tabsDragUtils.getSelectedTabs(aInfo.event || sourceBrowser);
var draggedRoots = [aTab];
var isMultipleMove = false;
if (draggedTabs.length > 1) {
isMultipleMove = true;
if (!(aInfo.action & this.kACTIONS_FOR_DESTINATION)) {
draggedRoots = [];
draggedTabs.forEach(function(aTab) {
var parent = aTab,
current;
do {
current = parent;
parent = sourceBrowser.treeStyleTab.getParentTab(parent)
if (parent && draggedTabs.indexOf(parent) > -1) continue;
draggedRoots.push(current);
return;
}
while (parent);
}, this);
}
}
else if (aInfo.action & this.kACTIONS_FOR_DESTINATION) {
draggedTabs = [aTab].concat(sourceBrowser.treeStyleTab.getDescendantTabs(aTab));
}
return {
draggedTab : aTab,
draggedTabs : draggedTabs,
draggedRoots : draggedRoots,
isMultipleMove : isMultipleMove
};
},
attachTabsOnDrop : function TSTBrowser_attachTabsOnDrop(aTabs, aParent)
{
this.mTabBrowser.movingSelectedTabs = true; // Multiple Tab Handler
aTabs.forEach(function(aTab) {
if (!aTab.parentNode) return; // ignore removed tabs
if (aParent)
this.attachTabTo(aTab, aParent);
else
this.partTab(aTab);
this.collapseExpandTab(aTab, false);
}, this);
this.mTabBrowser.movingSelectedTabs = false; // Multiple Tab Handler
},
partTabsOnDrop : function TSTBrowser_partTabsOnDrop(aTabs)
{
this.mTabBrowser.movingSelectedTabs = true; // Multiple Tab Handler
aTabs.forEach(function(aTab) {
if (!aTab.parentNode) return; // ignore removed tabs
this.partTab(aTab);
this.collapseExpandTab(aTab, false);
}, this);
this.mTabBrowser.movingSelectedTabs = false; // Multiple Tab Handler
},
closeOwner : function TSTBrowser_closeOwner(aTabOwner)
{
var w = aTabOwner.ownerDocument.defaultView;
if (!w) return;
if ('SplitBrowser' in w) {
if ('getSubBrowserFromChild' in w.SplitBrowser) {
var subbrowser = w.SplitBrowser.getSubBrowserFromChild(aTabOwner);
if (subbrowser) {
subbrowser.close();
return;
}
}
if (w.SplitBrowser.browsers.length) return;
}
w.close();
},
clearDropPosition : function TSTBrowser_clearDropPosition()
{
var b = this.mTabBrowser;
var xpathResult = this.evaluateXPath(
'child::xul:tab[@'+this.kDROP_POSITION+']',
b.mTabContainer
);
for (var i = 0, maxi = xpathResult.snapshotLength; i < maxi; i++)
{
xpathResult.snapshotItem(i).removeAttribute(this.kDROP_POSITION);
}
},
isDraggingAllTabs : function TSTBrowser_isDraggingAllTabs(aTab, aTabs)
{
var actionInfo = {
action : this.kACTIONS_FOR_DESTINATION | this.kACTION_IMPORT
};
var tabsInfo = this.getDraggedTabsInfoFromOneTab(actionInfo, aTab);
return tabsInfo.draggedTabs.length == (aTabs || this.getAllTabsArray(this.mTabBrowser)).length;
},
isDraggingAllCurrentTabs : function TSTBrowser_isDraggingAllCurrentTabs(aTab)
{
return this.isDraggingAllTabs(aTab, this.getTabsArray(this.mTabBrowser));
},
/* commands */
/* reset */
@ -4334,7 +3863,7 @@ TreeStyleTabBrowser.prototype = {
this.updateTabsCount(parent);
},
promoteTooDeepLevelTabs : function TSTBrowser_promoteTooDeepLevelTabs(aParent)
promoteTooDeepLevelTabs : function TSTBrowser_promoteTooDeepLevelTabs(aParent)
{
if (this.maxTreeLevel < 0 || !this.maxTreeLevelPhisical)
return;

View File

@ -95,10 +95,10 @@ TreeStyleTabBrowserTabbarDNDObserver.prototype = {
tooltip.popupBoxObject.popupState != 'closed')
tooltip.hidePopup();
var dropAction = sv.getDropAction(aEvent);
var dropAction = this.getDropAction(aEvent);
if ('dataTransfer' in aEvent) {
var dt = aEvent.dataTransfer;
if (dropAction.action & this.kACTION_NEWTAB) {
if (dropAction.action & sv.kACTION_NEWTAB) {
dt.effectAllowed = dt.dropEffect = (
!dropAction.source ? 'link' :
sv.isCopyAction(aEvent) ? 'copy' :
@ -127,7 +127,7 @@ try{
if (sv.isCollapsed(tab))
return false;
var info = sv.getDropAction(aEvent, session);
var info = this.getDropAction(aEvent, session);
return info.canDrop;
}
catch(e) {
@ -136,6 +136,489 @@ catch(e) {
}
},
getDropAction : function TSTTabbarDND_getDropAction(aEvent, aDragSession)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
if (!aDragSession)
aDragSession = sv.getCurrentDragSession();
var tab = aDragSession ? sv.getTabFromChild(aDragSession.sourceNode) : null ;
sv.ensureTabInitialized(tab);
var info = this.getDropActionInternal(aEvent, tab);
info.canDrop = true;
info.source = tab;
if (tab) {
var isCopy = sv.isCopyAction(aEvent);
if (isCopy && 'duplicateTab' in b) {
info.action |= sv.kACTION_DUPLICATE;
}
if (
!isCopy &&
sv.getTabBrowserFromChild(tab) != b &&
(
('duplicateTab' in b) ||
('swapBrowsersAndCloseOther' in b)
)
) {
info.action |= sv.kACTION_IMPORT;
}
if (info.action & sv.kACTIONS_FOR_DESTINATION) {
if (info.action & sv.kACTION_MOVE) info.action ^= sv.kACTION_MOVE;
if (info.action & sv.kACTION_STAY) info.action ^= sv.kACTION_STAY;
}
if (info.action & sv.kACTION_ATTACH) {
if (info.parent == tab) {
info.canDrop = false;
}
else {
var orig = tab;
tab = info.target;
while (tab = sv.getParentTab(tab))
{
if (tab != orig) continue;
info.canDrop = false;
break;
}
}
}
}
return info;
},
getDropActionInternal : function TSTTabbarDND_getDropActionInternal(aEvent, aSourceTab)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
var tab = aEvent.target;
var tabs = sv.getTabsArray(b);
var firstTab = sv.getFirstNormalTab(b);
var lastTabIndex = tabs.length -1;
var isInverted = sv.isVertical ? false : window.getComputedStyle(b.parentNode, null).direction == 'rtl';
var info = {
target : null,
position : null,
action : null,
parent : null,
insertBefore : null,
event : aEvent
};
var isTabMoveFromOtherWindow = aSourceTab && aSourceTab.ownerDocument != document;
var isNewTabAction = !aSourceTab || aSourceTab.ownerDocument != document;
if (tab.localName != 'tab') {
var action = isTabMoveFromOtherWindow ? sv.kACTION_STAY : (sv.kACTION_MOVE | sv.kACTION_PART) ;
if (isNewTabAction) action |= sv.kACTION_NEWTAB;
if (aEvent[sv.positionProp] < firstTab.boxObject[sv.positionProp]) {
info.target = info.parent = info.insertBefore = firstTab;
info.position = isInverted ? sv.kDROP_AFTER : sv.kDROP_BEFORE ;
info.action = action;
return info;
}
else if (aEvent[sv.positionProp] > tabs[lastTabIndex].boxObject[sv.positionProp] + tabs[lastTabIndex].boxObject[sv.sizeProp]) {
info.target = info.parent = tabs[lastTabIndex];
info.position = isInverted ? sv.kDROP_BEFORE : sv.kDROP_AFTER ;
info.action = action;
return info;
}
else {
let index = b.getNewIndex ?
b.getNewIndex(aEvent) :
b.tabContainer._getDropIndex(aEvent) ;
info.target = tabs[Math.min(index, lastTabIndex)];
}
}
else {
sv.ensureTabInitialized(tab);
info.target = tab;
}
var positionProp = sv.isVertical && tab.getAttribute('pinned') == 'true' ? sv.invertedPositionProp : sv.positionProp ;
var sizeProp = sv.isVertical && tab.getAttribute('pinned') == 'true' ? sv.invertedSizeProp : sv.sizeProp ;
var boxPos = tab.boxObject[positionProp];
var boxUnit = Math.round(tab.boxObject[sizeProp] / 3);
if (aEvent[positionProp] < boxPos + boxUnit) {
info.position = isInverted ? sv.kDROP_AFTER : sv.kDROP_BEFORE ;
}
else if (aEvent[positionProp] > boxPos + boxUnit + boxUnit) {
info.position = isInverted ? sv.kDROP_BEFORE : sv.kDROP_AFTER ;
}
else {
info.position = sv.kDROP_ON;
}
switch (info.position)
{
case sv.kDROP_ON:
info.action = sv.kACTION_STAY | sv.kACTION_ATTACH;
info.parent = tab;
var visible = sv.getNextVisibleTab(tab);
info.insertBefore = sv.getTreePref('insertNewChildAt') == sv.kINSERT_FISRT ?
(sv.getFirstChildTab(tab) || visible) :
(sv.getNextSiblingTab(tab) || sv.getNextTab(sv.getLastDescendantTab(tab)) || visible);
break;
case sv.kDROP_BEFORE:
/*
[TARGET ] <EFBFBD>ªpart from parent, and move
[ ]
[TARGET ] <EFBFBD>ªattach to the parent of the target, and move
[ ]
[TARGET ] <EFBFBD>ªattach to the parent of the target, and move
[ ]
[TARGET] <EFBFBD>ªattach to the parent of the target (previous tab), and move
*/
var prevTab = sv.getPreviousVisibleTab(tab);
if (!prevTab) {
info.action = sv.kACTION_MOVE | sv.kACTION_PART;
info.insertBefore = firstTab;
}
else {
var prevLevel = Number(prevTab.getAttribute(sv.kNEST));
var targetNest = Number(tab.getAttribute(sv.kNEST));
info.parent = (prevLevel < targetNest) ? prevTab : sv.getParentTab(tab) ;
info.action = sv.kACTION_MOVE | (info.parent ? sv.kACTION_ATTACH : sv.kACTION_PART );
info.insertBefore = tab;
}
break;
case sv.kDROP_AFTER:
/*
[TARGET ] <EFBFBD>«if the target has a parent, attach to it and and move
[TARGET] <EFBFBD>«attach to the parent of the target, and move
[ ]
[TARGET ] <EFBFBD>«attach to the parent of the target, and move
[ ]
[TARGET ] <EFBFBD>«attach to the target, and move
[ ]
*/
var nextTab = sv.getNextVisibleTab(tab);
if (!nextTab) {
info.action = sv.kACTION_MOVE | sv.kACTION_ATTACH;
info.parent = sv.getParentTab(tab);
}
else {
var targetNest = Number(tab.getAttribute(sv.kNEST));
var nextLevel = Number(nextTab.getAttribute(sv.kNEST));
info.parent = (targetNest < nextLevel) ? tab : sv.getParentTab(tab) ;
info.action = sv.kACTION_MOVE | (info.parent ? sv.kACTION_ATTACH : sv.kACTION_PART );
info.insertBefore = nextTab;
/*
[TARGET ] <EFBFBD>«attach dragged tab to the parent of the target as its next sibling
[DRAGGED]
*/
if (aSourceTab == nextTab && sv.getDescendantTabs(info.parent).length == 1) {
info.action = sv.kACTION_MOVE | sv.kACTION_ATTACH;
info.parent = sv.getParentTab(tab);
info.insertBefore = sv.getNextTab(nextTab);
}
}
break;
}
if (isNewTabAction) action |= sv.kACTION_NEWTAB;
return info;
},
performDrop : function TSTTabbarDND_performDrop(aInfo, aDraggedTab)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
var tabsInfo = this.getDraggedTabsInfoFromOneTab(aInfo, aDraggedTab);
if (!tabsInfo.draggedTab) return false;
aDraggedTab = tabsInfo.draggedTab;
var draggedTabs = tabsInfo.draggedTabs;
var draggedRoots = tabsInfo.draggedRoots;
var targetBrowser = b;
var tabs = sv.getTabsArray(targetBrowser);
var sourceWindow = aDraggedTab.ownerDocument.defaultView;
var sourceBrowser = sv.getTabBrowserFromChild(aDraggedTab);
var draggedWholeTree = [].concat(draggedRoots);
draggedRoots.forEach(function(aRoot) {
draggedWholeTree = draggedWholeTree.concat(sv.getDescendantTabs(aRoot));
}, this);
while (aInfo.insertBefore && draggedWholeTree.indexOf(aInfo.insertBefore) > -1)
{
aInfo.insertBefore = sv.getNextTab(aInfo.insertBefore);
}
if (aInfo.action & sv.kACTIONS_FOR_SOURCE) {
if (aInfo.action & sv.kACTION_PART) {
this.partTabsOnDrop(draggedRoots);
}
else if (aInfo.action & sv.kACTION_ATTACH) {
this.attachTabsOnDrop(draggedRoots, aInfo.parent);
}
else {
return false;
}
if ( // if this move will cause no change...
sourceBrowser == targetBrowser &&
sourceBrowser.treeStyleTab.getNextVisibleTab(draggedTabs[draggedTabs.length-1]) == aInfo.insertBefore
) {
// then, do nothing
return true;
}
}
// prevent Multiple Tab Handler feature
targetBrowser.duplicatingSelectedTabs = true;
targetBrowser.movingSelectedTabs = true;
var newRoots = [];
var shouldClose = (
aInfo.action & sv.kACTION_IMPORT &&
sv.getAllTabsArray(sourceBrowser).length == draggedTabs.length
);
var oldTabs = [];
var newTabs = [];
var treeStructure = draggedTabs.map(function(aTab) {
var parent = sourceBrowser.treeStyleTab.getParentTab(aTab);
return parent ? draggedTabs.indexOf(parent) : -1 ;
});
var parentTabsArray = draggedTabs.map(function(aTab) {
return (aInfo.action & sv.kACTIONS_FOR_DESTINATION) ?
sourceBrowser.treeStyleTab.getParentTab(aTab) : null ;
}, this);
// Firefox fails to "move" collapsed tabs. So, expand them first
// and collapse them after they are moved.
var collapseExpandState = [];
if (aInfo.action & sv.kACTION_IMPORT &&
'swapBrowsersAndCloseOther' in targetBrowser) {
draggedWholeTree.forEach(function(aTab) {
collapseExpandState.push(sv.getTabValue(aTab, sv.kSUBTREE_COLLAPSED) == 'true');
sv.collapseExpandSubtree(aTab, false, true);
sv.collapseExpandTab(aTab, false, true);
}, sv);
}
var lastTabIndex = tabs.length -1;
draggedTabs.forEach(function(aTab, aIndex) {
var tab = aTab;
if (aInfo.action & sv.kACTIONS_FOR_DESTINATION) {
var parent = parentTabsArray[aIndex];
if (tabsInfo.isMultipleMove && 'MultipleTabService' in sourceWindow)
sourceWindow.MultipleTabService.setSelection(aTab, false);
if (aInfo.action & sv.kACTION_IMPORT &&
'swapBrowsersAndCloseOther' in targetBrowser) {
tab = targetBrowser.addTab();
tab.linkedBrowser.stop();
tab.linkedBrowser.docShell;
targetBrowser.swapBrowsersAndCloseOther(tab, aTab);
targetBrowser.setTabTitle(tab);
}
else {
tab = targetBrowser.duplicateTab(aTab);
sv.deleteTabValue(tab, sv.kCHILDREN);
sv.deleteTabValue(tab, sv.kPARENT);
if (aInfo.action & sv.kACTION_IMPORT)
oldTabs.push(aTab);
}
newTabs.push(tab);
if (tabsInfo.isMultipleMove && 'MultipleTabService' in window)
MultipleTabService.setSelection(tab, true);
if (!parent || draggedTabs.indexOf(parent) < 0)
newRoots.push(tab);
lastTabIndex++;
}
var newIndex = aInfo.insertBefore ? aInfo.insertBefore._tPos : lastTabIndex ;
if (aInfo.insertBefore && newIndex > tab._tPos) newIndex--;
sv.internallyTabMovingCount++;
targetBrowser.moveTabTo(tab, newIndex);
sv.collapseExpandTab(tab, false, true);
sv.internallyTabMovingCount--;
}, this);
// close imported tabs from the source browser
oldTabs.forEach(function(aTab) {
sourceBrowser.removeTab(aTab, { animate : true });
});
if (shouldClose)
this.closeOwner(sourceBrowser);
// restore tree structure for newly opened tabs
newTabs.forEach(function(aTab, aIndex) {
var index = treeStructure[aIndex];
if (index < 0) return;
sv.attachTabTo(aTab, newTabs[index]);
}, sv);
newTabs.reverse();
collapseExpandState.reverse();
collapseExpandState.forEach(function(aCollapsed, aIndex) {
sv.collapseExpandSubtree(newTabs[aIndex], aCollapsed, true);
}, sv);
if (aInfo.action & sv.kACTIONS_FOR_DESTINATION &&
aInfo.action & sv.kACTION_ATTACH)
this.attachTabsOnDrop(newRoots, aInfo.parent);
// Multiple Tab Handler
targetBrowser.movingSelectedTabs = false;
targetBrowser.duplicatingSelectedTabs = false;
return true;
},
getDraggedTabsInfoFromOneTab : function TSTTabbarDND_getDraggedTabsInfoFromOneTab(aInfo, aTab)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
aTab = sv.getTabFromChild(aTab);
if (!aTab)
return {
draggedTab : null,
draggedTabs : [],
draggedRoots : [],
isMultipleMove : false
};
var targetBrowser = b;
var tabs = sv.getTabsArray(targetBrowser);
var sourceWindow = aTab.ownerDocument.defaultView;
var sourceBrowser = sv.getTabBrowserFromChild(aTab);
var draggedTabs = window['piro.sakura.ne.jp'].tabsDragUtils.getSelectedTabs(aInfo.event || sourceBrowser);
var draggedRoots = [aTab];
var isMultipleMove = false;
if (draggedTabs.length > 1) {
isMultipleMove = true;
if (!(aInfo.action & sv.kACTIONS_FOR_DESTINATION)) {
draggedRoots = [];
draggedTabs.forEach(function(aTab) {
var parent = aTab,
current;
do {
current = parent;
parent = sourceBrowser.treeStyleTab.getParentTab(parent)
if (parent && draggedTabs.indexOf(parent) > -1) continue;
draggedRoots.push(current);
return;
}
while (parent);
}, this);
}
}
else if (aInfo.action & sv.kACTIONS_FOR_DESTINATION) {
draggedTabs = [aTab].concat(sourceBrowser.treeStyleTab.getDescendantTabs(aTab));
}
return {
draggedTab : aTab,
draggedTabs : draggedTabs,
draggedRoots : draggedRoots,
isMultipleMove : isMultipleMove
};
},
attachTabsOnDrop : function TSTTabbarDND_attachTabsOnDrop(aTabs, aParent)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
b.movingSelectedTabs = true; // Multiple Tab Handler
aTabs.forEach(function(aTab) {
if (!aTab.parentNode) return; // ignore removed tabs
if (aParent)
sv.attachTabTo(aTab, aParent);
else
sv.partTab(aTab);
sv.collapseExpandTab(aTab, false);
}, sv);
b.movingSelectedTabs = false; // Multiple Tab Handler
},
partTabsOnDrop : function TSTTabbarDND_partTabsOnDrop(aTabs)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
b.movingSelectedTabs = true; // Multiple Tab Handler
aTabs.forEach(function(aTab) {
if (!aTab.parentNode) return; // ignore removed tabs
sv.partTab(aTab);
sv.collapseExpandTab(aTab, false);
}, sv);
b.movingSelectedTabs = false; // Multiple Tab Handler
},
closeOwner : function TSTTabbarDND_closeOwner(aTabOwner)
{
var w = aTabOwner.ownerDocument.defaultView;
if (!w) return;
if ('SplitBrowser' in w) {
if ('getSubBrowserFromChild' in w.SplitBrowser) {
var subbrowser = w.SplitBrowser.getSubBrowserFromChild(aTabOwner);
if (subbrowser) {
subbrowser.close();
return;
}
}
if (w.SplitBrowser.browsers.length) return;
}
w.close();
},
clearDropPosition : function TSTTabbarDND_clearDropPosition()
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
var xpathResult = sv.evaluateXPath(
'child::xul:tab[@'+sv.kDROP_POSITION+']',
b.mTabContainer
);
for (var i = 0, maxi = xpathResult.snapshotLength; i < maxi; i++)
{
xpathResult.snapshotItem(i).removeAttribute(sv.kDROP_POSITION);
}
},
isDraggingAllTabs : function TSTTabbarDND_isDraggingAllTabs(aTab, aTabs)
{
var sv = this.mOwner;
var b = sv.mTabBrowser;
var actionInfo = {
action : sv.kACTIONS_FOR_DESTINATION | sv.kACTION_IMPORT
};
var tabsInfo = this.getDraggedTabsInfoFromOneTab(actionInfo, aTab);
return tabsInfo.draggedTabs.length == (aTabs || sv.getAllTabsArray(b)).length;
},
isDraggingAllCurrentTabs : function TSTTabbarDND_isDraggingAllCurrentTabs(aTab)
{
return this.isDraggingAllTabs(aTab, this.getTabsArray(this.mOwner.mTabBrowser));
},
handleEvent : function TSTTabbarDND_handleEvent(aEvent)
{
switch (aEvent.type)
@ -166,7 +649,7 @@ catch(e) {
action : sv.kACTIONS_FOR_DESTINATION | sv.kACTION_MOVE,
event : aEvent
};
var tabsInfo = sv.getDraggedTabsInfoFromOneTab(actionInfo, aTab);
var tabsInfo = this.getDraggedTabsInfoFromOneTab(actionInfo, aTab);
if (tabsInfo.draggedTabs.length > 1)
window['piro.sakura.ne.jp'].tabsDragUtils.startTabsDrag(aEvent, tabsInfo.draggedTabs);
},
@ -246,7 +729,7 @@ catch(e) {
var tabbarFromEvent = sv.getTabbarFromChild(aEvent.relatedTarget);
if (!tabbarFromEvent)
sv.clearDropPosition();
this.clearDropPosition();
window.clearTimeout(this.mAutoExpandTimer);
this.mAutoExpandTimer = null;
@ -271,7 +754,7 @@ catch(e) {
var strip = sv.tabStrip;
var dt = aEvent.dataTransfer;
sv.clearDropPosition();
this.clearDropPosition();
if (dt.mozUserCancelled || dt.dropEffect != 'none')
return;
@ -302,7 +785,7 @@ catch(e) {
return;
var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (sv.isDraggingAllCurrentTabs(draggedTab))
if (this.isDraggingAllCurrentTabs(draggedTab))
return;
b.replaceTabWithWindow(draggedTab);
@ -341,7 +824,7 @@ try{
sv.autoScroll.processAutoScroll(aEvent);
var info = sv.getDropAction(aEvent, session);
var info = this.getDropAction(aEvent, session);
var observer = b;
if (b.tabContainer && b.tabContainer._setEffectAllowedForDataTransfer) // for Firefox 4.0
@ -379,7 +862,7 @@ try{
b.mTabContainer,
XPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue)
sv.clearDropPosition();
this.clearDropPosition();
if (
!info.canDrop ||
@ -427,13 +910,13 @@ catch(e) {
var tabbar = b.mTabContainer;
var dt = aEvent.dataTransfer;
sv.clearDropPosition();
this.clearDropPosition();
if (tabbar._tabDropIndicator) // for Firefox 4 or later
tabbar._tabDropIndicator.collapsed = true;
var session = sv.getCurrentDragSession();
var dropActionInfo = sv.getDropAction(aEvent, session);
var dropActionInfo = this.getDropAction(aEvent, session);
var draggedTab;
if (dt.dropEffect != 'link') {
@ -444,7 +927,7 @@ catch(e) {
}
}
if (draggedTab && sv.performDrop(dropActionInfo, draggedTab)) {
if (draggedTab && this.performDrop(dropActionInfo, draggedTab)) {
aEvent.stopPropagation();
return;
}
@ -500,7 +983,7 @@ catch(e) {
let tab = sv.getTabFromEvent(aEvent);
if (!tab || dt.dropEffect == 'copy') {
sv.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad }));
this.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad }));
}
else {
let locked = (
@ -514,7 +997,7 @@ catch(e) {
try {
if (loadDroppedLinkToNewChildTab || locked) {
sv.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad }));
this.performDrop(dropActionInfo, b.loadOneTab(getShortcutOrURI(url), { inBackground: bgLoad }));
}
else {
tab.linkedBrowser.loadURI(getShortcutOrURI(url));