treestyletab/modules/base.js

2748 lines
88 KiB
JavaScript
Raw Normal View History

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Tree Style Tab.
*
2012-10-13 14:31:06 -04:00
* The Initial Developer of the Original Code is YUKI "Piro" Hiroshi.
2012-01-13 11:15:12 -05:00
* Portions created by the Initial Developer are Copyright (C) 2010-2012
* the Initial Developer. All Rights Reserved.
*
2012-10-13 14:31:06 -04:00
* Contributor(s): YUKI "Piro" Hiroshi <piro.outsider.reflex@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ******/
const EXPORTED_SYMBOLS = ['TreeStyleTabBase'];
const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
2012-11-11 08:48:42 -05:00
XPCOMUtils.defineLazyModuleGetter(this, 'Services', 'resource://gre/modules/Services.jsm');
2011-05-25 23:11:08 -04:00
Components.utils.import('resource://treestyletab-modules/lib/prefs.js');
Components.utils.import('resource://treestyletab-modules/lib/namespace.jsm');
var window = getNamespaceFor('piro.sakura.ne.jp');
2011-05-25 23:11:08 -04:00
2012-11-11 08:48:42 -05:00
XPCOMUtils.defineLazyModuleGetter(this, 'Deferred', 'resource://treestyletab-modules/lib/jsdeferred.js');
2011-05-25 23:11:08 -04:00
XPCOMUtils.defineLazyGetter(this, 'jstimer', function() {
var jstimer = {};
Components.utils.import('resource://treestyletab-modules/lib/jstimer.jsm', jstimer);
return jstimer;
});
XPCOMUtils.defineLazyGetter(this, 'extensions', function() {
Components.utils.import('resource://treestyletab-modules/lib/extensions.js', {});
return window['piro.sakura.ne.jp'].extensions;
});
XPCOMUtils.defineLazyGetter(this, 'animationManager', function() {
Components.utils.import('resource://treestyletab-modules/lib/animationManager.js', {});
return window['piro.sakura.ne.jp'].animationManager;
});
XPCOMUtils.defineLazyGetter(this, 'autoScroll', function() {
Components.utils.import('resource://treestyletab-modules/lib/autoScroll.js', {});
return window['piro.sakura.ne.jp'].autoScroll;
});
XPCOMUtils.defineLazyModuleGetter(this, 'UninstallationListener',
'resource://treestyletab-modules/lib/UninstallationListener.js');
2012-11-11 08:48:42 -05:00
XPCOMUtils.defineLazyModuleGetter(this, 'confirmWithPopup', 'resource://treestyletab-modules/lib/confirmWithPopup.js');
XPCOMUtils.defineLazyModuleGetter(this, 'utils', 'resource://treestyletab-modules/utils.js', 'TreeStyleTabUtils');
if (Services.appinfo.OS === 'WINNT') {
XPCOMUtils.defineLazyModuleGetter(this, 'AeroPeek',
'resource://gre/modules/WindowsPreviewPerTab.jsm', 'AeroPeek');
}
var TreeStyleTabBase = {
2011-05-25 23:11:08 -04:00
__proto__ : window['piro.sakura.ne.jp'].prefs,
tabsHash : null,
inWindowDestoructionProcess : false,
/* attributes */
kID : 'treestyletab-id',
kCHILDREN : 'treestyletab-children',
kPARENT : 'treestyletab-parent',
kANCESTOR : 'treestyletab-ancestors',
kNEST : 'treestyletab-nest',
kINSERT_BEFORE : 'treestyletab-insert-before',
kINSERT_AFTER : 'treestyletab-insert-after',
kCLOSED_SET_ID : 'treestyletab-closed-set-id',
kID_NEW : 'treestyletab-id-new',
kID_RESTORING : 'treestyletab-id-restoring',
kCHILDREN_RESTORING : 'treestyletab-children-restoring',
kSUBTREE_COLLAPSED : 'treestyletab-subtree-collapsed',
kSUBTREE_EXPANDED_MANUALLY : 'treestyletab-subtree-expanded-manually',
kCOLLAPSED : 'treestyletab-collapsed',
kCOLLAPSED_DONE : 'treestyletab-collapsed-done',
kCOLLAPSING_PHASE : 'treestyletab-collapsing-phase',
kCOLLAPSING_PHASE_TO_BE_COLLAPSED : 'collapse',
kCOLLAPSING_PHASE_TO_BE_EXPANDED : 'expand',
kALLOW_COLLAPSE : 'treestyletab-allow-subtree-collapse',
kALLOW_STACK : 'treestyletab-stack-collapsed-tabs',
kREMOVED : 'treestyletab-removed',
kX_OFFSET : 'treestyletab-x-offset',
kY_OFFSET : 'treestyletab-y-offset',
kTABBAR_POSITION : 'treestyletab-tabbar-position',
kMODE : 'treestyletab-mode',
kHIDE_NEWTAB : 'treestyletab-hide-newtab-button',
kSTYLE : 'treestyletab-style',
kFIRSTTAB_BORDER : 'treestyletab-firsttab-border',
kFIXED : 'treestyletab-tabbar-fixed',
kRESIZING : 'treestyletab-tabbar-resizing',
kINDENTED : 'treestyletab-tabs-indented',
kMAX_LEVEL : 'treestyletab-max-tree-level',
kPRINT_PREVIEW : 'treestyletab-print-preview',
kANIMATION_ENABLED : 'treestyletab-animation-enabled',
kINVERT_SCROLLBAR : 'treestyletab-invert-scrollbar',
kNARROW_SCROLLBAR : 'treestyletab-narrow-scrollbar',
kFAVICONIZED : 'treestyletab-faviconized',
kBG_NOTIFY_PHASE : 'treestyletab-notifybgtab-phase',
kIGNORE_POPUP_STATE : 'treestyletab-ignore-state',
kTAB_INVERTED : 'treestyletab-tab-inverted',
kTAB_CONTENTS_INVERTED : 'treestyletab-tab-contents-inverted',
kCLOSEBOX_INVERTED : 'treestyletab-closebox-inverted',
kTWISTY_HOVER : 'treestyletab-twisty-hover',
kTWISTY_STYLE : 'treestyletab-twisty-style',
kDROP_POSITION : 'treestyletab-drop-position',
kDRAG_TYPE_TABBAR : 'application/x-moz-treestyletab-tabbrowser-tabbar',
kDROP_POSITION_UNKNOWN : 'unknown',
kTABBAR_MOVE_FORCE : 'force',
kTABBAR_MOVE_NORMAL : 'normal',
/* classes */
kTWISTY : 'treestyletab-twisty',
kCOUNTER : 'treestyletab-counter',
kCOUNTER_CONTAINER : 'treestyletab-counter-container',
kCOUNTER_PAREN : 'treestyletab-counter-paren',
kSPLITTER : 'treestyletab-splitter',
kTABBAR_TOGGLER : 'treestyletab-tabbar-toggler',
kTABBAR_PLACEHOLDER : 'treestyletab-tabbar-placeholder',
kTABBAR_TOOLBAR : 'treestyletab-tabbar-toolbar',
kTABBAR_TOOLBAR_READY : 'treestyletab-tabbar-toolbar-ready',
kTABBAR_TOOLBAR_READY_POPUP : 'treestyletab-tabbar-toolbar-ready-popup',
/* event types, topics */
kEVENT_TYPE_TAB_FOCUS_SWITCHING_KEY_DOWN : 'nsDOMTreeStyleTabFocusSwitchingKeyDown',
kEVENT_TYPE_TAB_FOCUS_SWITCHING_START : 'nsDOMTreeStyleTabFocusSwitchingStart',
kEVENT_TYPE_TAB_FOCUS_SWITCHING_END : 'nsDOMTreeStyleTabFocusSwitchingEnd',
kTAB_FOCUS_SWITCHING_SCROLL_DOWN : (1 << 0),
kTAB_FOCUS_SWITCHING_SCROLL_UP : (1 << 1),
kTAB_FOCUS_SWITCHING_STAND_BY : (1 << 2),
kTAB_FOCUS_SWITCHING_ONLY_SHIFT_KEY : (1 << 3),
kEVENT_TYPE_SUBTREE_CLOSING : 'nsDOMTreeStyleTabSubtreeClosing',
kEVENT_TYPE_SUBTREE_CLOSED : 'nsDOMTreeStyleTabSubtreeClosed',
kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED : 'nsDOMTreeStyleTabCollapsedStateChange',
kEVENT_TYPE_TABBAR_INITIALIZED : 'nsDOMTreeStyleTabTabbarInitialized',
kEVENT_TYPE_TABBAR_POSITION_CHANGING : 'nsDOMTreeStyleTabTabbarPositionChanging',
kEVENT_TYPE_TABBAR_POSITION_CHANGED : 'nsDOMTreeStyleTabTabbarPositionChanged',
kEVENT_TYPE_TABBAR_STATE_CHANGING : 'nsDOMTreeStyleTabTabbarStateChanging',
kEVENT_TYPE_TABBAR_STATE_CHANGED : 'nsDOMTreeStyleTabTabbarStateChanged',
kEVENT_TYPE_FOCUS_NEXT_TAB : 'nsDOMTreeStyleTabFocusNextTab',
kEVENT_TYPE_ATTACHED : 'nsDOMTreeStyleTabAttached',
kEVENT_TYPE_DETACHED : 'nsDOMTreeStyleTabParted',
kEVENT_TYPE_PRINT_PREVIEW_ENTERED : 'nsDOMTreeStyleTabPrintPreviewEntered',
kEVENT_TYPE_PRINT_PREVIEW_EXITED : 'nsDOMTreeStyleTabPrintPreviewExited',
kEVENT_TYPE_AUTO_HIDE_STATE_CHANGING : 'nsDOMTreeStyleTabAutoHideStateChanging',
kEVENT_TYPE_AUTO_HIDE_STATE_CHANGE : 'nsDOMTreeStyleTabAutoHideStateChange',
kEVENT_TYPE_BEFORE_TOOLBAR_CUSTOMIZATION : 'nsDOMTreeStyleTabBeforeToolbarCustomization',
kEVENT_TYPE_AFTER_TOOLBAR_CUSTOMIZATION : 'nsDOMTreeStyleTabAfterToolbarCustomization',
kTOPIC_INDENT_MODIFIED : 'TreeStyleTab:indentModified',
kTOPIC_COLLAPSE_EXPAND_ALL : 'TreeStyleTab:collapseExpandAllSubtree',
kTOPIC_CHANGE_TREEVIEW_AVAILABILITY : 'TreeStyleTab:changeTreeViewAvailability',
/* other constant values */
kFOCUS_ALL : 0,
kFOCUS_VISIBLE : 1,
kDROP_BEFORE : -1,
kDROP_ON : 0,
kDROP_AFTER : 1,
kACTION_MOVE : 1 << 0,
kACTION_STAY : 1 << 1,
kACTION_DUPLICATE : 1 << 2,
kACTION_IMPORT : 1 << 3,
kACTION_NEWTAB : 1 << 4,
kACTION_ATTACH : 1 << 10,
kACTION_PART : 1 << 11,
kACTIONS_FOR_SOURCE : (1 << 0) | (1 << 1),
kACTIONS_FOR_DESTINATION : (1 << 2) | (1 << 3),
kTABBAR_TOP : 1 << 0,
kTABBAR_BOTTOM : 1 << 1,
kTABBAR_LEFT : 1 << 2,
kTABBAR_RIGHT : 1 << 3,
kTABBAR_HORIZONTAL : (1 << 0) | (1 << 1),
kTABBAR_VERTICAL : (1 << 2) | (1 << 3),
kTABBAR_REGULAR : (1 << 0) | (1 << 2),
kTABBAR_INVERTED : (1 << 3) | (1 << 4),
kINSERT_FISRT : 0,
kINSERT_LAST : 1,
kTABBAR_UPDATE_BY_UNKNOWN_REASON : (1 << 0),
kTABBAR_UPDATE_BY_RESET : (1 << 1),
kTABBAR_UPDATE_BY_PREF_CHANGE : (1 << 2),
kTABBAR_UPDATE_BY_APPEARANCE_CHANGE : (1 << 3),
kTABBAR_UPDATE_BY_SHOWHIDE_TABBAR : (1 << 4),
kTABBAR_UPDATE_BY_TABBAR_RESIZE : (1 << 5),
kTABBAR_UPDATE_BY_WINDOW_RESIZE : (1 << 6),
kTABBAR_UPDATE_BY_FULLSCREEN : (1 << 7),
kTABBAR_UPDATE_BY_PRIVATE_BROWSING : (1 << 8),
kTABBAR_UPDATE_BY_AUTOHIDE : (1 << 9),
kTABBAR_UPDATE_BY_INITIALIZE : (1 << 10),
kTABBAR_UPDATE_BY_TOGGLE_SIDEBAR : (1 << 11),
kTABBAR_UPDATE_NOW : (1 << 5) | (1 << 6) | (1 << 9) | (1 << 10),
kTABBAR_UPDATE_SYNC_TO_TABBAR : (1 << 0) | (1 << 1) | (1 << 2) | (1 << 5) | (1 << 8) | (1 << 9),
kTABBAR_UPDATE_SYNC_TO_PLACEHOLDER : (1 << 3) | (1 << 4) | (1 << 6) | (1 << 7) | (1 << 10) | (1 << 11),
kCLOSE_PARENT_BEHAVIOR_PROMOTE_FIRST_CHILD : 3,
kCLOSE_PARENT_BEHAVIOR_PROMOTE_ALL_CHILDREN : 0,
kCLOSE_PARENT_BEHAVIOR_DETACH_ALL_CHILDREN : 1,
kCLOSE_PARENT_BEHAVIOR_SIMPLY_DETACH_ALL_CHILDREN : 4,
kCLOSE_PARENT_BEHAVIOR_CLOSE_ALL_CHILDREN : 2, // onTabRemoved only
kRESTORE_TREE_LEVEL_NONE : 0,
kRESTORE_TREE_ONLY_VISIBLE : 1,
kRESTORE_TREE_ALL : 2,
kCOUNTER_ROLE_ALL_TABS : 1,
kCOUNTER_ROLE_CONTAINED_TABS : 2,
MAX_TABBAR_SIZE_RATIO : 0.8,
DEFAULT_SHRUNKEN_WIDTH_RATIO : 0.67,
MIN_TABBAR_WIDTH : 24,
MIN_TABBAR_HEIGHT : 24,
/* base variables */
baseIndentVertical : 12,
baseIndentHorizontal : 4,
shouldDetectClickOnIndentSpaces : true,
smoothScrollEnabled : true,
smoothScrollDuration : 150,
animationEnabled : true,
indentDuration : 200,
collapseDuration : 150,
shouldExpandTwistyArea : true,
scrollToNewTabMode : false,
counterRoleHorizontal : -1,
counterRoleVertical : -1,
get SessionStore() {
if (!this._SessionStore) {
this._SessionStore = Cc['@mozilla.org/browser/sessionstore;1'].getService(Ci.nsISessionStore);
}
return this._SessionStore;
},
_SessionStore : null,
get FocusManager()
{
2012-01-13 03:38:12 -05:00
if (!this._FocusManager) {
this._FocusManager = Cc['@mozilla.org/focus-manager;1'].getService(Ci.nsIFocusManager);
}
return this._FocusManager;
},
2012-01-13 03:38:12 -05:00
_FocusManager : null,
get isGecko10OrLater()
{
2012-09-17 22:59:48 -04:00
return Services.vc.compare(Services.appinfo.version, '10.0a') > 0;
},
2011-05-25 23:11:08 -04:00
get extensions() { return extensions; },
get animationManager() { return animationManager; },
get autoScroll() { return autoScroll; },
2011-05-25 23:15:47 -04:00
get Deferred() { return Deferred; },
get AeroPeek() { return AeroPeek; }, // for Windows
init : function utils_init()
{
if (this._initialized) return;
2012-09-17 13:15:08 -04:00
this.isMac = Services.appinfo.OS == 'Darwin';
this.applyPlatformDefaultPrefs();
this.migratePrefs();
this.addPrefListener(this);
this.initUninstallationListener();
this.onPrefChange('extensions.treestyletab.indent.vertical');
this.onPrefChange('extensions.treestyletab.indent.horizontal');
this.onPrefChange('extensions.treestyletab.clickOnIndentSpaces.enabled');
this.onPrefChange('browser.tabs.loadFolderAndReplace.override');
this.onPrefChange('browser.tabs.insertRelatedAfterCurrent.override');
this.onPrefChange('extensions.stm.tabBarMultiRows.override'); // Super Tab Mode
this.onPrefChange('extensions.treestyletab.tabbar.scroll.smooth');
this.onPrefChange('extensions.treestyletab.tabbar.scroll.duration');
this.onPrefChange('extensions.treestyletab.tabbar.scrollToNewTab.mode');
this.onPrefChange('extensions.treestyletab.tabbar.narrowScrollbar.size');
this.onPrefChange('browser.tabs.animate');
this.onPrefChange('extensions.treestyletab.animation.indent.duration');
this.onPrefChange('extensions.treestyletab.animation.collapse.duration');
this.onPrefChange('extensions.treestyletab.twisty.expandSensitiveArea');
this.onPrefChange('extensions.treestyletab.counter.role.horizontal');
this.onPrefChange('extensions.treestyletab.counter.role.vertical');
2011-10-27 15:17:22 -04:00
try {
this.overrideExtensions();
}
catch(e) {
dump(e+'\n');
}
},
_initialized : false,
applyPlatformDefaultPrefs : function utils_applyPlatformDefaultPrefs()
{
2012-09-17 13:15:08 -04:00
var OS = Services.appinfo.OS;
var processed = {};
2012-02-04 16:31:03 -05:00
var originalKeys = this.getDescendant('extensions.treestyletab.platform.'+OS);
for (let i = 0, maxi = originalKeys.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let originalKey = originalKeys[i];
let key = originalKey.replace('platform.'+OS+'.', '');
this.setDefaultPref(key, this.getPref(originalKey));
processed[key] = true;
}
2012-02-04 16:31:03 -05:00
originalKeys = this.getDescendant('extensions.treestyletab.platform.default');
for (let i = 0, maxi = originalKeys.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let originalKey = originalKeys[i];
let key = originalKey.replace('platform.default.', '');
if (!(key in processed))
this.setDefaultPref(key, this.getPref(originalKey));
}
},
kPREF_VERSION : 9,
migratePrefs : function utils_migratePrefs()
{
// migrate old prefs
var orientalPrefs = [];
switch (utils.getTreePref('prefsVersion'))
{
case 0:
orientalPrefs = orientalPrefs.concat([
'extensions.treestyletab.tabbar.fixed',
'extensions.treestyletab.enableSubtreeIndent',
'extensions.treestyletab.allowSubtreeCollapseExpand'
]);
2012-02-10 13:09:24 -05:00
case 1:
case 2:
if (utils.getTreePref('urlbar.loadSameDomainToNewChildTab') !== null) {
let value = utils.getTreePref('urlbar.loadSameDomainToNewChildTab');
utils.setTreePref('urlbar.loadSameDomainToNewTab', value);
utils.setTreePref('urlbar.loadSameDomainToNewTab.asChild', value);
if (value) utils.setTreePref('urlbar.loadDifferentDomainToNewTab', value);
utils.clearTreePref('urlbar.loadSameDomainToNewChildTab');
}
case 3:
if (utils.getTreePref('loadDroppedLinkToNewChildTab') !== null) {
utils.setTreePref('dropLinksOnTab.behavior',
utils.getTreePref('loadDroppedLinkToNewChildTab.confirm') ?
this.kDROPLINK_ASK :
utils.getTreePref('loadDroppedLinkToNewChildTab') ?
this.kDROPLINK_NEWTAB :
this.kDROPLINK_LOAD
);
utils.clearTreePref('loadDroppedLinkToNewChildTab.confirm');
utils.clearTreePref('loadDroppedLinkToNewChildTab');
}
if (utils.getTreePref('openGroupBookmarkAsTabSubTree') !== null) {
let behavior = 0;
if (utils.getTreePref('openGroupBookmarkAsTabSubTree.underParent'))
behavior += this.kGROUP_BOOKMARK_USE_DUMMY;
if (!utils.getTreePref('openGroupBookmarkBehavior.confirm')) {
behavior += (
utils.getTreePref('openGroupBookmarkAsTabSubTree') ?
this.kGROUP_BOOKMARK_SUBTREE :
utils.getTreePref('browser.tabs.loadFolderAndReplace') ?
this.kGROUP_BOOKMARK_REPLACE :
this.kGROUP_BOOKMARK_SEPARATE
);
}
utils.setTreePref('openGroupBookmark.behavior', behavior);
utils.clearTreePref('openGroupBookmarkBehavior.confirm');
utils.clearTreePref('openGroupBookmarkAsTabSubTree');
utils.clearTreePref('openGroupBookmarkAsTabSubTree.underParent');
this.setPref('browser.tabs.loadFolderAndReplace', !!(behavior & this.kGROUP_BOOKMARK_REPLACE));
}
case 4:
let (prefs = [
'extensions.treestyletab.autoCollapseExpandSubTreeOnSelect',
'extensions.treestyletab.autoCollapseExpandSubTreeOnSelect.onCurrentTabRemove',
'extensions.treestyletab.autoCollapseExpandSubTreeOnSelect.whileFocusMovingByShortcut',
'extensions.treestyletab.autoExpandSubTreeOnAppendChild',
'extensions.treestyletab.autoExpandSubTreeOnCollapsedChildFocused',
'extensions.treestyletab.collapseExpandSubTree.dblclick',
'extensions.treestyletab.createSubTree.underParent',
'extensions.treestyletab.show.context-item-reloadTabSubTree',
'extensions.treestyletab.show.context-item-removeTabSubTree',
'extensions.treestyletab.show.context-item-bookmarkTabSubTree',
'extensions.multipletab.show.multipletab-selection-item-removeTabSubTree',
'extensions.multipletab.show.multipletab-selection-item-createSubTree'
]) {
for (let i = 0, maxi = prefs.length; i < maxi; i++)
{
let pref = prefs[i];
let value = this.getPref(pref);
if (value === null) continue;
this.setPref(pref.replace('SubTree', 'Subtree'), value);
this.clearPref(pref);
}
}
case 5:
let (behavior = utils.getTreePref('openGroupBookmark.behavior')) {
behavior = behavior | 2048;
utils.setTreePref('openGroupBookmark.behavior', behavior);
}
case 6:
let (
general = utils.getTreePref('autoAttachNewTabsAsChildren'),
search = utils.getTreePref('autoAttachSearchResultAsChildren')
) {
if (general !== null)
utils.setTreePref('autoAttach', general);
if (search !== null)
utils.setTreePref('autoAttach.searchResult', search);
}
case 7:
let (
enabled = utils.getTreePref('autoCollapseExpandSubtreeOnSelect.whileFocusMovingByShortcut'),
delay = utils.getTreePref('autoCollapseExpandSubtreeOnSelect.whileFocusMovingByShortcut.delay')
) {
if (enabled !== null) {
utils.setTreePref('autoExpandSubtreeOnSelect.whileFocusMovingByShortcut', enabled);
utils.setTreePref('autoExpandSubtreeOnSelect.whileFocusMovingByShortcut.collapseOthers', enabled);
}
if (delay !== null)
utils.setTreePref('autoExpandSubtreeOnSelect.whileFocusMovingByShortcut.delay', delay);
}
case 8:
orientalPrefs = orientalPrefs.concat([
'extensions.treestyletab.indent',
'extensions.treestyletab.indent.min'
]);
default:
for (let i = 0, maxi = orientalPrefs.length; i < maxi; i++)
{
let pref = orientalPrefs[i];
let value = this.getPref(pref);
if (value === null) continue;
this.setPref(pref+'.horizontal', value);
this.setPref(pref+'.vertical', value);
this.clearPref(pref);
}
break;
}
utils.setTreePref('prefsVersion', this.kPREF_VERSION);
},
initUninstallationListener : function TSTWindow_initUninstallationListener()
{
var restorePrefs = function() {
// Remove pref listener before restore backuped prefs.
prefs.removePrefListener(this);
let restorePrefs = [
'browser.tabs.loadFolderAndReplace',
'browser.tabs.insertRelatedAfterCurrent',
'extensions.stm.tabBarMultiRows' // Super Tab Mode
];
for (let i = 0, maxi = restorePrefs.length; i < maxi; i++)
{
let pref = restorePrefs[i];
let backup = prefs.getPref(pref+'.backup');
if (backup === null) continue;
// restore user preference.
prefs.setPref(pref, backup);
// clear backup pref.
prefs.clearPref(pref+'.backup');
}
}.bind(this);
new UninstallationListener({
id : 'treestyletab@piro.sakura.ne.jp',
onuninstalled : restorePrefs,
ondisabled : restorePrefs
});
},
overrideExtensions : function utils_overrideExtensions()
2011-10-27 15:17:22 -04:00
{
// Scriptish
// https://addons.mozilla.org/firefox/addon/scriptish/
if (utils.getTreePref('compatibility.Scriptish')) {
try {
let tabModule = Components.utils.import('resource://scriptish/utils/Scriptish_openInTab.js', {});
let Scriptish_openInTab = tabModule.Scriptish_openInTab;
tabModule.Scriptish_openInTab = function(aURL, aLoadInBackground, aReuse, aChromeWin) {
aChromeWin.TreeStyleTabService.readyToOpenChildTabNow(aChromeWin.gBrowser);
return Scriptish_openInTab.apply(this, arguments);
};
}
catch(e) {
}
}
2011-10-27 15:17:22 -04:00
},
updateNarrowScrollbarStyle : function utils_updateNarrowScrollbarStyle()
{
const SSS = Cc['@mozilla.org/content/style-sheet-service;1']
.getService(Ci.nsIStyleSheetService);
if (this.lastAgentSheet &&
SSS.sheetRegistered(this.lastAgentSheet, SSS.AGENT_SHEET))
SSS.unregisterSheet(this.lastAgentSheet, SSS.AGENT_SHEET);
const style = 'data:text/css,'+encodeURIComponent(
2012-10-12 03:46:37 -04:00
('@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");' +
'tabs.tabbrowser-tabs[%MODE%="vertical"][%NARROW%="true"]' +
' .tabbrowser-arrowscrollbox' +
' > scrollbox' +
' > scrollbar[orient="vertical"],' +
'tabs.tabbrowser-tabs[%MODE%="vertical"][%NARROW%="true"]' +
' .tabbrowser-arrowscrollbox' +
' > scrollbox' +
' > scrollbar[orient="vertical"] * {' +
' max-width: %SIZE%;' +
' min-width: %SIZE%;' +
'}' +
'tabs.tabbrowser-tabs[%MODE%="vertical"][%NARROW%="true"]' +
' .tabbrowser-arrowscrollbox' +
' > scrollbox' +
' > scrollbar[orient="vertical"] {' +
' font-size: %SIZE%;' +
'}' +
'tabs.tabbrowser-tabs[%MODE%="vertical"][%NARROW%="true"]' +
' .tabbrowser-arrowscrollbox' +
' > scrollbox' +
' > scrollbar[orient="vertical"] * {' +
' padding-left: 0;' +
' padding-right: 0;' +
' margin-left: 0;' +
' margin-right: 0;' +
'}' +
'%FORCE_NARROW_SCROLLBAR%')
.replace(/%FORCE_NARROW_SCROLLBAR%/g,
utils.getTreePref('tabbar.narrowScrollbar.overrideSystemAppearance') ?
this.kOVERRIDE_SYSTEM_SCROLLBAR_APPEARANCE : '' )
.replace(/%MODE%/g, this.kMODE)
.replace(/%NARROW%/g, this.kNARROW_SCROLLBAR)
.replace(/%SIZE%/g, utils.getTreePref('tabbar.narrowScrollbar.size'))
);
this.lastAgentSheet = this.makeURIFromSpec(style);
SSS.loadAndRegisterSheet(this.lastAgentSheet, SSS.AGENT_SHEET);
},
2012-10-12 03:46:37 -04:00
kOVERRIDE_SYSTEM_SCROLLBAR_APPEARANCE :
'tabs.tabbrowser-tabs[%MODE%="vertical"][%NARROW%="true"]' +
' .tabbrowser-arrowscrollbox' +
' > scrollbox' +
' > scrollbar[orient="vertical"] {' +
' appearance: none;' +
' -moz-appearance: none;' +
' background: ThreeDFace;' +
' border: 1px solid ThreeDShadow;' +
'}',
lastAgentSheet : null,
observe : function utils_observe(aSubject, aTopic, aData)
{
switch (aTopic)
{
case 'nsPref:changed':
this.onPrefChange(aData);
return;
}
},
/* utilities */
get browserWindow()
{
return this.topBrowserWindow;
},
get topBrowserWindow()
{
return Services.wm.getMostRecentWindow('navigator:browser');
},
get browserWindows()
{
var windows = [];
var targets = Services.wm.getZOrderDOMWindowEnumerator('navigator:browser', true);
// By the bug 156333, we cannot find windows by their Z order on Linux.
// https://bugzilla.mozilla.org/show_bug.cgi?id=156333
if (!targets.hasMoreElements())
targets = Services.wm.getEnumerator('navigator:browser');
while (targets.hasMoreElements())
{
let target = targets.getNext()
2011-07-20 06:59:59 -04:00
.QueryInterface(Ci.nsIDOMWindow);
if ('nsIDOMWindowInternal' in Ci) // for Firefox 7 or olders
target = target.QueryInterface(Ci.nsIDOMWindowInternal);
windows.push(target);
}
return windows;
},
get browser()
{
var w = this.browserWindow;
return !w ? null :
'SplitBrowser' in w ? w.SplitBrowser.activeBrowser :
w.gBrowser ;
},
get window()
{
return this.browser.ownerDocument.defaultView;
},
get currentDragSession()
{
return Cc['@mozilla.org/widget/dragservice;1']
.getService(Ci.nsIDragService)
.getCurrentSession();
},
dropLinksOnTabBehavior : function utils_dropLinksOnTabBehavior()
{
var behavior = utils.getTreePref('dropLinksOnTab.behavior');
if (behavior & this.kDROPLINK_FIXED) return behavior;
var checked = { value : false };
var newChildTab = Services.prompt.confirmEx(this.browserWindow,
utils.treeBundle.getString('dropLinkOnTab.title'),
utils.treeBundle.getString('dropLinkOnTab.text'),
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1),
utils.treeBundle.getString('dropLinkOnTab.openNewChildTab'),
utils.treeBundle.getString('dropLinkOnTab.loadInTheTab'),
null,
utils.treeBundle.getString('dropLinkOnTab.never'),
checked
) == 0;
behavior = newChildTab ? this.kDROPLINK_NEWTAB : this.kDROPLINK_LOAD ;
if (checked.value)
utils.setTreePref('dropLinksOnTab.behavior', behavior);
return behavior
},
kDROPLINK_ASK : 0,
kDROPLINK_FIXED : 1 + 2,
kDROPLINK_LOAD : 1,
kDROPLINK_NEWTAB : 2,
openGroupBookmarkBehavior : function utils_openGroupBookmarkBehavior()
{
var behavior = utils.getTreePref('openGroupBookmark.behavior');
if (behavior & this.kGROUP_BOOKMARK_FIXED) return behavior;
var dummyTabFlag = behavior & this.kGROUP_BOOKMARK_USE_DUMMY;
var checked = { value : false };
var button = Services.prompt.confirmEx(this.browserWindow,
utils.treeBundle.getString('openGroupBookmarkBehavior.title'),
utils.treeBundle.getString('openGroupBookmarkBehavior.text'),
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1) +
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2),
utils.treeBundle.getString('openGroupBookmarkBehavior.subTree'),
utils.treeBundle.getString('openGroupBookmarkBehavior.separate'),
utils.treeBundle.getString('openGroupBookmarkBehavior.replace'),
utils.treeBundle.getString('openGroupBookmarkBehavior.never'),
checked
);
if (button < 0) button = 1;
var behaviors = [
this.kGROUP_BOOKMARK_SUBTREE | dummyTabFlag,
this.kGROUP_BOOKMARK_SEPARATE,
this.kGROUP_BOOKMARK_REPLACE
];
behavior = behaviors[button];
if (checked.value) {
utils.setTreePref('openGroupBookmark.behavior', behavior);
2011-01-22 13:32:04 -05:00
this.setPref('browser.tabs.loadFolderAndReplace', !!(behavior & this.kGROUP_BOOKMARK_REPLACE));
}
return behavior;
},
kGROUP_BOOKMARK_ASK : 0,
kGROUP_BOOKMARK_FIXED : 1 + 2 + 4,
kGROUP_BOOKMARK_SUBTREE : 1,
kGROUP_BOOKMARK_SEPARATE : 2,
kGROUP_BOOKMARK_REPLACE : 4,
kGROUP_BOOKMARK_USE_DUMMY : 256,
kGROUP_BOOKMARK_USE_DUMMY_FORCE : 1024,
kGROUP_BOOKMARK_DONT_RESTORE_TREE_STRUCTURE : 512,
kGROUP_BOOKMARK_EXPAND_ALL_TREE : 2048,
bookmarkDroppedTabsBehavior : function utils_bookmarkDroppedTabsBehavior()
{
var behavior = utils.getTreePref('bookmarkDroppedTabs.behavior');
if (behavior & this.kBOOKMARK_DROPPED_TABS_FIXED) return behavior;
var checked = { value : false };
var button = Services.prompt.confirmEx(this.browserWindow,
utils.treeBundle.getString('bookmarkDroppedTabs.title'),
utils.treeBundle.getString('bookmarkDroppedTabs.text'),
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1),
utils.treeBundle.getString('bookmarkDroppedTabs.bookmarkAll'),
utils.treeBundle.getString('bookmarkDroppedTabs.bookmarkOnlyParent'),
null,
utils.treeBundle.getString('bookmarkDroppedTabs.never'),
checked
);
if (button < 0) button = 1;
var behaviors = [
this.kBOOKMARK_DROPPED_TABS_ALL,
this.kBOOKMARK_DROPPED_TABS_ONLY_PARENT
];
behavior = behaviors[button];
if (checked.value)
utils.setTreePref('bookmarkDroppedTabs.behavior', behavior);
return behavior;
},
kBOOKMARK_DROPPED_TABS_ASK : 0,
kBOOKMARK_DROPPED_TABS_FIXED : 1 | 2,
kBOOKMARK_DROPPED_TABS_ALL : 1,
kBOOKMARK_DROPPED_TABS_ONLY_PARENT : 2,
askUndoCloseTabSetBehavior : function utils_askUndoCloseTabSetBehavior(aRestoredTab, aCount)
{
var behavior = this.undoCloseTabSetBehavior;
if (behavior & this.kUNDO_CLOSE_SET) behavior ^= this.kUNDO_CLOSE_SET;
var self = this;
return confirmWithPopup({
browser : aRestoredTab.linkedBrowser,
label : utils.treeBundle.getFormattedString('undoCloseTabSetBehavior.label', [aCount]),
value : 'treestyletab-undo-close-tree',
image : 'chrome://treestyletab/content/res/icon.png',
buttons : [
utils.treeBundle.getString('undoCloseTabSetBehavior.restoreOnce'),
utils.treeBundle.getString('undoCloseTabSetBehavior.restoreForever'),
utils.treeBundle.getString('undoCloseTabSetBehavior.ignoreForever')
],
persistence : -1 // don't hide even if the tab is restored after the panel is shown.
})
.next(function(aButtonIndex) {
if (aButtonIndex < 2) {
behavior |= self.kUNDO_CLOSE_SET;
}
if (aButtonIndex > 0) {
behavior ^= self.kUNDO_ASK;
utils.setTreePref('undoCloseTabSet.behavior', behavior);
}
return behavior;
});
},
get undoCloseTabSetBehavior()
{
return utils.getTreePref('undoCloseTabSet.behavior');
},
kUNDO_ASK : 1,
kUNDO_CLOSE_SET : 2,
kUNDO_CLOSE_FULL_SET : 256,
doAndWaitDOMEvent : function utils_doAndWaitDOMEvent()
{
var type, target, delay, task;
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = arguments.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let arg = arguments[i];
switch(typeof arg)
{
case 'string':
type = arg;
continue;
case 'number':
delay = arg;
continue;
case 'function':
task = arg;
continue;
default:
target = arg;
continue;
}
}
if (!target || !type) {
if (task) task();
return;
}
var done = false;
var listener = function(aEvent) {
2011-05-25 23:11:08 -04:00
jstimer.setTimeout(function() {
done = true;
}, delay || 0);
target.removeEventListener(type, listener, false);
};
if (task)
2011-03-18 16:00:12 -04:00
Deferred.next(function() {
try {
task();
}
catch(e) {
dump(e+'\n');
target.removeEventListener(type, listener, false);
done = true;
}
2012-02-06 12:13:46 -05:00
}).error(this.defaultDeferredErrorHandler);
target.addEventListener(type, listener, false);
var thread = Components
.classes['@mozilla.org/thread-manager;1']
.getService()
.mainThread;
while (!done)
{
//dump('WAIT '+type+' '+Date.now()+'\n');
thread.processNextEvent(true);
}
},
findOffsetParent : function utils_findOffsetParent(aNode)
{
var parent = aNode.parentNode;
var doc = aNode.ownerDocument || aNode;
var view = doc.defaultView;
while (parent && parent instanceof Ci.nsIDOMElement)
{
let position = view.getComputedStyle(parent, null).getPropertyValue('position');
if (position != 'static')
return parent;
parent = parent.parentNode;
}
return doc.documentElement;
},
assertBeforeDestruction : function utils_assertBeforeDestruction(aNotDestructed)
2012-01-23 14:07:56 -05:00
{
if (aNotDestructed)
return;
var message = 'ERROR: accessed after destruction!';
var error = new Error(message);
dump(message+'\n'+error.stack+'\n');
throw error;
},
defaultDeferredErrorHandler : function utils_defaultDeferredErrorHandler(aError)
2012-02-06 12:13:46 -05:00
{
if (aError.stack)
Components.utils.reportError(aError.message+'\n'+aError.stack);
else
Components.utils.reportError(aError);
},
// event
isNewTabAction : function utils_isNewTabAction(aEvent)
{
return aEvent.button == 1 || (aEvent.button == 0 && this.isAccelKeyPressed(aEvent));
},
isAccelKeyPressed : function utils_isAccelKeyPressed(aEvent)
{
if ( // this is releasing of the accel key!
(aEvent.type == 'keyup') &&
(aEvent.keyCode == (this.isMac ? Ci.nsIDOMKeyEvent.DOM_VK_META : Ci.nsIDOMKeyEvent.DOM_VK_CONTROL ))
) {
return false;
}
return this.isMac ?
(aEvent.metaKey || (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_META)) :
(aEvent.ctrlKey || (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL)) ;
},
isCopyAction : function utils_isCopyAction(aEvent)
{
return this.isAccelKeyPressed(aEvent) ||
(aEvent.dataTransfer && aEvent.dataTransfer.dropEffect == 'copy');
},
isEventFiredOnClosebox : function utils_isEventFiredOnClosebox(aEvent)
{
return this.evaluateXPath(
'ancestor-or-self::*[contains(concat(" ", normalize-space(@class), " "), " tab-close-button ")]',
aEvent.originalTarget || aEvent.target,
Ci.nsIDOMXPathResult.BOOLEAN_TYPE
).booleanValue;
},
isEventFiredOnClickable : function utils_isEventFiredOnClickable(aEvent)
{
return this.evaluateXPath(
'ancestor-or-self::*[contains(" button toolbarbutton scrollbar nativescrollbar popup menupopup panel tooltip splitter textbox ", concat(" ", local-name(), " "))]',
aEvent.originalTarget,
Ci.nsIDOMXPathResult.BOOLEAN_TYPE
).booleanValue;
},
isEventFiredOnScrollbar : function utils_isEventFiredOnScrollbar(aEvent)
{
return this.evaluateXPath(
'ancestor-or-self::*[local-name()="scrollbar" or local-name()="nativescrollbar"]',
aEvent.originalTarget,
Ci.nsIDOMXPathResult.BOOLEAN_TYPE
).booleanValue;
},
isEventFiredOnTwisty : function utils_isEventFiredOnTwisty(aEvent)
{
var tab = this.getTabFromEvent(aEvent);
if (!tab ||
!this.hasChildTabs(tab) ||
!this.canCollapseSubtree(tab))
return false;
var twisty = tab.ownerDocument.getAnonymousElementByAttribute(tab, 'class', this.kTWISTY);
if (!twisty)
return false;
var box = twisty.boxObject;
var left = box.screenX;
var top = box.screenY;
var right = left + box.width;
var bottom = top + box.height;
var favicon = this.getFaviconRect(tab);
if (!box.width || !box.height) {
left = favicon.left;
top = favicon.top;
right = favicon.right;
bottom = favicon.bottom;
}
else if (
this.shouldExpandTwistyArea &&
!this._expandTwistyAreaBlockers.length
) {
left = Math.min(left, favicon.left);
top = Math.min(top, favicon.top);
right = Math.max(right, favicon.right);
bottom = Math.max(bottom, favicon.bottom);
}
var x = aEvent.screenX;
var y = aEvent.screenY;
return (x >= left && x <= right && y >= top && y <= bottom);
},
getFaviconRect : function utils_getFaviconRect(aTab)
{
var icon = aTab.ownerDocument.getAnonymousElementByAttribute(aTab, 'class', 'tab-icon-image');
var iconBox = icon.boxObject;
var iconRect = {
left : iconBox.screenX,
top : iconBox.screenY,
right : iconBox.screenX + iconBox.width,
bottom : iconBox.screenY + iconBox.height
};
var throbber = aTab.ownerDocument.getAnonymousElementByAttribute(aTab, 'class', 'tab-throbber');
var throbberBox = throbber.boxObject;
var throbberRect = {
left : throbberBox.screenX,
top : throbberBox.screenY,
right : throbberBox.screenX + throbberBox.width,
bottom : throbberBox.screenY + throbberBox.height
};
if (!iconBox.width && !iconBox.height)
return throbberRect;
if (!throbberBox.width && !throbberBox.height)
return iconRect;
return {
left : Math.min(throbberRect.left, iconRect.left),
right : Math.max(throbberRect.right, iconRect.right),
top : Math.min(throbberRect.top, iconRect.top),
bottom : Math.max(throbberRect.bottom, iconRect.bottom)
};
},
// called with target(nsIDOMEventTarget), document(nsIDOMDocument), type(string) and data(object)
fireDataContainerEvent : function utils_fireDataContainerEvent()
{
var target, document, type, data, canBubble, cancellable;
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = arguments.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let arg = arguments[i];
if (typeof arg == 'boolean') {
if (canBubble === void(0))
canBubble = arg;
else
cancellable = arg;
}
else if (typeof arg == 'string')
type = arg;
else if (arg instanceof Ci.nsIDOMDocument)
document = arg;
else if (arg instanceof Ci.nsIDOMEventTarget)
target = arg;
else
data = arg;
}
if (!target)
target = document;
if (!document)
document = target.ownerDocument || target;
var event = document.createEvent('DataContainerEvent');
event.initEvent(type, canBubble, cancellable);
2012-02-04 16:31:03 -05:00
var properties = Object.keys(data);
for (let i = 0, maxi = properties.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let property = properties[i];
let value = data[property];
event.setData(property, value);
event[property] = value; // for backward compatibility
}
return target.dispatchEvent(event);
},
registerExpandTwistyAreaBlocker : function utils_registerExpandTwistyAreaBlocker(aBlocker) /* PUBLIC API */
{
if (this._expandTwistyAreaBlockers.indexOf(aBlocker) < 0)
this._expandTwistyAreaBlockers.push(aBlocker);
},
_expandTwistyAreaBlockers : [],
registerExpandTwistyAreaAllowance : function utils_registerExpandTwistyAreaAllowance(aAllowance) /* PUBLIC API, obsolete, for backward compatibility */
{
this.registerExpandTwistyAreaBlocker(aAllowance.toSource());
},
// string
makeNewId : function utils_makeNewId()
{
return 'tab-<'+Date.now()+'-'+parseInt(Math.random() * 65000)+'>';
},
makeNewClosedSetId : function utils_makeNewId()
{
return 'tabs-closed-set-<'+Date.now()+'-'+parseInt(Math.random() * 65000)+'>';
},
makeURIFromSpec : function utils_makeURIFromSpec(aURI)
{
var newURI;
aURI = aURI || '';
if (aURI && String(aURI).indexOf('file:') == 0) {
2012-09-17 11:44:51 -04:00
var fileHandler = Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler);
var tempLocalFile = fileHandler.getFileFromURLSpec(aURI);
2012-09-17 11:44:51 -04:00
newURI = Services.io.newFileURI(tempLocalFile);
}
else {
if (!/^\w+\:/.test(aURI)) aURI = 'http://'+aURI;
2012-09-17 11:44:51 -04:00
newURI = Services.io.newURI(aURI, null, null);
}
return newURI;
},
getGroupTabURI : function utils_getGroupTabURI(aTitle)
{
return 'about:treestyletab-group'+(aTitle === void(0) ? '' : '?'+encodeURIComponent(aTitle) );
},
// xpath
NSResolver : {
lookupNamespaceURI : function(aPrefix)
{
switch (aPrefix)
{
case 'xul':
return 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
case 'html':
case 'xhtml':
return 'http://www.w3.org/1999/xhtml';
case 'xlink':
return 'http://www.w3.org/1999/xlink';
default:
return '';
}
}
},
evaluateXPath : function utils_evaluateXPath(aExpression, aContext, aType)
{
if (!aType) aType = Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
try {
var XPathResult = (aContext.ownerDocument || aContext).evaluate(
aExpression,
(aContext || document),
this.NSResolver,
aType,
null
);
}
catch(e) {
return {
singleNodeValue : null,
snapshotLength : 0,
snapshotItem : function() {
return null
}
};
}
return XPathResult;
},
getArrayFromXPathResult : function utils_getArrayFromXPathResult(aXPathResult)
{
var max = aXPathResult.snapshotLength;
var array = new Array(max);
if (!max) return array;
for (var i = 0; i < max; i++)
{
array[i] = aXPathResult.snapshotItem(i);
}
return array;
},
/* Session Store API */
getTabValue : function utils_getTabValue(aTab, aKey)
{
var value = '';
try {
value = this.SessionStore.getTabValue(aTab, aKey);
}
catch(e) {
}
if (this.useTMPSessionAPI) {
let TMPValue = aTab.getAttribute(this.kTMP_SESSION_DATA_PREFIX+aKey);
if (TMPValue) value = TMPValue;
}
return value;
},
setTabValue : function utils_setTabValue(aTab, aKey, aValue)
{
if (!aValue) return this.deleteTabValue(aTab, aKey);
aTab.setAttribute(aKey, aValue);
try {
this.checkCachedSessionDataExpiration(aTab);
this.SessionStore.setTabValue(aTab, aKey, aValue);
}
catch(e) {
}
if (this.useTMPSessionAPI)
aTab.setAttribute(this.kTMP_SESSION_DATA_PREFIX+aKey, aValue);
return aValue;
},
deleteTabValue : function utils_deleteTabValue(aTab, aKey)
{
aTab.removeAttribute(aKey);
try {
this.checkCachedSessionDataExpiration(aTab);
this.SessionStore.setTabValue(aTab, aKey, '');
this.SessionStore.deleteTabValue(aTab, aKey);
}
catch(e) {
}
if (this.useTMPSessionAPI)
aTab.removeAttribute(this.kTMP_SESSION_DATA_PREFIX+aKey);
},
// workaround for http://piro.sakura.ne.jp/latest/blosxom/mozilla/extension/treestyletab/2009-09-29_debug.htm
checkCachedSessionDataExpiration : function utils_checkCachedSessionDataExpiration(aTab)
{
2012-01-13 03:38:12 -05:00
var data = aTab.linkedBrowser.__SS_data;
if (data &&
data._tabStillLoading &&
aTab.getAttribute('busy') != 'true' &&
aTab.linkedBrowser.__SS_restoreState != 1)
data._tabStillLoading = false;
},
markAsClosedSet : function utils_markAsClosedSet(aTabs) /* PUBLIC API */
{
if (!aTabs || aTabs.length <= 1) return;
var id = this.makeNewClosedSetId() + '::' + aTabs.length;
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
this.setTabValue(aTabs[i], this.kCLOSED_SET_ID, id);
}
},
unmarkAsClosedSet : function utils_unmarkAsClosedSet(aTabs) /* PUBLIC API */
{
if (!aTabs || !aTabs.length) return;
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
this.deleteTabValue(aTabs[i], this.kCLOSED_SET_ID);
}
},
useTMPSessionAPI : false,
kTMP_SESSION_DATA_PREFIX : 'tmp-session-data-',
// tab
getTabStrip : function utils_getTabStrip(aTabBrowser)
{
if (!(aTabBrowser instanceof Ci.nsIDOMElement))
return null;
var strip = aTabBrowser.mStrip;
return (strip && strip instanceof Ci.nsIDOMElement) ?
strip :
this.evaluateXPath(
'ancestor::xul:toolbar[1]',
2012-09-23 08:04:20 -04:00
aTabBrowser.tabContainer,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue || aTabBrowser.tabContainer.parentNode;
},
get tabStrip()
{
return this.getTabStrip(this.browser);
},
getTabContainerBox : function utils_getTabContainerBox(aTabBrowser)
{
if (!(aTabBrowser instanceof Ci.nsIDOMElement))
return null;
var strip = this.getTabStrip(aTabBrowser);
return strip.treeStyleTabToolbarInnerBox || aTabBrowser.tabContainer;
},
get tabContainerBox()
{
return this.getTabContainerBox(this.browser);
},
setTabbrowserAttribute : function utils_setTabbrowserAttribute(aName, aValue, aTabBrowser)
{
aTabBrowser = aTabBrowser || this.mTabBrowser || this.browser;
if (aValue) {
aTabBrowser.setAttribute(aName, aValue);
aTabBrowser.mTabContainer.setAttribute(aName, aValue);
aTabBrowser.treeStyleTab.setTabStripAttribute(aName, aValue);
}
else {
aTabBrowser.removeAttribute(aName);
aTabBrowser.mTabContainer.removeAttribute(aName);
aTabBrowser.treeStyleTab.removeTabStripAttribute(aName);
}
},
removeTabbrowserAttribute : function utils_removeTabbrowserAttribute(aName, aTabBrowser)
{
this.setTabbrowserAttribute(aName, null, aTabBrowser);
},
setTabStripAttribute : function utils_setTabStripAttribute(aAttr, aValue)
{
var strip = this.tabStrip;
if (!strip) return;
var isFeatureAttribute = aAttr.indexOf('treestyletab-') == 0;
if (aValue) {
if (this._tabStripPlaceHolder)
this._tabStripPlaceHolder.setAttribute(aAttr, aValue);
if (!this._tabStripPlaceHolder || aAttr != 'ordinal')
strip.setAttribute(aAttr, aValue);
if (strip.treeStyleTabToolbarInnerBox)
strip.treeStyleTabToolbarInnerBox.setAttribute(aAttr, aValue);
if (isFeatureAttribute) {
// Only attributes for TST's feature are applied to the root element.
// (width, height, and other general attributes have to be ignored!)
strip.ownerDocument.defaultView.setTimeout(function(aSelf) {
strip.ownerDocument.documentElement.setAttribute(aAttr, aValue);
}, 10, this);
}
}
else {
if (this._tabStripPlaceHolder)
this._tabStripPlaceHolder.removeAttribute(aAttr);
if (!this._tabStripPlaceHolder || aAttr != 'ordinal')
strip.removeAttribute(aAttr);
if (strip.treeStyleTabToolbarInnerBox)
strip.treeStyleTabToolbarInnerBox.removeAttribute(aAttr);
if (isFeatureAttribute) {
strip.ownerDocument.defaultView.setTimeout(function(aSelf) {
strip.ownerDocument.documentElement.removeAttribute(aAttr);
}, 10, this);
}
}
},
removeTabStripAttribute : function utils_removeTabStripAttribute(aAttr)
{
this.setTabStripAttribute(aAttr, null);
},
getTabFromChild : function utils_getTabFromChild(aTab)
{
return this.evaluateXPath(
'ancestor-or-self::xul:tab',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getTabFromEvent : function utils_getTabFromEvent(aEvent)
{
return this.getTabFromChild(aEvent.originalTarget || aEvent.target);
},
getNewTabButtonFromEvent : function utils_getNewTabButtonFromEvent(aEvent)
{
return this.evaluateXPath(
'ancestor-or-self::*['
+'@id="new-tab-button" or '
+'contains(concat(" ", normalize-space(@class), " "), " tabs-newtab-button ")'
+'][1]',
aEvent.originalTarget,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getSplitterFromEvent : function utils_getSplitterFromEvent(aEvent)
{
return this.evaluateXPath(
'ancestor-or-self::xul:splitter[contains(concat(" ", normalize-space(@class), " "), " '+this.kSPLITTER+' ")]',
aEvent.originalTarget,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
isEventFiredOnGrippy : function utils_isEventFiredOnGrippy(aEvent)
{
return this.evaluateXPath(
'ancestor-or-self::xul:grippy',
aEvent.originalTarget,
Ci.nsIDOMXPathResult.BOOLEAN_TYPE
).booleanValue;
},
getTabFromFrame : function utils_getTabFromFrame(aFrame, aTabBrowser)
{
var b = aTabBrowser || this.browser;
var top = aFrame.top;
2012-09-23 02:43:49 -04:00
var tabs = this.getAllTabs(b);
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = tabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let tab = tabs[i];
if (tab.linkedBrowser.contentWindow == top)
return tab;
}
return null;
},
getTabbarFromChild : function utils_getTabbarFromChild(aNode)
{
return this.evaluateXPath(
2012-10-12 13:24:02 -04:00
'ancestor-or-self::*[contains(concat(" ", normalize-space(@class), " "), " tabbrowser-strip ")] | ' +
'ancestor-or-self::xul:tabs[@tabbrowser] | ' +
'ancestor-or-self::xul:toolbar/child::xul:tabs[@tabbrowser]',
aNode,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getAncestorTabbarFromChild : function utils_getAncestorTabbarFromChild(aNode)
{
return this.evaluateXPath(
2012-10-12 13:24:02 -04:00
'ancestor-or-self::*[contains(concat(" ", normalize-space(@class), " "), " tabbrowser-strip ")] | ' +
'ancestor-or-self::xul:tabs[@tabbrowser]',
aNode,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getTabbarFromEvent : function utils_getTabbarFromEvent(aEvent)
{
return this.getTabbarFromChild(aEvent.originalTarget || aEvent.target);
},
getAncestorTabbarFromEvent : function utils_getAncestorTabbarFromEvent(aEvent)
{
return this.getAncestorTabbarFromChild(aEvent.originalTarget || aEvent.target);
},
cleanUpTabsArray : function utils_cleanUpTabsArray(aTabs)
{
var newTabs = [];
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let tab = aTabs[i];
if (!tab || !tab.parentNode) continue; // ignore removed tabs
if (newTabs.indexOf(tab) < 0) newTabs.push(tab);
}
newTabs.sort(this.sortTabsByOrder);
return newTabs;
},
sortTabsByOrder : function utils_sortTabsByOrder(aA, aB)
{
return aA._tPos - aB._tPos;
},
gatherSubtreeMemberTabs : function utils_gatherSubtreeMemberTabs(aTabOrTabs, aOnlyChildren)
{
var tabs = aTabOrTabs;
if (!(tabs instanceof Array)) {
tabs = [aTabOrTabs];
}
var b = this.getTabBrowserFromChild(tabs[0]);
var descendant = [];
for (var i = 0, maxi = tabs.length; i < maxi; i++)
{
descendant = descendant.concat(b.treeStyleTab.getDescendantTabs(tabs[i]));
}
return this.cleanUpTabsArray(aOnlyChildren ? descendant : tabs.concat(descendant));
},
splitTabsToSubtrees : function utils_splitTabsToSubtrees(aTabs) /* PUBLIC API */
{
var groups = [];
2011-05-25 23:11:08 -04:00
var group = [];
2012-02-04 16:31:03 -05:00
aTabs = this.cleanUpTabsArray(aTabs);
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let tab = aTabs[i];
let parent = this.getParentTab(tab);
if (!parent || group.indexOf(parent) < 0) {
if (group.length) groups.push(group);
group = [tab];
}
else {
group.push(tab);
}
}
if (group.length) groups.push(group);
return groups;
},
// tabbrowser
getTabBrowserFromChild : function utils_getTabBrowserFromChild(aTabBrowserChild)
{
if (!aTabBrowserChild)
return null;
if (aTabBrowserChild.__treestyletab__linkedTabBrowser) // tab
return aTabBrowserChild.__treestyletab__linkedTabBrowser;
if (aTabBrowserChild.localName == 'tabbrowser') // itself
return aTabBrowserChild;
if (aTabBrowserChild.tabbrowser) // tabs
return aTabBrowserChild.tabbrowser;
if (aTabBrowserChild.localName == 'toolbar') // tabs toolbar
return aTabBrowserChild.getElementsByTagName('tabs')[0].tabbrowser;
// tab context menu
var popup = this.evaluateXPath(
'ancestor-or-self::xul:menupopup[@id="tabContextMenu"]',
aTabBrowserChild,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
if (popup && 'TabContextMenu' in aTabBrowserChild.ownerDocument.defaultView)
return this.getTabBrowserFromChild(aTabBrowserChild.ownerDocument.defaultView.TabContextMenu.contextTab);
var b = this.evaluateXPath(
'ancestor::xul:tabbrowser | '+
'ancestor::xul:tabs[@tabbrowser] |'+
'ancestor::xul:toolbar/descendant::xul:tabs',
aTabBrowserChild,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
return (b && b.tabbrowser) || b;
},
getTabBrowserFromFrame : function utils_getTabBrowserFromFrame(aFrame)
{
var w = this.browserWindow;
return !w ? null :
('SplitBrowser' in w) ? this.getTabBrowserFromChild(w.SplitBrowser.getSubBrowserAndBrowserFromFrame(aFrame.top).browser) :
this.browser ;
},
getFrameFromTabBrowserElements : function utils_getFrameFromTabBrowserElements(aFrameOrTabBrowser)
{
var frame = aFrameOrTabBrowser;
if (frame == '[object XULElement]') {
if (frame.localName == 'tab') {
frame = frame.linkedBrowser.contentWindow;
}
else if (frame.localName == 'browser') {
frame = frame.contentWindow;
}
else {
frame = this.getTabBrowserFromChild(frame);
if (!frame) return null;
frame = frame.contentWindow;
}
}
if (!frame)
frame = this.browser.contentWindow;
return frame;
},
/* get tab(s) */
getTabById : function utils_getTabById(aId, aTabBrowserChildren)
{
if (!aId) return null;
if (aTabBrowserChildren && !(aTabBrowserChildren instanceof Ci.nsIDOMNode))
aTabBrowserChildren = null;
var b = this.getTabBrowserFromChild(aTabBrowserChildren) || this.browser;
if (this.tabsHash) // XPath-less implementation
return this.tabsHash[aId] || null;
2012-09-23 02:43:49 -04:00
return b.mTabContainer.querySelector('tab['+this.kID+'="'+aId+'"]');
},
isTabDuplicated : function utils_isTabDuplicated(aTab)
{
if (!aTab) return false;
var id = this.getTabValue(aTab, this.kID);
var b = this.getTabBrowserFromChild(aTab) || this.browser;
2012-09-23 02:51:11 -04:00
var tabs = b.mTabContainer.querySelectorAll('tab['+this.kID+'="'+id+'"], tab['+this.kID_RESTORING+'="'+id+'"]');
return tabs.length > 1;
},
/**
2012-09-23 02:43:49 -04:00
* Returns all tabs in the current group as an array.
* It includes tabs hidden by Tab Panorama.
*/
getAllTabs : function utils_getTabs(aTabBrowserChild)
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
2012-09-23 02:43:49 -04:00
return Array.slice(b.mTabContainer.querySelectorAll('tab'));
},
/**
2012-09-23 02:43:49 -04:00
* Returns all tabs in the current group as an array.
* It excludes tabs hidden by Tab Panorama.
*/
getTabs : function utils_getTabs(aTabBrowserChild)
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
2012-09-23 02:43:49 -04:00
return Array.slice(b.mTabContainer.querySelectorAll('tab:not([hidden="true"])'));
},
getAllTabsArray : function utils_getAllTabsArray(aTabBrowserChild) /* for backward compatibility */
{
2012-09-23 02:43:49 -04:00
return this.getAllTabs(aTabBrowserChild);
},
getTabsArray : function utils_getTabsArray(aTabBrowserChild) /* for backward compatibility */
{
2012-09-23 02:43:49 -04:00
return this.getTabs(aTabBrowserChild);
},
/**
* Returns the first tab in the current group.
*/
getFirstTab : function utils_getFirstTab(aTabBrowserChild)
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
var tabs = b.visibleTabs;
return tabs ? tabs[0] : b.mTabContainer.firstChild;
},
/**
* Returns the first visible, not collapsed, and not pinned tab.
*/
getFirstNormalTab : function utils_getFirstNormalTab(aTabBrowserChild)
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
2012-09-23 02:51:11 -04:00
return b.mTabContainer.querySelector('tab:not([pinned="true"]):not([hidden="true"])');
},
/**
* Returns the last tab in the current group.
*/
getLastTab : function utils_getLastTab(aTabBrowserChild)
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
var tabs = b.visibleTabs;
return tabs ? tabs[tabs.length-1] : b.mTabContainer.lastChild ;
},
/**
* Returns the next tab in the current group.
*/
getNextTab : function utils_getNextTab(aTab)
{
if (!aTab) return null;
var b = this.getTabBrowserFromChild(aTab);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
var tabs = b.visibleTabs;
if (tabs) {
let index = tabs.indexOf(aTab);
if (index > -1)
return tabs.length > index ? tabs[index+1] : null
}
var tab = aTab.nextSibling;
return (tab && tab.localName == 'tab') ? tab : null ;
},
/**
* Returns the previous tab in the current group.
*/
getPreviousTab : function utils_getPreviousTab(aTab)
{
if (!aTab) return null;
var b = this.getTabBrowserFromChild(aTab);
2012-01-23 14:07:56 -05:00
this.assertBeforeDestruction(b && b.mTabContainer);
var tabs = b.visibleTabs;
if (tabs) {
let index = tabs.indexOf(aTab);
if (index > -1)
return 0 < index ? tabs[index-1] : null
}
var tab = aTab.previousSibling;
return (tab && tab.localName == 'tab') ? tab : null ;
},
/**
* Returns the index of the specified tab, in the current group.
*/
getTabIndex : function utils_getTabIndex(aTab)
{
if (!aTab) return -1;
var b = this.getTabBrowserFromChild(aTab);
2012-09-23 02:43:49 -04:00
return this.getTabs(b).indexOf(aTab);
},
/**
* Returns the next not collapsed tab in the current group.
*/
getNextVisibleTab : function utils_getNextVisibleTab(aTab)
{
if (!aTab) return null;
var b = this.getTabBrowserFromChild(aTab);
if (!this.canCollapseSubtree(b))
return this.getNextTab(aTab);
2012-09-23 02:43:49 -04:00
var tabs = this.getVisibleTabs(b);
if (tabs.indexOf(aTab) < 0) tabs.push(aTab);
tabs.sort(this.sortTabsByOrder);
var index = tabs.indexOf(aTab);
return (index < tabs.length-1) ? tabs[index+1] : null ;
},
/**
* Returns the previous not collapsed tab in the current group.
*/
getPreviousVisibleTab : function utils_getPreviousVisibleTab(aTab)
{
if (!aTab) return null;
var b = this.getTabBrowserFromChild(aTab);
if (!this.canCollapseSubtree(b))
return this.getPreviousTab(aTab);
2012-09-23 02:43:49 -04:00
var tabs = this.getVisibleTabs(b);
if (tabs.indexOf(aTab) < 0) tabs.push(aTab);
tabs.sort(this.sortTabsByOrder);
var index = tabs.indexOf(aTab);
return (index > 0) ? tabs[index-1] : null ;
},
/**
* Returns the last not collapsed tab in the current group.
*/
getLastVisibleTab : function utils_getLastVisibleTab(aTabBrowserChild)
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
if (!b) return null;
2012-09-23 02:43:49 -04:00
var tabs = this.getVisibleTabs(b);
return tabs.length ? tabs[tabs.length-1] : null ;
},
/**
* Returns a XPathResult of not collapsed tabs in the current group.
*/
getVisibleTabs : function utils_getVisibleTabs(aTabBrowserChild) /* OBSOLETE */
{
var b = this.getTabBrowserFromChild(aTabBrowserChild || this.browser);
if (!this.canCollapseSubtree(b))
return this.getTabs(b);
2012-09-23 02:43:49 -04:00
return Array.slice(b.mTabContainer.querySelectorAll('tab:not(['+this.kCOLLAPSED+'="true"]):not([hidden="true"])'));
},
getVisibleTabsArray : function utils_getVisibleTabsArray(aTabBrowserChild) /* for backward compatibility */
{
2012-09-23 02:43:49 -04:00
return this.getVisibleTabs(aTabBrowserChild);
},
/**
* Returns the index of the specified tab, in the array of not collapsed
* tabs in the current group.
*/
getVisibleIndex : function utils_getVisibleIndex(aTab)
{
if (!aTab) return -1;
var b = this.getTabBrowserFromChild(aTab);
2012-09-23 02:43:49 -04:00
return this.getVisibleTabs(b).indexOf(aTab);
},
/**
* Returns tabs which are newly opened in the given operation.
*/
getNewTabsWithOperation : function utils_getNewTabsWithOperation(aOperation, aTabBrowser)
{
var previousTabs = this.getTabsInfo(aTabBrowser);
aOperation.call(this);
return this.getNewTabsFromPreviousTabsInfo(aTabBrowser, previousTabs);
},
/**
* Returns tabs which are newly opened. This requires the "previous state".
*/
getNewTabsFromPreviousTabsInfo : function utils_getNewTabsFromPreviousTabsInfo(aTabBrowser, aTabsInfo)
{
2012-09-23 02:43:49 -04:00
var tabs = this.getTabs(aTabBrowser);
var currentTabsInfo = this.getTabsInfo(aTabBrowser);
return tabs.filter(function(aTab, aIndex) {
return aTabsInfo.indexOf(currentTabsInfo[aIndex]) < 0;
});
},
getTabsInfo : function utils_getTabsInfo(aTabBrowser)
{
2012-09-23 02:43:49 -04:00
var tabs = this.getTabs(aTabBrowser);
return tabs.map(function(aTab) {
return aTab.getAttribute(this.kID)+'\n'+
aTab.getAttribute('busy')+'\n'+
aTab.linkedBrowser.currentURI.spec;
}, this);
},
/* notify "ready to open child tab(s)" */
readyToOpenChildTab : function utils_readyToOpenChildTab(aFrameOrTabBrowser, aMultiple, aInsertBefore) /* PUBLIC API */
{
if (!utils.getTreePref('autoAttach')) return false;
var frame = this.getFrameFromTabBrowserElements(aFrameOrTabBrowser);
if (!frame)
return false;
var ownerBrowser = this.getTabBrowserFromFrame(frame);
var parentTab = this.getTabFromFrame(frame, ownerBrowser);
if (!parentTab || parentTab.getAttribute('pinned') == 'true')
return false;
ownerBrowser.treeStyleTab.ensureTabInitialized(parentTab);
var parentId = parentTab.getAttribute(this.kID);
var refId = null;
if (aInsertBefore) {
ownerBrowser.treeStyleTab.ensureTabInitialized(parentTab);
refId = aInsertBefore.getAttribute(this.kID);
}
ownerBrowser.treeStyleTab.readiedToAttachNewTab = true;
ownerBrowser.treeStyleTab.readiedToAttachMultiple = aMultiple || false ;
ownerBrowser.treeStyleTab.multipleCount = aMultiple ? 0 : -1 ;
ownerBrowser.treeStyleTab.parentTab = parentId;
ownerBrowser.treeStyleTab.insertBefore = refId;
return true;
},
/**
* Extended version. If you don't know whether a new tab will be actually
* opened or not (by the command called after TST's API), then use this.
* This version automatically cancels the "ready" state with delay.
*/
readyToOpenChildTabNow : function utils_readyToOpenChildTabNow(aFrameOrTabBrowser, aMultiple) /* PUBLIC API */
{
if (this.readyToOpenChildTab.apply(this, arguments)) {
let self = this;
this.Deferred.next(function() {
self.stopToOpenChildTab(aFrameOrTabBrowser);
2012-02-06 12:13:46 -05:00
}).error(this.defaultDeferredErrorHandler);
return true;
}
return false;
},
readyToOpenNextSiblingTab : function utils_readyToOpenNextSiblingTab(aFrameOrTabBrowser) /* PUBLIC API */
{
var frame = this.getFrameFromTabBrowserElements(aFrameOrTabBrowser);
if (!frame)
return false;
var ownerBrowser = this.getTabBrowserFromFrame(frame);
var tab = this.getTabFromFrame(frame, ownerBrowser);
if (!tab || tab.getAttribute('pinned') == 'true')
return false;
var parentTab = this.getParentTab(tab);
var nextTab = this.getNextSiblingTab(tab);
if (parentTab) {
/**
* If the base tab has a parent, open the new tab as a child of
* the parent tab.
*/
return this.readyToOpenChildTab(parentTab, false, nextTab);
}
else {
/**
* Otherwise, open the tab as a new root tab. If there is no
* tab next to the base tab (in other words, if the tab is the
* last tab), then do nothing.
*/
if (!nextTab) return;
ownerBrowser.treeStyleTab.readiedToAttachNewTab = true;
ownerBrowser.treeStyleTab.parentTab = null;
ownerBrowser.treeStyleTab.insertBefore = nextTab.getAttribute(this.kID);
return true;
}
},
/**
* Extended version. If you don't know whether a new tab will be actually
* opened or not (by the command called after TST's API), then use this.
* This version automatically cancels the "ready" state with delay.
*/
readyToOpenNextSiblingTabNow : function utils_readyToOpenNextSiblingTabNow(aFrameOrTabBrowser) /* PUBLIC API */
{
if (this.readyToOpenNextSiblingTab.apply(this, arguments)) {
let self = this;
this.Deferred.next(function() {
self.stopToOpenChildTab(aFrameOrTabBrowser);
2012-02-06 12:13:46 -05:00
}).error(this.defaultDeferredErrorHandler);
return true;
}
return false;
},
readyToOpenNewTabGroup : function utils_readyToOpenNewTabGroup(aFrameOrTabBrowser, aTreeStructure, aExpandAllTree) /* PUBLIC API */
{
if (!utils.getTreePref('autoAttach')) return false;
var frame = this.getFrameFromTabBrowserElements(aFrameOrTabBrowser);
if (!frame) return false;
this.stopToOpenChildTab(frame);
var ownerBrowser = this.getTabBrowserFromFrame(frame);
ownerBrowser.treeStyleTab.readiedToAttachNewTabGroup = true;
ownerBrowser.treeStyleTab.readiedToAttachMultiple = true;
ownerBrowser.treeStyleTab.multipleCount = 0;
ownerBrowser.treeStyleTab.treeStructure = aTreeStructure;
2011-01-22 13:32:04 -05:00
ownerBrowser.treeStyleTab.shouldExpandAllTree = !!aExpandAllTree;
return true;
},
/**
* Extended version. If you don't know whether new tabs will be actually
* opened or not (by the command called after TST's API), then use this.
* This version automatically cancels the "ready" state with delay.
*/
readyToOpenNewTabGroupNow : function utils_readyToOpenNewTabGroupNow(aFrameOrTabBrowser) /* PUBLIC API */
{
if (this.readyToOpenNewTabGroup.apply(this, arguments)) {
let self = this;
this.Deferred.next(function() {
self.stopToOpenChildTab(aFrameOrTabBrowser);
2012-02-06 12:13:46 -05:00
}).error(this.defaultDeferredErrorHandler);
return true;
}
return false;
},
stopToOpenChildTab : function utils_stopToOpenChildTab(aFrameOrTabBrowser) /* PUBLIC API */
{
var frame = this.getFrameFromTabBrowserElements(aFrameOrTabBrowser);
if (!frame) return false;
var ownerBrowser = this.getTabBrowserFromFrame(frame);
ownerBrowser.treeStyleTab.readiedToAttachNewTab = false;
ownerBrowser.treeStyleTab.readiedToAttachNewTabGroup = false;
ownerBrowser.treeStyleTab.readiedToAttachMultiple = false;
ownerBrowser.treeStyleTab.multipleCount = -1;
ownerBrowser.treeStyleTab.parentTab = null;
ownerBrowser.treeStyleTab.insertBefore = null;
ownerBrowser.treeStyleTab.treeStructure = null;
ownerBrowser.treeStyleTab.shouldExpandAllTree = false;
return true;
},
checkToOpenChildTab : function utils_checkToOpenChildTab(aFrameOrTabBrowser) /* PUBLIC API */
{
var frame = this.getFrameFromTabBrowserElements(aFrameOrTabBrowser);
if (!frame) return false;
var ownerBrowser = this.getTabBrowserFromFrame(frame);
2011-01-22 13:32:04 -05:00
return !!(ownerBrowser.treeStyleTab.readiedToAttachNewTab || ownerBrowser.treeStyleTab.readiedToAttachNewTabGroup);
},
/* tree manipulations */
get treeViewEnabled() /* PUBLIC API */
{
return this._treeViewEnabled;
},
set treeViewEnabled(aValue)
{
2011-01-22 13:32:04 -05:00
this._treeViewEnabled = !!aValue;
Services.obs.notifyObservers(
window,
this.kTOPIC_CHANGE_TREEVIEW_AVAILABILITY,
this._treeViewEnabled
);
return aValue;
},
_treeViewEnabled : true,
get rootTabs() /* PUBLIC API */
{
return Array.slice(this.browser.mTabContainer.querySelectorAll('tab:not(['+this.kNEST+']), tab['+this.kNEST+'=""], tab['+this.kNEST+'="0"]'));
},
get allRootTabs() /* PUBLIC API */
{
return this.rootTabs;
},
get visibleRootTabs() /* PUBLIC API */
{
return this.rootTabs.filter(function(aTab) {
return !aTab.hidden;
});
},
canCollapseSubtree : function utils_canCollapseSubtree(aTabOrTabBrowser) /* PUBLIC API */
{
if (aTabOrTabBrowser &&
aTabOrTabBrowser.localName == 'tab' &&
aTabOrTabBrowser.getAttribute(this.kALLOW_COLLAPSE) != 'true')
return false;
var b = this.getTabBrowserFromChild(aTabOrTabBrowser) || this.browser;
2011-06-15 22:54:13 -04:00
return b && b.getAttribute(this.kALLOW_COLLAPSE) == 'true';
},
isCollapsed : function utils_isCollapsed(aTab) /* PUBLIC API */
{
if (!aTab ||
!this.canCollapseSubtree(this.getRootTab(aTab)))
return false;
return aTab.getAttribute(this.kCOLLAPSED) == 'true';
},
isSubtreeCollapsed : function utils_isSubtreeCollapsed(aTab) /* PUBLIC API */
{
if (!aTab || !this.canCollapseSubtree(aTab) || !this.hasChildTabs(aTab))
return false;
return aTab.getAttribute(this.kSUBTREE_COLLAPSED) == 'true';
},
shouldCloseTabSubtreeOf : function utils_shouldCloseTabSubtreeOf(aTab)
{
return (
this.hasChildTabs(aTab) &&
(
utils.getTreePref('closeParentBehavior') == this.kCLOSE_PARENT_BEHAVIOR_CLOSE_ALL_CHILDREN ||
this.isSubtreeCollapsed(aTab)
)
);
},
shouldCloseTabSubTreeOf : function() { return this.shouldCloseTabSubtreeOf.apply(this, arguments); }, // obsolete, for backward compatibility
shouldCloseLastTabSubtreeOf : function utils_shouldCloseLastTabSubtreeOf(aTab)
{
var b = this.getTabBrowserFromChild(aTab);
return (
2011-06-15 22:54:13 -04:00
b &&
this.shouldCloseTabSubtreeOf(aTab) &&
2012-09-23 02:43:49 -04:00
this.getDescendantTabs(aTab).length + 1 == this.getAllTabs(b).length
);
},
shouldCloseLastTabSubTreeOf : function() { return this.shouldCloseLastTabSubtreeOf.apply(this, arguments); }, // obsolete, for backward compatibility
getParentTab : function utils_getParentTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
2011-12-12 13:54:43 -05:00
var parent;
var id = aTab.getAttribute(this.kPARENT);
if (this.tabsHash) { // XPath-less implementation
parent = this.getTabById(id);
if (parent && !parent.parentNode && this.tabsHash) {
delete this.tabsHash[id];
parent = null;
}
}
2011-12-12 13:54:43 -05:00
else {
parent = this.evaluateXPath(
'preceding-sibling::xul:tab[@'+this.kID+'="'+id+'"][1]',
2011-12-12 13:54:43 -05:00
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
}
return (parent && parent != aTab) ? parent : null ;
},
getAncestorTabs : function utils_getAncestorTabs(aTab) /* PUBLIC API */
{
2011-12-12 13:54:43 -05:00
var tabs = [aTab];
var parentTab = aTab;
while (parentTab = this.getParentTab(parentTab))
{
2011-12-12 13:54:43 -05:00
if (tabs.indexOf(parentTab) > -1) {
let message = 'recursive tree detected!\n'+
tabs.concat([parentTab])
.reverse().map(function(aTab) {
return ' '+aTab._tPos+' : '+
aTab.label+'\n '+
aTab.getAttribute(this.kID);
}, this).join('\n');
dump(message+'\n');
break;
}
if (aTab._tPos < parentTab._tPos) {
let message = 'broken tree detected!\n'+
tabs.concat([parentTab])
.reverse().map(function(aTab) {
return ' '+aTab._tPos+' : '+
aTab.label+'\n '+
aTab.getAttribute(this.kID);
}, this).join('\n');
dump(message+'\n');
}
tabs.push(parentTab);
2011-12-12 13:54:43 -05:00
aTab = parentTab;
}
2011-12-12 13:54:43 -05:00
return tabs.slice(1);
},
getRootTab : function utils_getRootTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
if (this.tabsHash) { // XPath-less implementation
2011-12-12 13:54:43 -05:00
let ancestors = this.getAncestorTabs(aTab);
return ancestors.length ? ancestors[ancestors.length-1] : aTab ;
}
return this.evaluateXPath(
'(self::*[not(@'+this.kPARENT+')] | preceding-sibling::xul:tab[not(@'+this.kPARENT+')])[last()]',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getNextSiblingTab : function utils_getNextSiblingTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
if (this.tabsHash) { // XPath-less implementation
let parentTab = this.getParentTab(aTab);
if (!parentTab) {
let next = aTab;
do {
next = next.nextSibling;
}
while (next &&
next.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
this.getParentTab(next));
return next;
}
let children = parentTab.getAttribute(this.kCHILDREN);
if (children) {
let list = ('|'+children).split('|'+aTab.getAttribute(this.kID));
list = list.length > 1 ? list[1].split('|') : [] ;
for (let i = 0, maxi = list.length; i < maxi; i++)
{
let firstChild = this.getTabById(list[i], aTab);
if (firstChild) return firstChild;
}
}
return null;
}
var parent = aTab.getAttribute(this.kPARENT);
return this.evaluateXPath(
'following-sibling::xul:tab['+
(parent ? '@'+this.kPARENT+'="'+parent+'"' : 'not(@'+this.kPARENT+')' )+
'][1]',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getPreviousSiblingTab : function utils_getPreviousSiblingTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
if (this.tabsHash) { // XPath-less implementation
let parentTab = this.getParentTab(aTab);
if (!parentTab) {
let prev = aTab;
do {
prev = prev.previousSibling;
}
while (prev &&
prev.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
this.getParentTab(prev));
return prev;
}
let children = parentTab.getAttribute(this.kCHILDREN);
if (children) {
let list = ('|'+children).split('|'+aTab.getAttribute(this.kID))[0].split('|');
for (let i = list.length-1; i > -1; i--)
{
let lastChild = this.getTabById(list[i], aTab);
if (lastChild) return lastChild;
}
}
return null;
}
var parent = aTab.getAttribute(this.kPARENT);
return this.evaluateXPath(
'preceding-sibling::xul:tab['+
(parent ? '@'+this.kPARENT+'="'+parent+'"' : 'not(@'+this.kPARENT+')' )+
'][1]',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getSiblingTabs : function utils_getSiblingTabs(aTab) /* PUBLIC API */
{
var parent = this.getParentTab(aTab);
var siblings = parent && parent.parentNode ? this.getChildTabs(parent) : this.visibleRootTabs ;
return siblings.filter(function(aSiblingTab) {
return aSiblingTab != aTab;
});
},
getChildTabs : function utils_getChildTabs(aTab, aAllTabsArray) /* PUBLIC API */
{
var tabs = [];
if (!aTab) return tabs;
var children = aTab.getAttribute(this.kCHILDREN);
if (!children) return tabs;
if (aAllTabsArray) tabs = aAllTabsArray;
var list = children.split('|');
for (let i = 0, maxi = list.length; i < maxi; i++)
{
let tab = this.getTabById(list[i], aTab);
if (!tab || tab == aTab) continue;
if (tabs.indexOf(tab) > -1) {
let message = 'broken (possible recursive) tree detected!\n'+
tabs.map(function(aTab) {
return ' '+aTab._tPos+' : '+
aTab.label+'\n '+
aTab.getAttribute(this.kID);
}, this).join('\n');
dump(message+'\n');
continue;
}
tabs.push(tab);
if (aAllTabsArray)
this.getChildTabs(tab, tabs);
}
return tabs;
},
hasChildTabs : function utils_hasChildTabs(aTab) /* PUBLIC API */
{
if (!aTab) return false;
return aTab.hasAttribute(this.kCHILDREN);
},
getDescendantTabs : function utils_getDescendantTabs(aTab) /* PUBLIC API */
{
var tabs = [];
this.getChildTabs(aTab, tabs);
return tabs;
},
getFirstChildTab : function utils_getFirstChildTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
if (this.tabsHash) { // XPath-less implementation
let children = aTab.getAttribute(this.kCHILDREN);
let firstChild = null;
if (children) {
let list = children.split('|');
for (let i = 0, maxi = list.length; i < maxi; i++)
{
firstChild = this.getTabById(list[i], aTab);
if (firstChild && firstChild != aTab) break;
}
}
return firstChild;
}
return this.evaluateXPath(
'following-sibling::xul:tab[@'+this.kPARENT+'="'+aTab.getAttribute(this.kID)+'"][1]',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getLastChildTab : function utils_getLastChildTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
if (this.tabsHash) { // XPath-less implementation
let children = aTab.getAttribute(this.kCHILDREN);
let lastChild = null;
if (children) {
let list = children.split('|');
for (let i = list.length-1; i > -1; i--)
{
lastChild = this.getTabById(list[i], aTab);
if (lastChild && lastChild != aTab) break;
}
}
return lastChild;
}
return this.evaluateXPath(
'following-sibling::xul:tab[@'+this.kPARENT+'="'+aTab.getAttribute(this.kID)+'"][last()]',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
getLastDescendantTab : function utils_getLastDescendantTab(aTab) /* PUBLIC API */
{
if (!aTab) return null;
if (this.tabsHash) { // XPath-less implementation
let tabs = this.getDescendantTabs(aTab);
return tabs.length ? tabs[tabs.length-1] : null ;
}
var parent = aTab.getAttribute(this.kPARENT);
return this.evaluateXPath(
'following-sibling::xul:tab['+
(parent ? '@'+this.kPARENT+'="'+parent+'"' : 'not(@'+this.kPARENT+')' )+
'][1]/preceding-sibling::xul:tab[1][not(@'+this.kID+'="'+aTab.getAttribute(this.kID)+'")]',
aTab,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE
).singleNodeValue;
},
collectRootTabs : function utils_collectRootTabs(aTabs) /* PUBLIC API */
{
2011-06-15 04:38:14 -04:00
aTabs = Array.slice(aTabs);
return aTabs.filter(function(aTab) {
var parent = this.getParentTab(aTab);
return !parent || aTabs.indexOf(parent) < 0;
}, this);
},
getChildIndex : function utils_getChildIndex(aTab, aParent) /* PUBLIC API */
{
var parent = this.getParentTab(aTab);
if (!aParent || !parent || aParent != parent) {
let tabs = [aTab].concat(this.getAncestorTabs(aTab));
parent = aTab;
for (let i = 0, maxi = tabs.length; i < maxi && parent != aParent; i++)
{
aTab = parent;
parent = i < maxi ? tabs[i+1] : null ;
}
if (parent != aParent)
return -1;
aParent = parent;
}
if (aParent) {
let children = aParent.getAttribute(this.kCHILDREN);
let list = children.split('|');
let id = aTab.getAttribute(this.kID);
for (let i = 0, maxi = list.length; i < maxi; i++)
{
if (list[i] == id) return i;
}
return -1;
}
else {
let tabs = this.rootTabs;
for (let i = 0, maxi = tabs.length; i < maxi; i++)
{
if (tabs[i] == aTab) return i;
}
}
return -1;
},
getXOffsetOfTab : function utils_getXOffsetOfTab(aTab)
{
var extraCondition = this.canCollapseSubtree(aTab) ?
'[not(@'+this.kCOLLAPSED+'="true")]' :
'' ;
return this.evaluateXPath(
'sum((self::* | preceding-sibling::xul:tab[not(@hidden="true")]'+extraCondition+')'+
'/attribute::'+this.kX_OFFSET+')',
aTab,
Ci.nsIDOMXPathResult.NUMBER_TYPE
).numberValue;
},
getYOffsetOfTab : function utils_getYOffsetOfTab(aTab)
{
var extraCondition = this.canCollapseSubtree(aTab) ?
'[not(@'+this.kCOLLAPSED+'="true")]' :
'';
return this.evaluateXPath(
'sum((self::* | preceding-sibling::xul:tab[not(@hidden="true")]'+extraCondition+')'+
'/attribute::'+this.kY_OFFSET+')',
aTab,
Ci.nsIDOMXPathResult.NUMBER_TYPE
).numberValue;
},
getFutureBoxObject : function utils_getFutureBoxObject(aTab)
{
var tabBox = aTab.boxObject;
var xOffset = this.getXOffsetOfTab(aTab);
var yOffset = this.getYOffsetOfTab(aTab);
return {
width : tabBox.width,
height : tabBox.height,
x : tabBox.x + xOffset,
y : tabBox.y + yOffset,
screenX : tabBox.screenX + xOffset,
screenY : tabBox.screenY + yOffset
};
},
getTabActualScreenPosition : function utils_getTabActualScreenPosition(aTab)
{
return aTab.parentNode.orient == 'vertical' ?
this.getTabActualScreenY(aTab) :
this.getTabActualScreenX(aTab) ;
},
MATRIX_PATTERN : /matrix\((-?\d+),\s*(-?\d+),\s*(-?\d+),\s*(-?\d+),\s*(-?\d+),\s*(-?\d+)\)/,
getTabActualScreenX : function utils_getTabActualScreenX(aTab)
{
var x = aTab.boxObject.screenX;
var w = aTab.ownerDocument.defaultView;
var transform = w.getComputedStyle(aTab, null).transform;
var offset = transform && transform.match(this.MATRIX_PATTERN);
offset = offset ? parseFloat(offset[5]) : 0 ;
return x + offset;
},
getTabActualScreenY : function utils_getTabActualScreenY(aTab)
{
var y = aTab.boxObject.screenY;
var w = aTab.ownerDocument.defaultView;
var transform = w.getComputedStyle(aTab, null).transform;
var offset = transform && transform.match(this.MATRIX_PATTERN);
offset = offset ? parseFloat(offset[6]) : 0 ;
return y + offset;
},
isGroupTab : function utils_isGroupTab(aTab, aLazyCheck)
{
return (
(aLazyCheck || aTab.linkedBrowser.sessionHistory.count == 1) &&
aTab.linkedBrowser.currentURI.spec.indexOf('about:treestyletab-group') > -1
);
},
get pinnedTabsCount()
{
2012-09-23 02:51:11 -04:00
return this.browser.mTabContainer.querySelectorAll('tab[pinned="true"]').length;
},
forceExpandTabs : function utils_forceExpandTabs(aTabs)
2011-06-15 04:38:14 -04:00
{
var collapsedStates = aTabs.map(function(aTab) {
return this.getTabValue(aTab, this.kSUBTREE_COLLAPSED) == 'true';
}, this);
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let tab = aTabs[i];
this.collapseExpandSubtree(tab, false, true);
this.collapseExpandTab(tab, false, true);
}
2011-06-15 04:38:14 -04:00
return collapsedStates;
},
getTreeStructureFromTabs : function utils_getTreeStructureFromTabs(aTabs)
{
/* this returns...
[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)
*/
return this.cleanUpTreeStructureArray(
aTabs.map(function(aTab, aIndex) {
let tab = this.getParentTab(aTab);
let index = tab ? aTabs.indexOf(tab) : -1 ;
return index >= aIndex ? -1 : index ;
}, this),
-1
);
},
cleanUpTreeStructureArray : function utils_cleanUpTreeStructureArray(aTreeStructure, aDefaultParent)
{
var offset = 0;
aTreeStructure = aTreeStructure
.map(function(aPosition, aIndex) {
return (aPosition == aIndex) ? -1 : aPosition ;
})
.map(function(aPosition, aIndex) {
if (aPosition == -1) {
offset = aIndex;
return aPosition;
}
return aPosition - offset;
});
/* The final step, this validates all of values.
Smaller than -1 is invalid, so it becomes to -1. */
aTreeStructure = aTreeStructure.map(function(aIndex) {
return aIndex < -1 ? aDefaultParent : aIndex ;
}, this);
return aTreeStructure;
},
applyTreeStructureToTabs : function utils_applyTreeStructureToTabs(aTabs, aTreeStructure, aExpandStates)
{
var b = this.getTabBrowserFromChild(aTabs[0]);
if (!b) return;
var sv = b.treeStyleTab;
aTabs = aTabs.slice(0, aTreeStructure.length);
aTreeStructure = aTreeStructure.slice(0, aTabs.length);
2011-06-15 04:38:14 -04:00
aExpandStates = (aExpandStates && typeof aExpandStates == 'object') ?
aExpandStates :
aTabs.map(function(aTab) {
return !!aExpandStates;
});
aExpandStates = aExpandStates.slice(0, aTabs.length);
while (aExpandStates.length < aTabs.length) aExpandStates.push(-1);
var parentTab = null;
2012-02-04 16:31:03 -05:00
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
2012-02-04 16:31:03 -05:00
let tab = aTabs[i];
if (sv.isCollapsed(tab)) sv.collapseExpandTab(tab, false, true);
sv.detachTab(tab);
let parentIndexInTree = aTreeStructure[i];
2011-06-15 04:38:14 -04:00
if (parentIndexInTree < 0) // there is no parent, so this is a new parent!
parentTab = tab.getAttribute(sv.kID);
let parent = sv.getTabById(parentTab);
if (parent) {
let tabs = [parent].concat(sv.getDescendantTabs(parent));
2011-06-15 04:38:14 -04:00
parent = parentIndexInTree < tabs.length ? tabs[parentIndexInTree] : parent ;
}
if (parent) {
sv.attachTabTo(tab, parent, {
forceExpand : true,
dontMove : true
});
}
}
2011-06-15 04:38:14 -04:00
for (let i = aTabs.length-1; i > -1; i--)
{
sv.collapseExpandSubtree(aTabs[i], !sv.hasChildTabs(aTabs[i]) || !aExpandStates[i], true);
2011-06-15 04:38:14 -04:00
}
},
getTreeStructureFromTabBrowser : function utils_getTreeStructureFromTabBrowser(aTabBrowser)
{
2012-09-23 02:43:49 -04:00
return this.getTreeStructureFromTabs(this.getAllTabs(aTabBrowser));
},
applyTreeStructureToTabBrowser : function utils_applyTreeStructureToTabBrowser(aTabBrowser, aTreeStructure, aExpandAllTree)
{
2012-09-23 02:43:49 -04:00
var tabs = this.getAllTabs(aTabBrowser);
return this.applyTreeStructureToTabs(tabs, aTreeStructure, aExpandAllTree);
},
/* tabbar position */
get position() /* PUBLIC API */
{
return utils.getTreePref('tabbar.position') || 'top';
},
set position(aValue)
{
var position = String(aValue).toLowerCase();
if (!position || !/^(top|bottom|left|right)$/.test(position))
position = 'top';
if (position != utils.getTreePref('tabbar.position'))
utils.setTreePref('tabbar.position', position);
return aValue;
},
get currentTabbarPosition() /* for backward compatibility */
{
return this.position;
},
set currentTabbarPosition(aValue)
{
return this.position = aValue;
},
getPositionFlag : function utils_getPositionFlag(aPosition)
{
aPosition = String(aPosition).toLowerCase();
return (aPosition == 'left') ? this.kTABBAR_LEFT :
(aPosition == 'right') ? this.kTABBAR_RIGHT :
(aPosition == 'bottom') ? this.kTABBAR_BOTTOM :
this.kTABBAR_TOP;
},
/* Pref Listener */
domains : [
'extensions.treestyletab.',
'browser.tabs.animate',
'browser.tabs.loadFolderAndReplace',
'browser.tabs.insertRelatedAfterCurrent',
'extensions.stm.tabBarMultiRows' // Super Tab Mode
],
onPrefChange : function utils_onPrefChange(aPrefName)
{
var value = this.getPref(aPrefName);
switch (aPrefName)
{
case 'extensions.treestyletab.indent.vertical':
this.baseIndentVertical = value;
Services.obs.notifyObservers(null, this.kTOPIC_INDENT_MODIFIED, value);
return;
case 'extensions.treestyletab.indent.horizontal':
this.baseIndentHorizontal = value;
Services.obs.notifyObservers(null, this.kTOPIC_INDENT_MODIFIED, value);
return;
case 'extensions.treestyletab.tabbar.width':
case 'extensions.treestyletab.tabbar.shrunkenWidth':
return this.updateTabWidthPrefs(aPrefName);
case 'browser.tabs.insertRelatedAfterCurrent':
case 'browser.tabs.loadFolderAndReplace':
case 'extensions.stm.tabBarMultiRows': // Super Tab Mode
if (this.prefOverriding) return;
aPrefName += '.override';
this.setPref(aPrefName, value);
case 'browser.tabs.insertRelatedAfterCurrent.override':
case 'browser.tabs.loadFolderAndReplace.override':
case 'extensions.stm.tabBarMultiRows.override': // Super Tab Mode
if (this.getPref(aPrefName+'.force')) {
let defaultValue = this.getDefaultPref(aPrefName);
if (value != defaultValue) {
this.setPref(aPrefName, defaultValue);
return;
}
}
this.prefOverriding = true;
let (target = aPrefName.replace('.override', '')) {
let originalValue = this.getPref(target);
if (originalValue !== null && originalValue != value)
this.setPref(target+'.backup', originalValue);
this.setPref(target, this.getPref(aPrefName));
}
this.prefOverriding = false;
return;
case 'extensions.treestyletab.clickOnIndentSpaces.enabled':
return this.shouldDetectClickOnIndentSpaces = this.getPref(aPrefName);
case 'extensions.treestyletab.tabbar.scroll.smooth':
return this.smoothScrollEnabled = value;
case 'extensions.treestyletab.tabbar.scroll.duration':
return this.smoothScrollDuration = value;
case 'extensions.treestyletab.tabbar.scrollToNewTab.mode':
return this.scrollToNewTabMode = value;
case 'extensions.treestyletab.tabbar.narrowScrollbar.size':
return this.updateNarrowScrollbarStyle();
case 'browser.tabs.animate':
return this.animationEnabled = value;
case 'extensions.treestyletab.animation.indent.duration':
return this.indentDuration = value;
case 'extensions.treestyletab.animation.collapse.duration':
return this.collapseDuration = value;
case 'extensions.treestyletab.twisty.expandSensitiveArea':
return this.shouldExpandTwistyArea = value;
case 'extensions.treestyletab.counter.role.horizontal':
return this.counterRoleHorizontal = value;
case 'extensions.treestyletab.counter.role.vertical':
return this.counterRoleVertical = value;
default:
return;
}
},
updateTabWidthPrefs : function utils_updateTabWidthPrefs(aPrefName)
{
var expanded = utils.getTreePref('tabbar.width');
var shrunken = utils.getTreePref('tabbar.shrunkenWidth');
var originalExpanded = expanded;
var originalShrunken = shrunken;
if (aPrefName == 'extensions.treestyletab.tabbar.shrunkenWidth') {
if (expanded <= shrunken)
expanded = parseInt(shrunken / this.DEFAULT_SHRUNKEN_WIDTH_RATIO)
let w = this.browserWindow;
if (w && expanded > w.gBrowser.boxObject.width) {
expanded = w.gBrowser.boxObject.width * this.MAX_TABBAR_SIZE_RATIO;
if (expanded <= shrunken)
shrunken = parseInt(expanded * this.DEFAULT_SHRUNKEN_WIDTH_RATIO)
}
}
else {
if (expanded <= shrunken)
shrunken = parseInt(expanded * this.DEFAULT_SHRUNKEN_WIDTH_RATIO);
}
if (expanded != originalExpanded ||
shrunken != originalShrunken) {
utils.setTreePref('tabbar.width', Math.max(0, expanded));
utils.setTreePref('tabbar.shrunkenWidth', Math.max(0, shrunken));
}
},
get shouldApplyNewPref()
{
return (
!this.applyOnlyForActiveWindow ||
this.window == this.topBrowserWindow
) &&
!this.inWindowDestoructionProcess;
},
applyOnlyForActiveWindow : false,
setPrefForActiveWindow : function(aTask) {
TreeStyleTabBase.applyOnlyForActiveWindow = true;
try {
aTask.call(this);
}
finally {
TreeStyleTabBase.applyOnlyForActiveWindow = false;
}
}
};
TreeStyleTabBase.init();