treestyletab/content/treestyletab/bookmarksOverlay.js

533 lines
17 KiB
JavaScript

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
'TreeStyleTabUtils', 'resource://treestyletab-modules/utils.js');
(function() {
let { inherit } = Components.utils.import('resource://treestyletab-modules/lib/inherit.jsm', {});
var TreeStyleTabBookmarksService = inherit(TreeStyleTabService, {
get BookmarksService() {
if (!this._BookmarksService) {
this._BookmarksService = Components
.classes['@mozilla.org/browser/nav-bookmarks-service;1']
.getService(Components.interfaces.nsINavBookmarksService);
}
return this._BookmarksService;
},
_BookmarksService : null,
beginAddBookmarksFromTabs : function TSTBMService_beginAddBookmarksFromTabs(aTabs) /* PUBLIC API */
{
if (this._observing) return;
this._observing = true;
aTabs = this.cleanUpTabsArray(aTabs);
this._addingBookmarks = [];
this._addingBookmarkTreeStructure = aTabs.map(function(aTab) {
var parent = this.getParentTab(aTab);
return aTabs.indexOf(parent);
}, this);
this.BookmarksService.addObserver(this, false);
},
endAddBookmarksFromTabs : function TSTBMService_endAddBookmarksFromTabs() /* PUBLIC API */
{
if (!this._observing) return;
this._observing = false;
this.BookmarksService.removeObserver(this);
this.handleNewBookmarksFromTabs(this._addingBookmarks, this._addingBookmarkTreeStructure);
this._addingBookmarks = [];
this._addingBookmarkTreeStructure = [];
},
handleNewBookmarksFromTabs : function TSTBMService_handleNewBookmarksFromTabs(aBookarmks, aTreeStructure)
{
// this is adding bookmark folder from tabs, so ignroe the first item!
if (
aBookarmks.length == aTreeStructure.length+1 &&
this.BookmarksService.getItemType(aBookarmks[0].id) == this.BookmarksService.TYPE_FOLDER
) {
aBookarmks.shift();
}
else if (aBookarmks.length != aTreeStructure.length) {
return;
}
for (let i = 0, maxi = aBookarmks.length; i < maxi; i++)
{
let item = aBookarmks[i];
item.position = this.BookmarksService.getItemIndex(item.id);
}
aBookarmks.sort(function(aA, aB) {
return aA.position - aB.position;
});
for (let i = 0, maxi = aBookarmks.length; i < maxi; i++)
{
let item = aBookarmks[i];
if (this.BookmarksService.getItemType(item.id) != this.BookmarksService.TYPE_BOOKMARK)
continue;
let uri = this.BookmarksService.getBookmarkURI(item.id);
if (/^about:treestyletab-group\b/.test(uri.spec)) {
let title = this.BookmarksService.getItemTitle(item.id);
let folderId = this.BookmarksService.createFolder(item.parent, title, item.position);
this.BookmarksService.removeItem(item.id);
item.id = folderId;
item.isFolder = true;
}
let index = aTreeStructure[i];
let parent = index > -1 ? aBookarmks[index] : null ;
if (parent && (parent.folder || parent).isFolder) {
let folder = parent.isFolder ? parent : parent.folder ;
this.BookmarksService.moveItem(item.id, folder.id, -1);
item.folder = folder;
}
if (parent && !parent.isFolder) {
PlacesUtils.setAnnotationsForItem(item.id, [{
name : this.kPARENT,
value : parent ? parent.id : -1,
expires : PlacesUtils.annotations.EXPIRE_NEVER
}]);
}
}
},
bookmarkTabSubtree : function TSTBMService_bookmarkTabSubtree(aTabOrTabs)
{
var tabs = aTabOrTabs;
if (!Array.isArray(tabs)) {
tabs = [aTabOrTabs];
}
var folderName = (this.isGroupTab(tabs[0], true) || tabs.length == 1) ?
tabs[0].label :
null ;
var b = this.getTabBrowserFromChild(tabs[0]);
var bookmarkedTabs = [];
for (let i = 0, maxi = tabs.length; i < maxi; i++)
{
let tab = tabs[i];
if (!this.isGroupTab(tab, i == 0)) bookmarkedTabs.push(tab);
bookmarkedTabs = bookmarkedTabs.concat(b.treeStyleTab.getDescendantTabs(tab));
}
this.beginAddBookmarksFromTabs(bookmarkedTabs);
try {
window['piro.sakura.ne.jp'].bookmarkMultipleTabs.addBookmarkFor(bookmarkedTabs, folderName);
}
catch(e) {
}
this.endAddBookmarksFromTabs();
},
bookmarkTabSubTree : function() { return this.bookmarkTabSubtree.apply(this, arguments); }, // obsolete, for backward compatibility
getParentItem : function TSTBMService_getParentItem(aId)
{
if (aId < 0) return -1;
var annotations = PlacesUtils.getAnnotationsForItem(aId);
for (let i in annotations)
{
if (annotations[i].name != this.kPARENT) continue;
return parseInt(annotations[i].value);
}
return -1;
},
getTreeStructureFromItems : function TSTBMService_getTreeStructureFromItems(aIDs, aDefaultParentID)
{
/* this returns a result same to getTreeStructureFromTabs().
[A] => -1 (parent is not in this tree)
[B] => 0 (parent is 1st item in this tree)
[C] => 0 (parent is 1st item in this tree)
[D] => 2 (parent is 2nd in this tree)
[E] => -1 (parent is not in this tree, and this creates another tree)
[F] => 0 (parent is 1st item in this another tree)
*/
if (aDefaultParentID === void(0))
aDefaultParentID = -1;
/* Get array of parents. The index becomes to -1,
if there is NO PARENT or the parent is THE TAB ITSELF. */
var treeStructure = aIDs.map(function(aId, aIndex) {
let id = this.getParentItem(aId);
let index = aIDs.indexOf(id);
return index >= aIndex ? aDefaultParentID : index ;
}, this);
/* Correct patterns like:
[TabA]
[TabB] - this has no parent
[TabC] - TabA's child
to:
[TabA]
[TabB]
[TabC]
*/
treeStructure = treeStructure.reverse();
treeStructure = treeStructure.map(function(aPosition, aIndex) {
if (aIndex > 0 &&
aIndex < treeStructure.length-1 &&
aPosition < 0) {
aPosition = treeStructure[aIndex-1];
}
return aPosition;
});
treeStructure = treeStructure.reverse();
return this.cleanUpTreeStructureArray(treeStructure, aDefaultParentID);
},
// based on PlacesUtils.getURLsForContainerNode()
getItemIdsForContainerNode : function TSTBMService_getItemIdsForContainerNode(aNode)
{
var ids = [];
if (!aNode || !PlacesUtils.nodeIsContainer(aNode)) return ids;
var root = aNode;
if ('getContainerNodeWithOptions' in PlacesUtils) {
root = PlacesUtils.getContainerNodeWithOptions(root, false, true);
}
var oldViewer = root.parentResult.viewer;
var wasOpen = root.containerOpen;
if (!wasOpen) {
if (oldViewer)
root.parentResult.viewer = null;
root.containerOpen = true;
}
for (let i = 0, maxi = root.childCount; i < maxi; ++i)
{
let child = root.getChild(i);
if (PlacesUtils.nodeIsURI(child)) ids.push(child.itemId || -1);
}
if (!wasOpen) {
root.containerOpen = false;
if (oldViewer)
root.parentResult.viewer = oldViewer;
}
return ids;
},
preInit : function TSTBMService_preInit()
{
window.addEventListener('load', this, false);
window.addEventListener(window['piro.sakura.ne.jp'].tabsDragUtils.EVENT_TYPE_TABS_DROP, this, false);
},
init : function TSTBMService_init()
{
window.removeEventListener('load', this, false);
window.addEventListener('unload', this, false);
if (!('PlacesUIUtils' in window)) return;
if (!PlacesUIUtils.__treestyletab__done) {
var ns = Components.utils.import('resource:///modules/PlacesUIUtils.jsm', {});
var sv = this;
with (ns) {
{
let method = (TreeStyleTabUtils.getTreePref('compatibility.TabUtilities') && PlacesUIUtils.TU__openTabset) ?
'TU__openTabset' :
'_openTabset';
TreeStyleTabUtils.doPatching(PlacesUIUtils[method], 'PlacesUIUtils.'+method, function(aName, aSource) {
var patched = eval(aName+' = '+aSource.replace(
/(function[^\(]*\([^\)]+)(\))/,
'$1, aFolderTitle$2'
).replace(
'{',
'{ var TSTTreeStructure = null, TSTPreviousTabs, TSTTreeStructureApplied = true, TSTOpenGroupBookmarkBehavior;'
).replace(
'var urls = [];',
'$& var ids = [];'
).replace(
'urls.push(item.uri);',
'if (item.uri) { $& ids.push(item.id); }'
).replace(
'this.markPageAsTyped(item.uri);',
'if (item.uri) { $& }'
).replace(
/(browserWindow\.(?:getBrowser\(\)|gBrowser)\.loadTabs\([^;]+\);)/,
'var TSTResult = browserWindow.TreeStyleTabBookmarksService.handleTabsOpenProcess(where, aEvent, browserWindow, ids, urls, aFolderTitle);\n' +
'TSTTreeStructure = TSTResult.treeStructure;\n' +
'TSTPreviousTabs = TSTResult.previousTabs;\n' +
'TSTTreeStructureApplied = TSTResult.treeStructureApplied;\n' +
'TSTOpenGroupBookmarkBehavior = TSTResult.behavior;\n' +
'if (typeof replaceCurrentTab != "undefined")\n' +
' replaceCurrentTab = TSTResult.replaceCurrentTab;\n' +
'$1'
).replace(
/(\}\)?)$/,
' if (TSTTreeStructure && TSTPreviousTabs) {\n' +
' let tabs = browserWindow.TreeStyleTabService.getNewTabsFromPreviousTabsInfo(browserWindow.gBrowser, TSTPreviousTabs);\n' +
' if (!TSTTreeStructureApplied)\n' +
' browserWindow.TreeStyleTabService.applyTreeStructureToTabs(tabs, TSTTreeStructure, TSTOpenGroupBookmarkBehavior & browserWindow.TreeStyleTabBookmarksService.kGROUP_BOOKMARK_EXPAND_ALL_TREE);\n' +
' if (!loadInBackground) {\n' +
' browserWindow.setTimeout(function() {\n' +
' browserWindow.gBrowser.treeStyleTab.scrollToTabs(tabs);\n' +
' }, browserWindow.gBrowser.treeStyleTab.collapseDuration); // start scroll after expanding animation is finished\n' +
' }\n' +
' }\n' +
'$1'
));
if (TreeStyleTabUtils.getTreePref('compatibility.TabUtilities') && method.indexOf('TU_') > -1)
window[method] = patched;
return patched;
}, 'TreeStyleTab');
}
{
let method = (TreeStyleTabUtils.getTreePref('compatibility.TabUtilities') && PlacesUIUtils.TU_openContainerNodeInTabs) ?
'TU_openContainerNodeInTabs' :
'openContainerNodeInTabs';
TreeStyleTabUtils.doPatching(PlacesUIUtils[method], 'PlacesUIUtils.'+method, function(aName, aSource) {
var patched = eval(aName+' = '+aSource.replace(
/(this\._openTabset\([^\)]+)(\))/,
'{\n' +
' let w = "_getTopBrowserWin" in this ?\n' +
' this._getTopBrowserWin() :\n' +
' "_getCurrentActiveWin" in this ?\n' +
' this._getCurrentActiveWin() :\n' +
' window;\n' +
' let nodes = w.TreeStyleTabBookmarksService.getItemIdsForContainerNode(aNode);\n' +
' for (let i in nodes) {\n' +
' urlsToOpen[i].id = nodes[i];\n' +
' }\n' +
'}\n' +
'$1, aNode.title$2'
));
if (TreeStyleTabUtils.getTreePref('compatibility.TabUtilities') && method.indexOf('TU_') > -1)
window[method] = patched;
return patched;
}, 'TreeStyleTab');
}
{
let method = (TreeStyleTabUtils.getTreePref('compatibility.TabUtilities') && PlacesUIUtils.TU_openURINodesInTabs) ?
'TU_openURINodesInTabs' :
'openURINodesInTabs';
TreeStyleTabUtils.doPatching(PlacesUIUtils[method], 'PlacesUIUtils.'+method, function(aName, aSource) {
var patched = eval(aName+' = '+aSource.replace(
'{',
'{\n' +
' var TSTBS, TSTUtils;\n' +
' {\n'+
' let w = "_getTopBrowserWin" in this ?\n' +
' this._getTopBrowserWin() :\n' +
' "_getCurrentActiveWin" in this ?\n' +
' this._getCurrentActiveWin() :\n' +
' window;\n' +
' TSTBS = w.TreeStyleTabBookmarksService;\n' +
' TSTUtils = w.TreeStyleTabUtils;\n' +
' PlacesUtils = w.PlacesUtils;\n' +
' }'
).replace(
'uri: aNodes[i].uri,',
'id: aNodes[i].itemId, $&'
).replace(
/(this\._openTabset\([^\)]+)(\))/,
'$1,\n' +
' TSTUtils.treeBundle\n' +
' .getFormattedString(\n' +
' PlacesUtils.nodeIsBookmark(aNodes[0]) ?\n' +
' "openSelectedPlaces.bookmarks" :\n' +
' "openSelectedPlaces.history",\n' +
' [aNodes[0].title, aNodes.length]\n' +
' )\n' +
'$2'
));
if (TreeStyleTabUtils.getTreePref('compatibility.TabUtilities') && method.indexOf('TU_') > -1)
window[method] = patched;
return patched;
}, 'TreeStyleTab');
}
PlacesUIUtils.__treestyletab__done = true;
} // end of with
}
if ('PlacesCommandHook' in window && 'bookmarkCurrentPages' in PlacesCommandHook) {
// Bookmark All Tabs
TreeStyleTabUtils.doPatching(PlacesCommandHook.bookmarkCurrentPages, 'PlacesCommandHook.bookmarkCurrentPages', function(aName, aSource) {
return eval(aName+' = '+aSource.replace(
'{',
'{\n' +
' TreeStyleTabBookmarksService.beginAddBookmarksFromTabs((function() {\n' +
' var tabs = [];\n' +
' var seen = {};\n' +
' var allTabs = getBrowser().mTabContainer.childNodes;\n' +
' for (let i = 0, maxi = allTabs.length; i < maxi; i++)\n' +
' {\n' +
' let tab = allTabs[i];\n' +
' let uri = tab.linkedBrowser.currentURI.spec;\n' +
' if (uri in seen) continue;\n' +
' seen[uri] = true;\n' +
' tabs.push(tab);\n' +
' }\n' +
' return tabs;\n' +
' })());\n' +
' try {'
).replace(
/(\}\)?)$/,
' }\n' +
' catch(e) {\n' +
' }\n' +
' TreeStyleTabBookmarksService.endAddBookmarksFromTabs();\n' +
'$1'
));
}, 'TreeStyleTab');
}
},
handleTabsOpenProcess : function TSTBMService_handleTabsOpenProcess(aWhere, aEvent, aBrowserWindow, aIDs, aURLs, aFolderTitle)
{
var result = {
behavior : undefined,
treeStructure : undefined,
previousTabs : undefined,
treeStructureApplied : false
};
if (
aEvent.type != 'drop' &&
aWhere.indexOf('tab') != 0 &&
aEvent.target.id != 'placesContext_openContainer:tabs' &&
aEvent.target.id != 'placesContext_openLinks:tabs' &&
aEvent.target != aEvent.target.parentNode._endOptOpenAllInTabs &&
aEvent.target.getAttribute('openInTabs') != 'true'
)
return result;
var sv = aBrowserWindow.TreeStyleTabBookmarksService;
result.behavior = sv.openGroupBookmarkBehavior();
if (result.behavior & sv.kGROUP_BOOKMARK_SUBTREE) {
let treeStructure = result.behavior & sv.kGROUP_BOOKMARK_DONT_RESTORE_TREE_STRUCTURE ?
null :
sv.getTreeStructureFromItems(aIDs) ;
if (treeStructure) {
let parentTabs = treeStructure.filter(function(aParent) {
return aParent < 0;
});
let haveMultipleTrees = parentTabs.length != treeStructure.length;
if (result.behavior & sv.kGROUP_BOOKMARK_USE_DUMMY) {
let parentCount = 0;
let childCount = 0;
for (let i in treeStructure) {
if (treeStructure[i] == -1)
parentCount++;
else
childCount++;
}
if (
parentCount > 1 &&
(
result.behavior & sv.kGROUP_BOOKMARK_USE_DUMMY_FORCE ||
// when there is any orphan, then all of parents and orphans should be grouped under a dummy tab.
childCount < parentCount
)
) {
aIDs.unshift(-1);
treeStructure = sv.getTreeStructureFromItems(aIDs, 0);
aURLs.unshift(sv.getGroupTabURI({
title: aFolderTitle,
temporary: TreeStyleTabUtils.getTreePref('openGroupBookmark.temporaryGroup')
}));
}
}
else if (!haveMultipleTrees) {
// make the first item parent.
treeStructure = treeStructure.map(function(aParent, aIndex) {
if (aIndex == 0)
return aParent;
if (aParent < 0)
return 0;
return aParent;
});
}
}
result.treeStructure = treeStructure;
result.previousTabs = aBrowserWindow.TreeStyleTabService.getTabsInfo(aBrowserWindow.gBrowser);
if (TreeStyleTabUtils.getTreePref('compatibility.TMP') &&
'TMP_Places' in aBrowserWindow &&
'openGroup' in aBrowserWindow.TMP_Places) {
result.treeStructureApplied = false;
}
else {
sv.readyToOpenNewTabGroup(null, treeStructure, result.behavior & sv.kGROUP_BOOKMARK_EXPAND_ALL_TREE);
result.treeStructureApplied = true;
}
}
return result;
},
destroy : function TSTBMService_destroy()
{
window.removeEventListener('unload', this, false);
window.removeEventListener(window['piro.sakura.ne.jp'].tabsDragUtils.EVENT_TYPE_TABS_DROP, this, false);
},
// observer for nsINavBookmarksService
onItemAdded : function TSTBMService_onItemAdded(aID, aFolderID, aPosition)
{
this._addingBookmarks.push({
id : aID,
parent : aFolderID
});
},
onItemRemoved : function TSTBMService_onItemRemoved(aID, aFolderID, aPosition) {},
onItemMoved : function TSTBMService_onItemMoved(aID, aFolderID, aPosition) {},
onItemChanged : function TSTBMService_onItemChanged(aID, aChange, aIsAnnotation, aNewValue) {},
onItemVisited : function TSTBMService_onItemVisited(aID, aHistoryID, aDate) {},
onBeginUpdateBatch : function TSTBMService_onBeginUpdateBatch() {},
onEndUpdateBatch : function TSTBMService_onEndUpdateBatch() {},
_onTabsDrop : function TSTBMService_onTabsDrop(aEvent)
{
var tabs = aEvent.detail.tabs;
var groups = this.splitTabsToSubtrees(tabs);
if (
groups.length == 1 &&
this.bookmarkDroppedTabsBehavior() != this.kBOOKMARK_DROPPED_TABS_ALL &&
!Array.some(tabs, function(aTab) {
return aTab.getAttribute('multiselected') == 'true';
})
) {
aEvent.preventDefault();
aEvent.stopPropagation();
}
},
handleEvent : function TSTBMService_handleEvent(aEvent)
{
switch (aEvent.type)
{
case 'load':
return this.init();
case 'unload':
return this.destroy();
case window['piro.sakura.ne.jp'].tabsDragUtils.EVENT_TYPE_TABS_DROP:
return this._onTabsDrop(aEvent);
}
}
});
TreeStyleTabBookmarksService.preInit();
window.TreeStyleTabBookmarksService = TreeStyleTabBookmarksService;
})();