diff --git a/content/treestyletab/res/tabsDragUtils.js b/content/treestyletab/res/tabsDragUtils.js
new file mode 100644
index 00000000..ca7ed309
--- /dev/null
+++ b/content/treestyletab/res/tabsDragUtils.js
@@ -0,0 +1,244 @@
+/*
+ Multiple Tabs Drag and Drop Utilities for Firefox 3.5 or later
+
+ Usage:
+ window['piro.sakura.ne.jp'].tabsDragUtils.initTabBrowser(gBrowser);
+
+ // in dragstart event listener
+ window['piro.sakura.ne.jp'].tabsDragUtils.startTabsDrag(aEvent, aArrayOfTabs);
+
+ license: The MIT License, Copyright (c) 2010 SHIMODA "Piro" Hiroshi
+ http://github.com/piroor/fxaddonlibs/blob/master/license.txt
+ original:
+ http://github.com/piroor/fxaddonlibs/blob/master/tabsDragUtils.js
+*/
+(function() {
+ const currentRevision = 1;
+
+ if (!('piro.sakura.ne.jp' in window)) window['piro.sakura.ne.jp'] = {};
+
+ var loadedRevision = 'tabsDragUtils' in window['piro.sakura.ne.jp'] ?
+ window['piro.sakura.ne.jp'].tabsDragUtils.revision :
+ 0 ;
+ if (loadedRevision && loadedRevision > currentRevision) {
+ return;
+ }
+
+ if (loadedRevision &&
+ 'destroy' in window['piro.sakura.ne.jp'].tabsDragUtils)
+ window['piro.sakura.ne.jp'].tabsDragUtils.destroy();
+
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+
+ var tabsDragUtils = {
+ revision : currentRevision,
+
+ init : function TDU_init()
+ {
+ window.addEventListener('load', this._delayedInit, false);
+ },
+ _delayedInit : function TDU_delayedInit()
+ {
+ window.removeEventListener('load', arguments.callee, false);
+ // BarTap
+ // https://addons.mozilla.org/firefox/addon/67651
+ if ('BarTap' in window &&
+ 'writeBarTap' in BarTap) {
+ eval('BarTap.writeBarTap = '+
+ BarTap.writeBarTap.toSource().replace(
+ 'bartap = JSON.stringify',
+ 'window["piro.sakura.ne.jp"].tabsDragUtils._backupArgumentURI(aURI, aBrowser); $&'
+ )
+ );
+ }
+ delete tabsDragUtils._delayedInit;
+ },
+ destroy : function TDU_destroy()
+ {
+ if (this._delayedInit)
+ window.removeEventListener('load', this._delayedInit, false);
+ },
+
+ initTabBrowser : function TDU_initTabBrowser(aTabBrowser)
+ {
+ var tabDNDObserver = (aTabBrowser.tabContainer && aTabBrowser.tabContainer.tabbrowser == aTabBrowser) ?
+ aTabBrowser.tabContainer : // Firefox 4.0 or later
+ aTabBrowser ; // Firefox 3.5 - 3.6
+ if ('_setEffectAllowedForDataTransfer' in tabDNDObserver &&
+ tabDNDObserver._setEffectAllowedForDataTransfer.toSource().indexOf('tabDragUtils') < 0) {
+ eval('tabDNDObserver._setEffectAllowedForDataTransfer = '+
+ tabDNDObserver._setEffectAllowedForDataTransfer.toSource().replace(
+ 'dt.mozItemCount > 1',
+ '$& && !window["piro.sakura.ne.jp"].tabsDragUtils.isTabsDragging(arguments[0])'
+ )
+ );
+ }
+ },
+ destroyTabBrowser : function TDU_destroyTabBrowser(aTabBrowser)
+ {
+ },
+
+ startTabsDrag : function TDU_startTabsDrag(aEvent, aTabs)
+ {
+ var draggedTab = this.getTabFromEvent(aEvent);
+ var tabs = aTabs || [];
+ var index = tabs.indexOf(draggedTab);
+ if (index < 0)
+ return;
+
+ var dt = aEvent.dataTransfer;
+ dt.setDragImage(this.createDragFeedbackImage(tabs), 0, 0);
+
+ tabs.splice(index, 1);
+ tabs.unshift(draggedTab);
+
+ tabs.forEach(function(aTab, aIndex) {
+ dt.mozSetDataAt(TAB_DROP_TYPE, aTab, aIndex);
+ dt.mozSetDataAt('text/x-moz-text-internal', this.getCurrentURIOfTab(aTab), aIndex);
+ }, this);
+
+ dt.mozCursor = 'default';
+
+ aEvent.stopPropagation();
+ },
+ createDragFeedbackImage : function TDU_createDragFeedbackImage(aTabs)
+ {
+ var previews = aTabs.map(function(aTab) {
+ return tabPreviews.capture(aTab, false);
+ }, this);
+ var offset = 16;
+
+ var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
+ canvas.width = previews[0].width + (offset * aTabs.length);
+ canvas.height = previews[0].height + (offset * aTabs.length);
+
+ var ctx = canvas.getContext('2d');
+ ctx.save();
+ try {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ previews.forEach(function(aPreview, aIndex) {
+ ctx.drawImage(aPreview, 0, 0);
+ ctx.translate(offset, offset);
+ ctx.globalAlpha = 1 / (aIndex+1);
+ }, this);
+ }
+ catch(e) {
+ }
+ ctx.restore();
+
+ return canvas;
+ },
+ getTabFromEvent : function TDU_getTabFromEvent(aEvent, aReallyOnTab)
+ {
+ var tab = (aEvent.originalTarget || aEvent.target).ownerDocument.evaluate(
+ 'ancestor-or-self::*[local-name()="tab"]',
+ aEvent.originalTarget || aEvent.target,
+ null,
+ XPathResult.FIRST_ORDERED_NODE_TYPE,
+ null
+ ).singleNodeValue;
+ if (tab || aReallyOnTab)
+ return tab;
+
+ var b = this.getTabBrowserFromChild(aEvent.originalTarget);
+ if (b &&
+ 'treeStyleTab' in b &&
+ 'getTabFromTabbarEvent' in b.treeStyleTab) { // Tree Style Tab
+ return b.treeStyleTab.getTabFromTabbarEvent(aEvent);
+ }
+ return null;
+ },
+ getTabBrowserFromChild : function TDU_getTabBrowserFromChild(aTabBrowserChild)
+ {
+ if (!aTabBrowserChild)
+ return null;
+
+ if (aTabBrowserChild.localName == 'tabbrowser') // itself
+ return aTabBrowserChild;
+
+ if (aTabBrowserChild.tabbrowser) // tabs, Firefox 4.0 or later
+ return aTabBrowserChild.tabbrowser;
+
+ if (aTabBrowserChild.id == 'TabsToolbar') // tabs toolbar, Firefox 4.0 or later
+ return aTabBrowserChild.getElementsByTagName('tabs')[0].tabbrowser;
+
+ var b = aTabBrowserChild.ownerDocument.evaluate(
+ 'ancestor-or-self::*[local-name()="tabbrowser"] | '+
+ 'ancestor-or-self::*[local-name()="tabs" and @tabbrowser]',
+ aTabBrowserChild,
+ XPathResult.FIRST_ORDERED_NODE_TYPE
+ ).singleNodeValue;
+ return (b && b.tabbrowser) || b;
+ },
+
+ isTabsDragging : function TDU_isTabsDragging(aEvent)
+ {
+ var dt = aEvent.dataTransfer;
+ if (dt.mozItemCount < 1)
+ return false;
+ for (let i = 0, maxi = dt.mozItemCount; i < maxi; i++)
+ {
+ if (Array.slice(dt.mozTypesAt(i)).indexOf(TAB_DROP_TYPE) < 0)
+ return false;
+ }
+ return true;
+ },
+
+ getDraggedTabs : function TDU_getDraggedTabs(aEvent)
+ {
+ var dt = aEvent.dataTransfer;
+ var tabs = [];
+ if (dt.mozItemCount < 1 ||
+ Array.slice(dt.mozTypesAt(0)).indexOf(TAB_DROP_TYPE) < 0)
+ return tabs;
+
+ for (let i = 0, maxi = dt.mozItemCount; i < maxi; i++)
+ {
+ tabs.push(dt.mozGetDataAt(TAB_DROP_TYPE, i));
+ }
+ return tabs.sort(function(aA, aB) { return aA._tPos - aB._tPos; });
+ },
+
+ getCurrentURIOfTab : function TDU_getCurrentURIOfTab(aTab)
+ {
+ if (aTab.getAttribute('ontap') == 'true') {
+ // If BarTap ( https://addons.mozilla.org/firefox/addon/67651 ) is installed,
+ // currentURI is possibly 'about:blank'. So, we have to get correct URI
+ // from the attribute or the session histrory.
+ var b = aTab.linkedBrowser;
+ try {
+ if (b.hasAttribute(this.kARGUMENT_URI))
+ return b.getAttribute(this.kARGUMENT_URI);
+ }
+ catch(e) {
+ }
+ try {
+ var h = b.sessionHistory;
+ var entry = h.getEntryAtIndex(h.index, false);
+ return entry.URI.spec;
+ }
+ catch(e) {
+ }
+ }
+ // Firefox 4.0-
+ if (aTab.linkedBrowser.__SS_needsRestore) {
+ let data = aTab.linkedBrowser.__SS_data;
+ let entry = data.entries[Math.max(data.index, data.entries.length-1)];
+ return entry.url;
+ }
+ return aTab.linkedBrowser.currentURI.spec;
+ },
+ _backupArgumentURI : function TDU_backupArgumentURI(aURI, aBrowser)
+ {
+ if (aURI) {
+ var uri = (aURI instanceof Ci.nsIURI) ? aURI.spec : aURI ;
+ aBrowser.setAttribute(this.kARGUMENT_URI, uri);
+ }
+ },
+ kARGUMENT_URI : 'tabs-drag-utils-bartap-uri'
+ };
+
+ window['piro.sakura.ne.jp'].tabsDragUtils = tabsDragUtils;
+ tabsDragUtils.init();
+})();
diff --git a/content/treestyletab/treestyletab.js b/content/treestyletab/treestyletab.js
index 358cfc0a..1f06f7c8 100644
--- a/content/treestyletab/treestyletab.js
+++ b/content/treestyletab/treestyletab.js
@@ -265,6 +265,7 @@ var TreeStyleTabService = {
+
behavior += this.kGROUP_BOOKMARK_USE_DUMMY;
if (!this.getTreePref('openGroupBookmarkBehavior.confirm')) {
behavior += (
@@ -425,9 +426,6 @@ var TreeStyleTabService = {
/\.screenX/g, '[TreeStyleTabService.getTabBrowserFromChild(TSTTabBrowser).treeStyleTab.positionProp]'
).replace(
/\.width/g, '[TreeStyleTabService.getTabBrowserFromChild(TSTTabBrowser).treeStyleTab.sizeProp]'
- ).replace(
- 'dt.mozItemCount > 1',
- '$& && !TreeStyleTabService.isTabsDragging(arguments[0])'
).replace(
/(return (?:true|dt.effectAllowed = "copyMove");)/,
-1) {
- tabsInfo.draggedTabs.splice(index, 1);
- tabsInfo.draggedTabs.unshift(tab);
- }
-
- if ('MultipleTabService' in window &&
- 'setUpTabsDragData' in MultipleTabService) {
- MultipleTabService.setUpTabsDragData(aEvent, tabsInfo.draggedTabs);
- }
- else {
- let dt = aEvent.dataTransfer;
- tabsInfo.draggedTabs.forEach(function(aTab, aIndex) {
- dt.mozSetDataAt(TAB_DROP_TYPE, aTab, aIndex);
- dt.mozSetDataAt('text/x-moz-text-internal', this.getCurrentURIOfTab(aTab), aIndex);
- }, this);
- }
- },
- getCurrentURIOfTab : function TSTService_getCurrentURIOfTab(aTab)
- {
- if (aTab.getAttribute('ontap') == 'true') {
- // If BarTap ( https://addons.mozilla.org/firefox/addon/67651 ) is installed,
- // currentURI is possibly 'about:blank'. So, we have to get correct URI
- // from the session histrory.
- var b = aTab.linkedBrowser;
- try {
- var h = b.sessionHistory;
- var entry = h.getEntryAtIndex(h.index, false);
- return entry.URI;
- }
- catch(e) {
- }
- }
- // Firefox 4.0-
- if (aTab.linkedBrowser.__SS_needsRestore) {
- let data = aTab.linkedBrowser.__SS_data;
- let entry = data.entries[Math.max(data.index, data.entries.length-1)];
- return this.makeURIFromSpec(entry.url);
- }
- return aTab.linkedBrowser.currentURI;
+ window['piro.sakura.ne.jp'].tabsDragUtils.startTabsDrag(aEvent, tabsInfo.draggedTabs);
},
onTabbarDragStart : function TSTService_onTabbarDragStart(aEvent, aTabBrowser)
diff --git a/content/treestyletab/treestyletab.xul b/content/treestyletab/treestyletab.xul
index f2f366e2..7692d152 100644
--- a/content/treestyletab/treestyletab.xul
+++ b/content/treestyletab/treestyletab.xul
@@ -8,6 +8,7 @@
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
diff --git a/content/treestyletab/treestyletabbrowser.js b/content/treestyletab/treestyletabbrowser.js
index b97cf644..ebab5c12 100644
--- a/content/treestyletab/treestyletabbrowser.js
+++ b/content/treestyletab/treestyletabbrowser.js
@@ -350,6 +350,8 @@ TreeStyleTabBrowser.prototype = {
b.addEventListener('MultipleTabHandlerTabsClosing', this, false);
+ window['piro.sakura.ne.jp'].tabsDragUtils.initTabBrowser(b);
+
/* Closing collapsed last tree breaks selected tab.
To solve this problem, I override the setter to
@@ -1641,6 +1643,8 @@ TreeStyleTabBrowser.prototype = {
b.removeEventListener('MultipleTabHandlerTabsClosing', this, false);
+ window['piro.sakura.ne.jp'].tabsDragUtils.destroyTabBrowser(b);
+
TreeStyleTabService.destroyTabDNDObserver(b);
this.tabbarDNDObserver.destroy();
@@ -3718,12 +3722,7 @@ TreeStyleTabBrowser.prototype = {
var sourceBrowser = this.getTabBrowserFromChild(aTab);
var dt = aInfo.event && aInfo.event.dataTransfer;
- var isMultipleDragEvent = (
- dt &&
- dt.mozItemCount > 1 &&
- Array.slice(dt.mozTypesAt(0)).indexOf(TAB_DROP_TYPE) > -1
- );
-
+ var isMultipleDragEvent = window['piro.sakura.ne.jp'].tabsDragUtils.isTabsDragging(aInfo.event);
var isMultipleMove = (
isMultipleDragEvent ||
(
@@ -3735,7 +3734,7 @@ TreeStyleTabBrowser.prototype = {
if (isMultipleMove) {
draggedTabs = isMultipleDragEvent ?
- this.getTabsFromDragEvent(aInfo.event) :
+ window['piro.sakura.ne.jp'].tabsDragUtils.getDraggedTabs(aInfo.event) :
sourceWindow.MultipleTabService.getSelectedTabs(sourceBrowser);
if (!(aInfo.action & this.kACTIONS_FOR_DESTINATION)) {
draggedRoots = [];
@@ -3765,17 +3764,6 @@ TreeStyleTabBrowser.prototype = {
};
},
- getTabsFromDragEvent : function TSTBrowser_getTabsFromDragEvent(aEvent)
- {
- var tabs = [];
- var dt = aEvent.dataTransfer;
- for (let i = 0, maxi = dt.mozItemCount; i < maxi; i++)
- {
- tabs.push(dt.mozGetDataAt(TAB_DROP_TYPE, i));
- }
- return tabs.sort(this.sortTabsByOrder);
- },
-
attachTabsOnDrop : function TSTBrowser_attachTabsOnDrop(aTabs, aParent)
{
this.mTabBrowser.movingSelectedTabs = true; // Multiple Tab Handler