2007-10-16 17:22:28 +00:00
var TreeStyleTabService = {
PREFROOT : 'extensions.treestyletab@piro.sakura.ne.jp' ,
2007-10-18 17:11:03 +00:00
kID : 'treestyletab-id' ,
kCHILDREN : 'treestyletab-children' ,
kPARENT : 'treestyletab-parent' ,
kINSERT _BEFORE : 'treestyletab-insert-before' ,
kSUBTREE _COLLAPSED : 'treestyletab-subtree-collapsed' ,
kCOLLAPSED : 'treestyletab-tab-collapsed' ,
kTWISTY : 'treestyletab-tab-tree-twisty' ,
2007-10-16 17:22:28 +00:00
2007-10-17 16:07:43 +00:00
levelMargin : 12 ,
2007-10-16 17:22:28 +00:00
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 '' ;
}
}
} ,
get SessionStore ( ) {
if ( ! this . _SessionStore ) {
this . _SessionStore = Components . classes [ '@mozilla.org/browser/sessionstore;1' ] . getService ( Components . interfaces . nsISessionStore ) ;
}
return this . _SessionStore ;
} ,
_SessionStore : null ,
/* Utilities */
2007-10-18 18:20:54 +00:00
2007-10-16 17:22:28 +00:00
isEventFiredOnTabIcon : function ( aEvent )
{
var tab = this . getTabFromEvent ( aEvent ) ;
if ( ! tab ) return false ;
var icon = document . getAnonymousElementByAttribute ( tab , 'class' , 'tab-icon' ) ;
var box = icon . boxObject ;
if ( aEvent . screenX > box . screenX &&
aEvent . screenY > box . screenY &&
aEvent . screenX < box . screenX + box . width &&
aEvent . screenY < box . screenY + box . height )
return true ;
return false ;
} ,
2007-10-18 17:11:03 +00:00
isEventFiredOnTwisty : function ( aEvent )
{
var node = aEvent . originalTarget ;
while ( node . getAttribute ( 'class' ) != this . kTWISTY && node . localName != 'tabs' )
{
node = node . parentNode ;
}
return ( node && node . getAttribute ( 'class' ) == this . kTWISTY ) ? true : false ;
} ,
2007-10-18 18:20:54 +00:00
2007-10-16 17:22:28 +00:00
get browser ( )
{
return 'SplitBrowser' ? SplitBrowser . activeBrowser : gBrowser ;
} ,
getArrayFromXPathResult : function ( 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 ;
} ,
getTabFromEvent : function ( aEvent )
{
var target = aEvent . originalTarget || aEvent . target ;
while ( target . localName != 'tab' && target . localName != 'tabs' && target . parentNode )
target = target . parentNode ;
return ( target . localName == 'tab' ) ? target : null ;
} ,
2007-10-17 16:07:43 +00:00
getTabFromFrame : function ( aFrame , aTabBrowser )
{
var b = aTabBrowser || this . browser ;
var docShell = aFrame . top
. QueryInterface ( Components . interfaces . nsIInterfaceRequestor )
. getInterface ( Components . interfaces . nsIWebNavigation )
. QueryInterface ( Components . interfaces . nsIDocShell ) ;
var tabs = b . mTabContainer . childNodes ;
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
if ( tabs [ i ] . linkedBrowser . docShell == docShell )
return tabs [ i ] ;
}
return null ;
} ,
2007-10-16 17:22:28 +00:00
getTabBrowserFromChildren : function ( aTab )
{
2007-10-17 16:07:43 +00:00
if ( ! aTab ) return null ;
2007-10-16 17:22:28 +00:00
if ( aTab . _ _treestyletab _ _linkedTabBrowser )
return aTab . _ _treestyletab _ _linkedTabBrowser ;
var target = aTab ;
while ( target . localName != 'tabbrowser' && target . parentNode )
target = target . parentNode ;
return ( target . localName == 'tabbrowser' ) ? target : null ;
} ,
showHideMenuItems : function ( aPopup )
{
var nodes = aPopup . childNodes ;
var pref ;
var b = this . getTabBrowserFromChildren ( aPopup ) || this . browser ;
var box = b . mTabContainer . mTabstrip || b . mTabContainer ;
var isVertical = ( ( box . getAttribute ( 'orient' ) || window . getComputedStyle ( box , '' ) . getPropertyValue ( '-moz-box-orient' ) ) == 'vertical' ) ;
var label ;
for ( var i = 0 , maxi = nodes . length ; i < maxi ; i ++ )
{
if (
( isVertical && ( label = nodes [ i ] . getAttribute ( 'label-vertical' ) ) ) ||
( ! isVertical && ( label = nodes [ i ] . getAttribute ( 'label-horizontal' ) ) )
)
nodes [ i ] . setAttribute ( 'label' , label ) ;
pref = this . getPref ( 'extensions.multipletab.show.' + nodes [ i ] . getAttribute ( 'id' ) . replace ( /-tabbrowser[0-9]+$/ , '' ) ) ;
if ( pref === null ) continue ;
if ( pref )
nodes [ i ] . removeAttribute ( 'hidden' ) ;
else
nodes [ i ] . setAttribute ( 'hidden' , true ) ;
}
var separators = this . getSeparators ( aPopup ) ;
for ( var i = separators . snapshotLength - 1 ; i > - 1 ; i -- )
{
separators . snapshotItem ( i ) . removeAttribute ( 'hidden' ) ;
}
var separator ;
while ( separator = this . getObsoleteSeparator ( aPopup ) )
{
separator . setAttribute ( 'hidden' , true ) ;
}
} ,
2007-10-17 17:45:09 +00:00
2007-10-16 17:22:28 +00:00
getSeparators : function ( aPopup )
{
try {
var xpathResult = document . evaluate (
'descendant::xul:menuseparator' ,
aPopup ,
2007-10-18 18:03:05 +00:00
this . NSResolver ,
2007-10-16 17:22:28 +00:00
XPathResult . ORDERED _NODE _SNAPSHOT _TYPE ,
null
) ;
}
catch ( e ) {
return { snapshotLength : 0 } ;
}
return xpathResult ;
} ,
getObsoleteSeparator : function ( aPopup )
{
try {
var xpathResult = document . evaluate (
'descendant::xul:menuseparator[not(@hidden)][not(following-sibling::*[not(@hidden)]) or not(preceding-sibling::*[not(@hidden)]) or local-name(following-sibling::*[not(@hidden)]) = "menuseparator"]' ,
aPopup ,
2007-10-18 18:03:05 +00:00
this . NSResolver ,
2007-10-16 17:22:28 +00:00
XPathResult . FIRST _ORDERED _NODE _TYPE ,
null
) ;
}
catch ( e ) {
return null ;
}
return xpathResult . singleNodeValue ;
} ,
/* Initializing */
2007-10-18 18:20:54 +00:00
2007-10-16 17:22:28 +00:00
init : function ( )
{
if ( ! ( 'gBrowser' in window ) ) return ;
window . removeEventListener ( 'load' , this , false ) ;
2007-10-17 13:25:50 +00:00
var appcontent = document . getElementById ( 'appcontent' ) ;
appcontent . addEventListener ( 'SubBrowserAdded' , this , false ) ;
appcontent . addEventListener ( 'SubBrowserRemoveRequest' , this , false ) ;
2007-10-16 17:22:28 +00:00
this . addPrefListener ( this ) ;
this . observe ( null , 'nsPref:changed' , 'extensions.treestyletab.' ) ;
2007-10-17 16:07:43 +00:00
eval ( 'window.nsBrowserAccess.prototype.openURI = ' +
window . nsBrowserAccess . prototype . openURI . toSource ( ) . replace (
/switch\s*\(aWhere\)/ ,
< > < ! [ CDATA [
if ( aOpener &&
aWhere == Components . interfaces . nsIBrowserDOMWindow . OPEN _NEWTAB ) {
var ownerBrowser = ( 'SplitBrowser' in window ) ? TreeStyleTabService . getTabBrowserFromChildren ( SplitBrowser . getSubBrowserAndBrowserFromFrame ( aOpener . top ) . browser ) : gBrowser ;
var parentTab = TreeStyleTabService . getTabFromFrame ( aOpener , ownerBrowser ) ;
ownerBrowser . _ _treestyletab _ _readyToAdoptNewTab = true ;
ownerBrowser . _ _treestyletab _ _parentTab = parentTab . getAttribute ( TreeStyleTabService . kID ) ;
}
switch ( aWhere )
] ] > < / >
)
) ;
window . QueryInterface ( Components . interfaces . nsIDOMChromeWindow ) . browserDOMWindow = null ;
window . QueryInterface ( Components . interfaces . nsIDOMChromeWindow ) . browserDOMWindow = new nsBrowserAccess ( ) ;
2007-10-16 17:22:28 +00:00
this . initTabBrowser ( gBrowser ) ;
} ,
2007-10-18 18:20:54 +00:00
2007-10-16 17:22:28 +00:00
initTabBrowser : function ( aTabBrowser )
{
aTabBrowser . mTabContainer . addEventListener ( 'TreeStyleTab:TabOpen' , this , true ) ;
aTabBrowser . mTabContainer . addEventListener ( 'TabClose' , this , true ) ;
2007-10-17 23:48:30 +00:00
aTabBrowser . mTabContainer . addEventListener ( 'TabMove' , this , true ) ;
aTabBrowser . mTabContainer . addEventListener ( 'SSTabRestoring' , this , true ) ;
2007-10-18 17:11:03 +00:00
aTabBrowser . mTabContainer . addEventListener ( 'click' , this , true ) ;
aTabBrowser . mTabContainer . addEventListener ( 'mousedown' , this , true ) ;
aTabBrowser . mTabContainer . addEventListener ( 'select' , this , true ) ;
2007-10-16 17:22:28 +00:00
aTabBrowser . mPanelContainer . addEventListener ( 'click' , this , true ) ;
2007-10-18 17:11:03 +00:00
eval ( 'aTabBrowser.mTabContainer.selectNewTab = ' +
aTabBrowser . mTabContainer . selectNewTab . toSource ( ) . replace (
/\{/ ,
< > < ! [ CDATA [
{
if ( aNewTab . _ _treestyletab _ _preventSelect ) {
aNewTab . _ _treestyletab _ _preventSelect = false ;
return ;
}
] ] > < / >
)
) ;
2007-10-16 17:22:28 +00:00
var addTabMethod = 'addTab' ;
var removeTabMethod = 'removeTab' ;
if ( aTabBrowser . _ _tabextensions _ _addTab ) {
addTabMethod = '__tabextensions__addTab' ;
removeTabMethod = '__tabextensions__removeTab' ;
}
aTabBrowser . _ _treestyletab _ _originalAddTab = aTabBrowser [ addTabMethod ] ;
aTabBrowser [ addTabMethod ] = function ( ) {
var tab = this . _ _treestyletab _ _originalAddTab . apply ( this , arguments ) ;
try {
TreeStyleTabService . initTab ( tab , this ) ;
}
catch ( e ) {
}
return tab ;
} ;
aTabBrowser . _ _treestyletab _ _originalRemoveTab = aTabBrowser [ removeTabMethod ] ;
aTabBrowser [ removeTabMethod ] = function ( aTab ) {
TreeStyleTabService . destroyTab ( aTab , this ) ;
var retVal = this . _ _treestyletab _ _originalRemoveTab . apply ( this , arguments ) ;
try {
if ( aTab . parentNode )
TreeStyleTabService . initTab ( aTab , this ) ;
}
catch ( e ) {
}
return retVal ;
} ;
// var tabContextMenu = document.getAnonymousElementByAttribute(aTabBrowser, 'anonid', 'tabContextMenu');
// tabContextMenu.addEventListener('popupshowing', this, false);
var tabs = aTabBrowser . mTabContainer . childNodes ;
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
this . initTab ( tabs [ i ] , aTabBrowser ) ;
}
delete addTabMethod ;
delete removeTabMethod ;
delete i ;
delete maxi ;
delete tabs ;
} ,
initTab : function ( aTab , aTabBrowser )
{
var id = 'tab-<' + Date . now ( ) + '-' + parseInt ( Math . random ( ) * 65000 ) + '>' ;
2007-10-17 18:18:16 +00:00
this . setTabValue ( aTab , this . kID , id ) ;
2007-10-16 17:22:28 +00:00
aTab . _ _treestyletab _ _linkedTabBrowser = aTabBrowser ;
2007-10-18 17:11:03 +00:00
this . initTabTwisty ( aTab ) ;
2007-10-16 17:22:28 +00:00
var event = document . createEvent ( 'Events' ) ;
event . initEvent ( 'TreeStyleTab:TabOpen' , true , false ) ;
aTab . dispatchEvent ( event ) ;
} ,
2007-10-18 17:11:03 +00:00
initTabTwisty : function ( aTab )
{
if ( document . getAnonymousElementByAttribute ( aTab , 'class' , this . kTWISTY ) ) return ;
var twisty = document . createElement ( 'toolbarbutton' ) ;
twisty . setAttribute ( 'class' , this . kTWISTY ) ;
var icon = document . getAnonymousElementByAttribute ( aTab , 'class' , 'tab-icon' ) ;
icon . appendChild ( twisty ) ;
} ,
2007-10-16 17:22:28 +00:00
destroy : function ( )
{
this . destroyTabBrowser ( gBrowser ) ;
window . removeEventListener ( 'unload' , this , false ) ;
2007-10-17 16:07:43 +00:00
var appcontent = document . getElementById ( 'appcontent' ) ;
2007-10-17 13:25:50 +00:00
appcontent . removeEventListener ( 'SubBrowserAdded' , this , false ) ;
appcontent . removeEventListener ( 'SubBrowserRemoveRequest' , this , false ) ;
2007-10-16 17:22:28 +00:00
this . removePrefListener ( this ) ;
var tabs = gBrowser . mTabContainer . childNodes ;
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
this . destroyTab ( tabs [ i ] ) ;
}
} ,
destroyTabBrowser : function ( aTabBrowser )
{
2007-10-18 15:40:06 +00:00
var tabs = aTabBrowser . mTabContainer . childNodes ;
var parent ;
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
parent = this . getParentTabOf ( tabs [ i ] ) ;
if ( parent )
this . setTabValue ( tabs [ i ] , this . kPARENT , parent . getAttribute ( this . kID ) ) ;
}
2007-10-16 17:22:28 +00:00
aTabBrowser . mTabContainer . removeEventListener ( 'TreeStyleTab:TabOpen' , this , true ) ;
aTabBrowser . mTabContainer . removeEventListener ( 'TabClose' , this , true ) ;
2007-10-17 23:48:30 +00:00
aTabBrowser . mTabContainer . removeEventListener ( 'TabMove' , this , true ) ;
aTabBrowser . mTabContainer . removeEventListener ( 'SSTabRestoring' , this , true ) ;
2007-10-18 17:11:03 +00:00
aTabBrowser . mTabContainer . removeEventListener ( 'click' , this , true ) ;
aTabBrowser . mTabContainer . removeEventListener ( 'mousedown' , this , true ) ;
aTabBrowser . mTabContainer . removeEventListener ( 'select' , this , true ) ;
2007-10-16 17:22:28 +00:00
aTabBrowser . mPanelContainer . removeEventListener ( 'click' , this , true ) ;
// var tabContextMenu = document.getAnonymousElementByAttribute(aTabBrowser, 'anonid', 'tabContextMenu');
// tabContextMenu.removeEventListener('popupshowing', this, false);
} ,
destroyTab : function ( aTab , aTabBrowser )
{
delete aTab . _ _treestyletab _ _linkedTabBrowser ;
} ,
/* Event Handling */
handleEvent : function ( aEvent )
{
switch ( aEvent . type )
{
case 'TreeStyleTab:TabOpen' :
this . onTabAdded ( aEvent ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-16 17:22:28 +00:00
case 'TabClose' :
this . onTabRemoved ( aEvent ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-16 17:22:28 +00:00
2007-10-17 23:48:30 +00:00
case 'TabMove' :
var tab = aEvent . originalTarget ;
2007-10-18 17:11:03 +00:00
this . initTabTwisty ( tab ) ; // twisty vanished after the tab is moved!!
var b = this . getTabBrowserFromChildren ( tab ) ;
2007-10-17 23:48:30 +00:00
if ( tab . getAttribute ( this . kCHILDREN ) && ! b . _ _treestyletab _ _isSubTreeMoving ) {
this . moveTabSubTreeTo ( tab , tab . _tPos ) ;
}
2007-10-18 17:11:03 +00:00
return ;
2007-10-17 23:48:30 +00:00
case 'SSTabRestoring' :
this . onTabRestored ( aEvent ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-17 23:48:30 +00:00
2007-10-16 17:22:28 +00:00
case 'click' :
2007-10-18 17:11:03 +00:00
if ( aEvent . target . ownerDocument == document ) {
this . onTabClick ( aEvent ) ;
return ;
}
2007-10-16 17:22:28 +00:00
var isMiddleClick = (
aEvent . button == 1 ||
aEvent . button == 0 && ( aEvent . ctrlKey || aEvent . metaKey )
) ;
var node = aEvent . originalTarget ;
while ( node . parentNode && ! node . href )
{
node = node . parentNode ;
}
if ( node . href && isMiddleClick ) {
var b = this . getTabBrowserFromChildren ( aEvent . currentTarget ) ;
b . _ _treestyletab _ _readyToAdoptNewTab = true ;
b . _ _treestyletab _ _parentTab = b . selectedTab . getAttribute ( this . kID ) ;
}
2007-10-18 17:11:03 +00:00
return ;
case 'mousedown' :
this . onTabMouseDown ( aEvent ) ;
return ;
case 'select' :
2007-10-18 18:03:05 +00:00
this . onTabSelect ( aEvent ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-16 17:22:28 +00:00
case 'load' :
this . init ( ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-16 17:22:28 +00:00
case 'unload' :
this . destroy ( ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-16 17:22:28 +00:00
case 'popupshowing' :
// this.showHideMenuItems(aEvent.target);
2007-10-18 17:11:03 +00:00
return ;
2007-10-17 13:25:50 +00:00
case 'SubBrowserAdded' :
this . initTabBrowser ( aEvent . originalTarget . browser ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-17 13:25:50 +00:00
case 'SubBrowserRemoveRequest' :
this . destroyTabBrowser ( aEvent . originalTarget . browser ) ;
2007-10-18 17:11:03 +00:00
return ;
2007-10-16 17:22:28 +00:00
}
} ,
onTabAdded : function ( aEvent )
{
var tab = aEvent . originalTarget ;
var b = this . getTabBrowserFromChildren ( tab ) ;
if ( b . _ _treestyletab _ _readyToAdoptNewTab ) {
var parent = this . getTabById ( b . _ _treestyletab _ _parentTab , b ) ;
if ( parent )
this . adoptTabTo ( tab , parent ) ;
}
b . _ _treestyletab _ _readyToAdoptNewTab = false ;
b . _ _treestyletab _ _parentTab = '' ;
} ,
onTabRemoved : function ( aEvent )
{
2007-10-18 17:11:03 +00:00
var tab = aEvent . originalTarget ;
var b = this . getTabBrowserFromChildren ( tab ) ;
if ( tab . getAttribute ( this . kSUBTREE _COLLAPSED ) ) {
var descendant = this . getDescendantTabsOf ( tab ) ;
for ( var i = descendant . length - 1 ; i > - 1 ; i -- )
{
b . removeTab ( descendant [ i ] ) ;
}
}
2007-10-17 17:45:09 +00:00
var firstChild = this . getFirstChildTabOf ( tab ) ;
var parentTab = this . getParentTabOf ( tab ) ;
var nextFocusedTab = null ;
2007-10-18 15:40:06 +00:00
if ( parentTab ) {
this . setTabValue ( tab , this . kPARENT , parentTab . getAttribute ( this . kID ) ) ;
var next = this . getNextSiblingTabOf ( tab ) ;
if ( next )
2007-10-18 17:11:03 +00:00
this . setTabValue ( tab , this . kINSERT _BEFORE , next . getAttribute ( this . kID ) ) ;
2007-10-18 15:40:06 +00:00
}
2007-10-17 17:45:09 +00:00
if ( firstChild ) {
2007-10-17 23:48:30 +00:00
var backupChildren = this . getTabValue ( tab , this . kCHILDREN ) ;
2007-10-17 18:18:16 +00:00
var children = this . getChildTabsOf ( tab ) ;
var self = this ;
var adoption = this . getPref ( 'extensions.treestyletab.adoptChildrenToGrandParentOnRemoveTab' ) ;
var processTab = ! adoption ? function ( aTab ) {
2007-10-18 15:40:06 +00:00
self . repudiateTab ( aTab , true ) ;
2007-10-17 18:36:39 +00:00
self . moveTabSubTreeTo ( aTab , b . mTabContainer . lastChild . _tPos ) ;
2007-10-17 18:18:16 +00:00
} :
parentTab ? function ( aTab ) {
2007-10-18 15:40:06 +00:00
self . adoptTabTo ( aTab , parentTab , tab , true ) ;
2007-10-17 18:18:16 +00:00
} :
function ( aTab ) {
2007-10-18 15:40:06 +00:00
self . repudiateTab ( aTab , true ) ;
2007-10-17 18:18:16 +00:00
} ;
2007-10-17 17:45:09 +00:00
for ( var i = 0 , maxi = children . length ; i < maxi ; i ++ )
{
2007-10-17 18:18:16 +00:00
processTab ( children [ i ] ) ;
}
2007-10-18 15:40:06 +00:00
this . updateTabsIndent ( children ) ;
2007-10-17 18:18:16 +00:00
if ( adoption ) {
nextFocusedTab = firstChild ;
2007-10-17 17:45:09 +00:00
}
2007-10-17 23:48:30 +00:00
this . setTabValue ( tab , this . kCHILDREN , backupChildren ) ;
2007-10-17 17:45:09 +00:00
}
2007-10-17 18:18:16 +00:00
if ( ! nextFocusedTab ) {
if ( parentTab ) {
var firstSibling = this . getFirstChildTabOf ( parentTab ) ;
var lastSibling = this . getLastChildTabOf ( parentTab ) ;
if ( tab == lastSibling ) {
if ( tab == firstSibling ) { // there is only one child
nextFocusedTab = parentTab ;
}
else { // previous sibling tab
nextFocusedTab = this . getPreviousSiblingTabOf ( tab ) ;
}
2007-10-17 17:45:09 +00:00
}
2007-10-18 15:40:06 +00:00
this . repudiateTab ( tab , true ) ;
2007-10-17 17:45:09 +00:00
}
2007-10-17 18:18:16 +00:00
else {
nextFocusedTab = this . getNextSiblingTabOf ( tab ) ;
}
2007-10-17 17:45:09 +00:00
}
if ( nextFocusedTab )
b . selectedTab = nextFocusedTab ;
2007-10-16 17:22:28 +00:00
} ,
2007-10-17 23:48:30 +00:00
onTabRestored : function ( aEvent )
{
var tab = aEvent . originalTarget ;
2007-10-18 15:40:06 +00:00
var b = this . getTabBrowserFromChildren ( tab ) ;
var id = this . getTabValue ( tab , this . kID ) ;
this . setTabValue ( tab , this . kID , id ) ;
2007-10-17 23:48:30 +00:00
2007-10-18 17:11:03 +00:00
var isSubTreeCollapsed = ( this . getTabValue ( tab , this . kSUBTREE _COLLAPSED ) == 'true' ) ;
2007-10-18 15:40:06 +00:00
var children = this . getTabValue ( tab , this . kCHILDREN ) ;
if ( children ) {
children = children . split ( '|' ) ;
var tabs = [ ] ;
for ( var i = 0 , maxi = children . length ; i < maxi ; i ++ )
{
if ( children [ i ] && ( children [ i ] = this . getTabById ( children [ i ] , b ) ) ) {
this . adoptTabTo ( children [ i ] , tab , null , true ) ;
tabs . push ( children [ i ] ) ;
}
}
2007-10-17 23:48:30 +00:00
}
2007-10-18 15:40:06 +00:00
var parent = this . getTabValue ( tab , this . kPARENT ) ;
2007-10-18 17:11:03 +00:00
var before = this . getTabValue ( tab , this . kINSERT _BEFORE ) ;
2007-10-18 15:40:06 +00:00
if ( parent && ( parent = this . getTabById ( parent , b ) ) ) {
this . adoptTabTo ( tab , parent , ( before ? this . getTabById ( before , b ) : null ) , true ) ;
this . deleteTabValue ( tab , this . kPARENT ) ;
this . updateTabsIndent ( [ tab ] ) ;
}
else if ( children ) {
this . updateTabsIndent ( tabs ) ;
2007-10-17 23:48:30 +00:00
}
2007-10-18 17:11:03 +00:00
/ *
if ( isSubTreeCollapsed ) {
this . collapseExpandTabSubTree ( tab , isSubTreeCollapsed ) ;
}
* /
} ,
onTabMouseDown : function ( aEvent )
{
if ( aEvent . button != 0 ||
! this . isEventFiredOnTwisty ( aEvent ) )
return ;
this . getTabFromEvent ( aEvent ) . _ _treestyletab _ _preventSelect = true ;
2007-10-17 23:48:30 +00:00
} ,
2007-10-16 17:22:28 +00:00
onTabClick : function ( aEvent )
{
2007-10-18 17:11:03 +00:00
if ( aEvent . button != 0 ||
! this . isEventFiredOnTwisty ( aEvent ) )
return ;
2007-10-16 17:22:28 +00:00
var tab = this . getTabFromEvent ( aEvent ) ;
2007-10-18 17:11:03 +00:00
this . collapseExpandTabSubTree ( tab , tab . getAttribute ( this . kSUBTREE _COLLAPSED ) != 'true' ) ;
2007-10-16 17:22:28 +00:00
2007-10-18 17:11:03 +00:00
aEvent . preventDefault ( ) ;
aEvent . stopPropagation ( ) ;
2007-10-16 17:22:28 +00:00
} ,
2007-10-18 18:03:05 +00:00
onTabSelect : function ( aEvent )
{
var b = this . getTabBrowserFromChildren ( aEvent . currentTarget ) ;
var tab = b . selectedTab
/ *
var p ;
if ( ( tab . getAttribute ( this . kCOLLAPSED ) == 'true' ) &&
( p = this . getParentTabOf ( tab ) ) ) {
b . selectedTab = p ;
}
* /
if ( tab . getAttribute ( this . kCOLLAPSED ) == 'true' ) {
var parentTab = tab ;
while ( parentTab = this . getParentTabOf ( parentTab ) )
{
this . collapseExpandTabSubTree ( parentTab , false ) ;
}
}
else if ( tab . getAttribute ( this . kCHILDREN ) &&
( tab . getAttribute ( this . kSUBTREE _COLLAPSED ) == 'true' ) &&
this . getPref ( 'extensions.treestyletab.autoCollapseExpandSubTreeOnSelect' ) ) {
2007-10-18 18:20:54 +00:00
this . collapseExpandTreesIntelligentlyFor ( tab ) ;
2007-10-18 18:03:05 +00:00
}
} ,
2007-10-18 18:20:54 +00:00
2007-10-17 17:45:09 +00:00
/* Tab Utilities */
2007-10-16 17:22:28 +00:00
2007-10-17 23:48:30 +00:00
getTabValue : function ( aTab , aKey )
{
var value = null ;
try {
value = this . SessionStore . getTabValue ( aTab , aKey ) ;
}
catch ( e ) {
}
return value ;
} ,
2007-10-17 17:45:09 +00:00
setTabValue : function ( aTab , aKey , aValue )
{
if ( ! aValue ) {
return this . deleteTabValue ( aTab , aKey ) ;
}
aTab . setAttribute ( aKey , aValue ) ;
try {
this . SessionStore . setTabValue ( aTab , aKey , aValue ) ;
}
catch ( e ) {
}
2007-10-17 23:48:30 +00:00
return aValue ;
2007-10-17 17:45:09 +00:00
} ,
deleteTabValue : function ( aTab , aKey )
{
aTab . removeAttribute ( aKey ) ;
try {
this . SessionStore . deleteTabValue ( aTab , aKey ) ;
}
catch ( e ) {
}
} ,
2007-10-16 17:22:28 +00:00
getTabById : function ( aId , aTabBrowser )
{
try {
var xpathResult = document . evaluate (
'descendant::xul:tab[@' + this . kID + ' = "' + aId + '"]' ,
aTabBrowser . mTabContainer ,
2007-10-18 18:03:05 +00:00
this . NSResolver ,
2007-10-16 17:22:28 +00:00
XPathResult . FIRST _ORDERED _NODE _TYPE ,
null
) ;
}
catch ( e ) {
return null ;
}
return xpathResult . singleNodeValue ;
} ,
getParentTabOf : function ( aTab )
{
var id = aTab . getAttribute ( this . kID ) ;
try {
var xpathResult = document . evaluate (
'parent::*/child::xul:tab[contains(@' + this . kCHILDREN + ', "' + id + '")]' ,
aTab ,
2007-10-18 18:03:05 +00:00
this . NSResolver ,
2007-10-16 17:22:28 +00:00
XPathResult . FIRST _ORDERED _NODE _TYPE ,
null
) ;
}
catch ( e ) {
return null ;
}
return xpathResult . singleNodeValue ;
} ,
2007-10-17 17:45:09 +00:00
getNextSiblingTabOf : function ( aTab )
2007-10-16 17:22:28 +00:00
{
2007-10-17 17:45:09 +00:00
var id = aTab . getAttribute ( this . kID ) ;
var parentTab = this . getParentTabOf ( aTab ) ;
2007-10-16 17:22:28 +00:00
2007-10-17 17:45:09 +00:00
if ( ! parentTab ) {
var next = aTab ;
do {
next = next . nextSibling ;
}
while ( next && this . getParentTabOf ( next ) ) ;
return next ;
}
2007-10-16 17:22:28 +00:00
2007-10-17 17:45:09 +00:00
var b = this . getTabBrowserFromChildren ( aTab ) ;
var children = parentTab . getAttribute ( this . kCHILDREN ) ;
if ( children ) {
2007-10-18 15:40:06 +00:00
var list = ( '|' + children ) . split ( '|' + id ) [ 1 ] . split ( '|' ) ;
2007-10-17 17:45:09 +00:00
for ( var i = 0 , maxi = list . length ; i < maxi ; i ++ )
{
firstChild = this . getTabById ( list [ i ] , b ) ;
if ( firstChild ) break ;
}
}
return firstChild ;
} ,
getPreviousSiblingTabOf : function ( aTab )
{
var id = aTab . getAttribute ( this . kID ) ;
var parentTab = this . getParentTabOf ( aTab ) ;
if ( ! parentTab ) {
var prev = aTab ;
do {
prev = prev . previousSibling ;
}
while ( prev && this . getParentTabOf ( prev ) ) ;
return prev ;
}
var b = this . getTabBrowserFromChildren ( aTab ) ;
var children = parentTab . getAttribute ( this . kCHILDREN ) ;
if ( children ) {
2007-10-18 15:40:06 +00:00
var list = ( '|' + children ) . split ( '|' + id ) [ 0 ] . split ( '|' ) ;
2007-10-17 17:45:09 +00:00
for ( var i = list . length - 1 ; i > - 1 ; i -- )
{
lastChild = this . getTabById ( list [ i ] , b )
if ( lastChild ) break ;
}
}
return lastChild ;
} ,
2007-10-17 18:36:39 +00:00
getDescendantTabsOf : function ( aTab )
{
var tabs = [ ] ;
this . getChildTabsOf ( aTab , tabs ) ;
return tabs ;
} ,
getChildTabsOf : function ( aTab , aAllTabsArray )
2007-10-17 17:45:09 +00:00
{
var tabs = [ ] ;
var children = aTab . getAttribute ( this . kCHILDREN ) ;
if ( ! children ) return tabs ;
2007-10-17 18:36:39 +00:00
if ( aAllTabsArray ) tabs = aAllTabsArray ;
2007-10-17 17:45:09 +00:00
var list = children . split ( '|' ) ;
var b = this . getTabBrowserFromChildren ( aTab ) ;
var tab ;
for ( var i = 0 , maxi = list . length ; i < maxi ; i ++ )
{
tab = this . getTabById ( list [ i ] , b )
2007-10-18 15:40:06 +00:00
if ( ! tab ) continue ;
tabs . push ( tab ) ;
if ( aAllTabsArray )
this . getChildTabsOf ( tab , tabs ) ;
2007-10-17 17:45:09 +00:00
}
return tabs ;
} ,
getFirstChildTabOf : function ( aTab )
{
var b = this . getTabBrowserFromChildren ( aTab ) ;
var children = aTab . getAttribute ( this . kCHILDREN ) ;
var firstChild = null ;
if ( children ) {
var list = children . split ( '|' ) ;
for ( var i = 0 , maxi = list . length ; i < maxi ; i ++ )
{
firstChild = this . getTabById ( list [ i ] , b )
if ( firstChild ) break ;
}
}
return firstChild ;
} ,
getLastChildTabOf : function ( aTab )
{
var b = this . getTabBrowserFromChildren ( aTab ) ;
var children = aTab . getAttribute ( this . kCHILDREN ) ;
2007-10-16 17:22:28 +00:00
var lastChild = null ;
if ( children ) {
var list = children . split ( '|' ) ;
for ( var i = list . length - 1 ; i > - 1 ; i -- )
{
lastChild = this . getTabById ( list [ i ] , b )
if ( lastChild ) break ;
}
}
2007-10-17 17:45:09 +00:00
return lastChild ;
} ,
/* Commands */
2007-10-18 15:40:06 +00:00
adoptTabTo : function ( aChild , aParent , aInsertBefore , aDontUpdateIndent )
2007-10-17 17:45:09 +00:00
{
2007-10-18 15:40:06 +00:00
if ( ! aChild || ! aParent ) return ;
2007-10-17 17:45:09 +00:00
var id = aChild . getAttribute ( this . kID ) ;
var b = this . getTabBrowserFromChildren ( aParent ) ;
2007-10-16 17:22:28 +00:00
2007-10-17 17:45:09 +00:00
this . repudiateTab ( aChild , true ) ;
var children = aParent . getAttribute ( this . kCHILDREN ) ;
var newIndex ;
2007-10-17 23:48:30 +00:00
if ( children . indexOf ( id ) > - 1 ) {
children = ( '|' + children ) . replace ( '|' + id , '' ) . replace ( /^\|/ ) ;
}
2007-10-17 17:45:09 +00:00
var beforeTab = aInsertBefore ? aInsertBefore . getAttribute ( this . kID ) : null ;
if ( aInsertBefore && children . indexOf ( beforeTab ) > - 1 ) {
2007-10-18 15:40:06 +00:00
children = children . replace ( beforeTab , id + '|' + beforeTab ) ;
2007-10-17 17:45:09 +00:00
newIndex = aInsertBefore . _tPos ;
2007-10-16 17:22:28 +00:00
}
2007-10-17 17:45:09 +00:00
else {
children = ( ( children || '' ) + '|' + id ) . replace ( /^\|/ , '' ) ;
2007-10-18 15:40:06 +00:00
var refTab = aParent ;
2007-10-17 17:45:09 +00:00
var lastChild = this . getLastChildTabOf ( aParent ) ;
2007-10-18 15:40:06 +00:00
if ( lastChild ) {
var descendant = this . getDescendantTabsOf ( lastChild ) ;
if ( descendant . length ) lastChild = descendant [ descendant . length - 1 ] ;
}
2007-10-17 17:45:09 +00:00
newIndex = ( lastChild ? lastChild : aParent ) . _tPos + 1 ;
2007-10-16 17:22:28 +00:00
}
2007-10-17 17:45:09 +00:00
this . setTabValue ( aParent , this . kCHILDREN , children ) ;
2007-10-16 17:22:28 +00:00
if ( newIndex > aChild . _tPos ) newIndex -- ;
2007-10-17 23:48:30 +00:00
this . moveTabSubTreeTo ( aChild , newIndex ) ;
2007-10-16 17:22:28 +00:00
2007-10-18 18:20:54 +00:00
if (
(
aParent . getAttribute ( this . kSUBTREE _COLLAPSED ) == 'true' ||
children . indexOf ( '|' ) < 0 // first child
) &&
this . getPref ( 'extensions.treestyletab.autoCollapseExpandSubTreeOnSelect' )
) {
this . collapseExpandTreesIntelligentlyFor ( aChild ) ;
}
else if ( aParent . getAttribute ( this . kSUBTREE _COLLAPSED ) == 'true' ) {
var p = aParent ;
do {
this . collapseExpandTabSubTree ( p , false ) ;
}
while ( p = this . getParentTabOf ( p ) ) ;
2007-10-18 17:11:03 +00:00
}
else
this . collapseExpandTab ( aChild , true ) ;
}
if ( aParent . getAttribute ( this . kCOLLAPSED ) == 'true' )
this . collapseExpandTab ( aChild , true ) ;
2007-10-18 15:40:06 +00:00
if ( ! aDontUpdateIndent ) this . updateTabsIndent ( [ aChild ] ) ;
2007-10-16 17:22:28 +00:00
} ,
2007-10-17 17:45:09 +00:00
repudiateTab : function ( aChild , aDontUpdateIndent )
2007-10-16 17:22:28 +00:00
{
2007-10-18 15:40:06 +00:00
if ( ! aChild ) return ;
2007-10-17 17:45:09 +00:00
var parentTab = this . getParentTabOf ( aChild ) ;
if ( ! parentTab ) return ;
2007-10-16 17:22:28 +00:00
var id = aChild . getAttribute ( this . kID ) ;
2007-10-17 17:45:09 +00:00
var children = ( '|' + parentTab . getAttribute ( this . kCHILDREN ) )
2007-10-16 17:22:28 +00:00
. replace ( new RegExp ( '\\|' + id ) , '' )
. replace ( /^\|/ , '' ) ;
2007-10-17 17:45:09 +00:00
this . setTabValue ( parentTab , this . kCHILDREN , children ) ;
if ( ! aDontUpdateIndent ) this . updateTabsIndent ( [ aChild ] ) ;
} ,
updateTabsIndent : function ( aTabs , aLevel )
{
if ( ! aTabs || ! aTabs . length ) return ;
if ( aLevel === void ( 0 ) ) {
var parentTab = this . getParentTabOf ( aTabs [ 0 ] ) ;
var aLevel = 0 ;
while ( parentTab )
{
aLevel ++ ;
parentTab = this . getParentTabOf ( parentTab ) ;
2007-10-16 17:22:28 +00:00
}
}
2007-10-17 17:45:09 +00:00
var indent = ( this . levelMargin * aLevel ) + 'px' ;
for ( var i = 0 , maxi = aTabs . length ; i < maxi ; i ++ )
{
aTabs [ i ] . setAttribute ( 'style' , aTabs [ i ] . getAttribute ( 'style' ) + ';margin-left:' + indent + ' !important;' ) ;
this . updateTabsIndent ( this . getChildTabsOf ( aTabs [ i ] ) , aLevel + 1 ) ;
}
2007-10-16 17:22:28 +00:00
} ,
2007-10-17 18:36:39 +00:00
moveTabSubTreeTo : function ( aTab , aIndex )
{
2007-10-18 17:11:03 +00:00
if ( ! aTab ) return ;
2007-10-17 18:36:39 +00:00
var b = this . getTabBrowserFromChildren ( aTab ) ;
2007-10-17 23:48:30 +00:00
b . _ _treestyletab _ _isSubTreeMoving = true ;
2007-10-17 18:36:39 +00:00
b . moveTabTo ( aTab , aIndex ) ;
2007-10-17 23:48:30 +00:00
var tabs = this . getDescendantTabsOf ( aTab ) ;
2007-10-17 18:36:39 +00:00
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
2007-10-18 15:40:06 +00:00
b . moveTabTo ( tabs [ i ] , aTab . _tPos + i + ( aTab . _tPos < tabs [ i ] . _tPos ? 1 : 0 ) ) ;
2007-10-17 18:36:39 +00:00
}
2007-10-17 23:48:30 +00:00
b . _ _treestyletab _ _isSubTreeMoving = false ;
2007-10-17 18:36:39 +00:00
} ,
2007-10-18 17:11:03 +00:00
collapseExpandTabSubTree : function ( aTab , aCollapse )
{
if ( ! aTab ) return ;
if ( aTab . getAttribute ( this . kSUBTREE _COLLAPSED ) == String ( aCollapse ) ) return ;
this . setTabValue ( aTab , this . kSUBTREE _COLLAPSED , aCollapse ) ;
var tabs = this . getChildTabsOf ( aTab ) ;
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
this . collapseExpandTab ( tabs [ i ] , aCollapse ) ;
}
} ,
collapseExpandTab : function ( aTab , aCollapse )
{
if ( ! aTab ) return ;
this . setTabValue ( aTab , this . kCOLLAPSED , aCollapse ) ;
var b = this . getTabBrowserFromChildren ( aTab ) ;
var p ;
if ( aTab == b . selectedTab && ( p = this . getParentTabOf ( aTab ) ) ) b . selectedTab = p ;
var isSubTreeCollapsed = ( aTab . getAttribute ( this . kSUBTREE _COLLAPSED ) == 'true' ) ;
var tabs = this . getChildTabsOf ( aTab ) ;
for ( var i = 0 , maxi = tabs . length ; i < maxi ; i ++ )
{
if ( ! isSubTreeCollapsed )
this . collapseExpandTab ( tabs [ i ] , aCollapse ) ;
}
} ,
2007-10-18 18:20:54 +00:00
collapseExpandTreesIntelligentlyFor : function ( aTab )
{
var b = this . getTabBrowserFromChildren ( aTab ) ;
var expandedParentTabs = [
aTab . getAttribute ( this . kID )
] ;
var parentTab = aTab ;
while ( parentTab = this . getParentTabOf ( parentTab ) )
{
expandedParentTabs . push ( parentTab . getAttribute ( this . kID ) ) ;
}
expandedParentTabs = expandedParentTabs . join ( '|' ) ;
try {
var xpathResult = document . evaluate (
'child::xul:tab[@' + this . kCHILDREN + ' and not(@' + this . kCOLLAPSED + '="true") and not(@' + this . kSUBTREE _COLLAPSED + '="true") and not(contains("' + expandedParentTabs + '", @' + this . kID + '))]' ,
b . mTabContainer ,
this . NSResolver ,
XPathResult . ORDERED _NODE _SNAPSHOT _TYPE ,
null
) ;
var collapseTab ;
var isDescendant ;
for ( var i = 0 , maxi = xpathResult . snapshotLength ; i < maxi ; i ++ )
{
isDescendant = false ;
collapseTab = xpathResult . snapshotItem ( i ) ;
var parentTab = collapseTab ;
while ( parentTab = this . getParentTabOf ( parentTab ) )
{
if ( parentTab != aTab ) continue ;
isDescendant = true ;
}
if ( ! isDescendant )
this . collapseExpandTabSubTree ( collapseTab , true ) ;
}
}
catch ( e ) {
}
this . collapseExpandTabSubTree ( aTab , false ) ;
} ,
2007-10-18 17:11:03 +00:00
2007-10-16 17:22:28 +00:00
/* Pref Listener */
domain : 'extensions.treestyletab' ,
observe : function ( aSubject , aTopic , aPrefName )
{
if ( aTopic != 'nsPref:changed' ) return ;
var value = this . getPref ( aPrefName ) ;
switch ( aPrefName )
{
case 'extensions.treestyletab.' :
break ;
default :
break ;
}
} ,
/* Save/Load Prefs */
get Prefs ( )
{
if ( ! this . _Prefs ) {
this . _Prefs = Components . classes [ '@mozilla.org/preferences;1' ] . getService ( Components . interfaces . nsIPrefBranch ) ;
}
return this . _Prefs ;
} ,
_Prefs : null ,
getPref : function ( aPrefstring )
{
try {
switch ( this . Prefs . getPrefType ( aPrefstring ) )
{
case this . Prefs . PREF _STRING :
return decodeURIComponent ( escape ( this . Prefs . getCharPref ( aPrefstring ) ) ) ;
break ;
case this . Prefs . PREF _INT :
return this . Prefs . getIntPref ( aPrefstring ) ;
break ;
default :
return this . Prefs . getBoolPref ( aPrefstring ) ;
break ;
}
}
catch ( e ) {
}
return null ;
} ,
setPref : function ( aPrefstring , aNewValue )
{
var pref = this . Prefs ;
var type ;
try {
type = typeof aNewValue ;
}
catch ( e ) {
type = null ;
}
switch ( type )
{
case 'string' :
pref . setCharPref ( aPrefstring , unescape ( encodeURIComponent ( aNewValue ) ) ) ;
break ;
case 'number' :
pref . setIntPref ( aPrefstring , parseInt ( aNewValue ) ) ;
break ;
default :
pref . setBoolPref ( aPrefstring , aNewValue ) ;
break ;
}
return true ;
} ,
clearPref : function ( aPrefstring )
{
try {
this . Prefs . clearUserPref ( aPrefstring ) ;
}
catch ( e ) {
}
return ;
} ,
addPrefListener : function ( aObserver )
{
var domains = ( 'domains' in aObserver ) ? aObserver . domains : [ aObserver . domain ] ;
try {
var pbi = this . Prefs . QueryInterface ( Components . interfaces . nsIPrefBranchInternal ) ;
for ( var i = 0 ; i < domains . length ; i ++ )
pbi . addObserver ( domains [ i ] , aObserver , false ) ;
}
catch ( e ) {
}
} ,
removePrefListener : function ( aObserver )
{
var domains = ( 'domains' in aObserver ) ? aObserver . domains : [ aObserver . domain ] ;
try {
var pbi = this . Prefs . QueryInterface ( Components . interfaces . nsIPrefBranchInternal ) ;
for ( var i = 0 ; i < domains . length ; i ++ )
pbi . removeObserver ( domains [ i ] , aObserver , false ) ;
}
catch ( e ) {
}
}
} ;
window . addEventListener ( 'load' , TreeStyleTabService , false ) ;
window . addEventListener ( 'unload' , TreeStyleTabService , false ) ;