( function ( window ) { window . tabkit = new function _tabkit ( ) { const tk = this ; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" ; const { classes : Cc , interfaces : Ci , utils : Cu } = Components ; const PREF_BRANCH = "extensions.tabkit." ; const TAB_MIN_WIDTH = 50 ; const { Preferences } = Cu . import ( "resource://gre/modules/Preferences.jsm" , { } ) ; const { RecentWindow } = Cu . import ( "resource:///modules/RecentWindow.jsm" , { } ) ; const TabStateFlusher = ( function ( ) { let result ; try { result = Cu . import ( "resource:///modules/sessionstore/TabStateFlusher.jsm" , { } ) . TabStateFlusher ; } catch ( e ) { } return result ; } ) ( ) ; if ( typeof gPrefService === "undefined" || gPrefService == null ) gPrefService = Cc [ "@mozilla.org/preferences-service;1" ] . getService ( Ci . nsIPrefBranch ) ; const _console = Cc [ "@mozilla.org/consoleservice;1" ] . getService ( Ci . nsIConsoleService ) ; const _ios = Cc [ "@mozilla.org/network/io-service;1" ] . getService ( Ci . nsIIOService ) ; const _prefs = Cc [ "@mozilla.org/preferences-service;1" ] . getService ( Ci . nsIPrefService ) . getBranch ( PREF_BRANCH ) ; this . localPrefService = _prefs ; const _ps = Cc [ "@mozilla.org/embedcomp/prompt-service;1" ] . getService ( Ci . nsIPromptService ) ; const _sm = Cc [ "@mozilla.org/scriptsecuritymanager;1" ] . getService ( Ci . nsIScriptSecurityManager ) ; let _ss = null ; const _winvars = { } ; if ( "@mozilla.org/browser/sessionstore;1" in Cc ) { _ss = Cc [ "@mozilla.org/browser/sessionstore;1" ] . getService ( Ci . nsISessionStore ) ; } const _sound = Cc [ "@mozilla.org/sound;1" ] . getService ( Ci . nsISound ) ; const _wm = Cc [ "@mozilla.org/appshell/window-mediator;1" ] . getService ( Ci . nsIWindowMediator ) ; const nsTransferable = Components . Constructor ( "@mozilla.org/widget/transferable;1" , "nsITransferable" ) ; function Transferable ( source ) { const res = nsTransferable ( ) ; let final_source = source ; if ( "init" in res ) { if ( final_source instanceof Ci . nsIDOMWindow ) final_source = final_source . QueryInterface ( Ci . nsIInterfaceRequestor ) . getInterface ( Ci . nsIWebNavigation ) ; res . init ( final_source ) ; } return res ; } this . tabSetHidden = function tabSetHidden ( tab , hidden ) { if ( hidden ) gBrowser . hideTab ( tab ) ; else gBrowser . showTab ( tab ) ; } ; this . logs = { dump : [ ] , log : [ ] , debug : [ ] , } ; this . dump = function dump ( error , actualException = null ) { try { let stack ; if ( _prefs . getBoolPref ( "debug" ) ) { const scriptError = Cc [ "@mozilla.org/scripterror;1" ] . createInstance ( Ci . nsIScriptError ) ; if ( ! actualException && typeof error === "object" ) actualException = error ; const haveException = ! ! actualException ; if ( haveException && actualException . stack ) { stack = actualException . stack ; } else { stack = new Error ( ) . stack ; stack = stack . substring ( stack . indexOf ( "\n" , stack . indexOf ( "\n" ) + 1 ) + 1 ) ; } const message = "TK Error: \"" + error + "\"\nat:\u00A0" + stack . replace ( "\n@:0" , "" ) . replace ( /\n/g , "\n    " ) ; const sourceName = ( haveException && "fileName" in actualException && actualException . fileName ) ? actualException . fileName : Components . stack . caller . filename ; const sourceLine = ( haveException && "sourceLine" in actualException && actualException . sourceLine ) ? actualException . sourceLine : Components . stack . caller . sourceLine ; const lineNumber = ( haveException && "lineNumber" in actualException && actualException . lineNumber ) ? actualException . lineNumber : Components . stack . caller . lineNumber ; const columnNumber = ( haveException && "columnNumber" in actualException && actualException . columnNumber ) ? actualException . columnNumber : 0 ; const flags = haveException ? scriptError . errorFlag : scriptError . warningFlag ; const category = "JavaScript error" ; scriptError . init ( message , sourceName , sourceLine , lineNumber , columnNumber , flags , category ) ; tk . logs . dump . push ( scriptError ) ; _console . logMessage ( scriptError ) ; } else { tk . logs . dump . push ( String ( error ) + "\n" + tk . quickStack ( ) ) ; } } catch ( ex ) { } } ; this . log = function log ( message ) { try { tk . logs . log . push ( message ) ; if ( _prefs . getBoolPref ( "debug" ) ) { const msg = "TK: " + message ; _console . logStringMessage ( msg ) ; } } catch ( ex ) { } } ; this . debug = function debug ( message ) { try { tk . logs . debug . push ( message ) ; if ( _prefs . getBoolPref ( "debug" ) && _prefs . getBoolPref ( "debugMinorToo" ) ) { const msg = "TK Debug: " + message ; _console . logStringMessage ( msg ) ; } } catch ( ex ) { } } ; this . assert = function assert ( condition , localEval , message ) { if ( ! _prefs . getBoolPref ( "debug" ) || localEval ( condition ) ) return ; let title = "Assert Failed: '" + condition + "' in " + Components . stack . caller . name + "(" ; if ( arguments . callee . caller . arguments . length > 0 ) title += ( uneval ( arguments . callee . caller . arguments [ 0 ] ) as any ) . toString ( ) ; for ( let i = 1 ; i < arguments . callee . caller . arguments . length ; i ++ ) title += ", " + ( uneval ( arguments . callee . caller . arguments [ i ] ) as any ) . toString ( ) ; title += ")" ; const msg = ( message ? message + "\n\n" : "" ) + "Stacktrace:\n" + tk . quickStack ( ) ; tk . dump ( title + "\n\n" + msg ) ; if ( "quickprompt" in window ) window . quickprompt ( localEval , title , msg , "help()" ) ; } ; this . startsWith = function startsWith ( str , start ) { return str . indexOf ( start ) === 0 ; } ; this . endsWith = function endsWith ( str , end ) { const startPos = str . length - end . length ; if ( startPos < 0 ) return false ; return str . lastIndexOf ( end , startPos ) === startPos ; } ; this . rawMD5 = function rawMD5 ( str ) { const converter = Cc [ "@mozilla.org/intl/scriptableunicodeconverter" ] . createInstance ( Ci . nsIScriptableUnicodeConverter ) ; converter . charset = "UTF-8" ; const result = { } ; const data = converter . convertToByteArray ( str , result ) ; const ch = Cc [ "@mozilla.org/security/hash;1" ] . createInstance ( Ci . nsICryptoHash ) ; ch . init ( ch . MD5 ) ; ch . update ( data , data . length ) ; return ch . finish ( false ) ; } ; this . randInt = function randInt ( min , max ) { return Math . floor ( Math . random ( ) * ( max - min + 1 ) ) + min ; } ; this . getWindowValue = function getWindowValue ( aKey ) { if ( _ss ) return _ss . getWindowValue ( window , aKey ) ; else return ( aKey in _winvars ? _winvars [ aKey ] : "" ) ; } ; this . setWindowValue = function setWindowValue ( aKey , aStringValue ) { if ( _ss ) _ss . setWindowValue ( window , aKey , aStringValue ) ; else _winvars [ aKey ] = aStringValue ; } ; this . deleteWindowValue = function removeWindowValue ( aKey ) { if ( _ss ) _ss . deleteWindowValue ( window , aKey ) ; else delete _winvars [ aKey ] ; } ; this . addDelayedEventListener = function addDelayedEventListener ( target , eventType , listener ) { if ( typeof listener === "object" ) { target . addEventListener ( eventType , function __delayedEventListener ( event ) { window . setTimeout ( function ( listener2 ) { listener2 . handleEvent ( event ) ; } , 0 , listener ) ; } , false ) ; } else { target . addEventListener ( eventType , function __delayedEventListener ( event ) { window . setTimeout ( function ( listener2 ) { listener2 ( event ) ; } , 0 , listener ) ; } , false ) ; } } ; this . scrollToElement = function scrollToElement ( overflowPane , targetItem ) { const scrollbar = overflowPane . mVerticalScrollbar ; if ( ! scrollbar ) { return ; } const container = targetItem . parentNode ; if ( container == null ) return ; let firstChild = container . firstChild ; while ( firstChild . hidden ) firstChild = firstChild . nextSibling ; let lastChild = container . lastChild ; while ( lastChild . hidden ) lastChild = lastChild . previousSibling ; let curpos = parseInt ( scrollbar . getAttribute ( "curpos" ) ) ; if ( isNaN ( curpos ) ) { tk . debug ( "curpos was NaN" ) ; curpos = 0 ; } const firstY = firstChild . boxObject . y ; const targetY = targetItem . boxObject . y ; const lastY = lastChild . boxObject . y ; const height = targetItem . boxObject . height ; const relativeY = targetY - firstY ; const paneHeight = overflowPane . boxObject . height ; if ( ( lastY - firstY ) % height === 0 && curpos % height !== 0 && ( curpos + paneHeight + firstY - lastY ) % height !== 0 ) { curpos = height * Math . round ( curpos / height ) ; } let minpos = relativeY ; if ( _prefs . getBoolPref ( "scrollOneExtra" ) && minpos > 0 && lastY - firstY > height ) { minpos -= height ; } if ( minpos < curpos ) { curpos = minpos ; } else { let maxpos = relativeY + height - paneHeight ; if ( _prefs . getBoolPref ( "scrollOneExtra" ) && targetY < lastY && lastY - firstY > height ) { maxpos += height ; } if ( maxpos > curpos ) { curpos = maxpos ; } } scrollbar . setAttribute ( "curpos" , curpos ) ; } ; this . moveBefore = function moveBefore ( tabToMove , target ) { try { let newIndex = target . _tPos ; if ( newIndex > tabToMove . _tPos ) newIndex -- ; if ( newIndex !== tabToMove . _tPos ) gBrowser . moveTabTo ( tabToMove , newIndex ) ; } catch ( ex ) { tk . dump ( ex ) ; } } ; this . moveAfter = function moveAfter ( tabToMove , target ) { try { let newIndex = target . _tPos + 1 ; if ( newIndex > tabToMove . _tPos ) newIndex -- ; if ( newIndex !== tabToMove . _tPos ) gBrowser . moveTabTo ( tabToMove , newIndex ) ; } catch ( ex ) { tk . dump ( ex ) ; } } ; this . quickStack = function quickStack ( ) { let func = arguments . callee . caller . caller ; let stack = "" ; for ( let i = 1 ; func && i < 8 ; i ++ ) { stack += " " + i + ". " + func . name ; func = func . caller ; } return stack ; } ; this . beep = function beep ( ) { _sound . beep ( ) ; } ; this . getTabId = function ( tab ) { if ( ! tab . hasAttribute ( "tabid" ) ) { tk . generateNewTabId ( tab ) ; } return tab . getAttribute ( "tabid" ) ; } ; this . generateNewTabId = function ( tab ) { const new_id = tk . generateId ( ) ; tab . setAttribute ( "tabid" , new_id ) ; return new_id ; } ; this . getTabGroupId = function ( tab ) { if ( ! tab . hasAttribute ( "groupid" ) ) { return null ; } return tab . getAttribute ( "groupid" ) ; } ; this . TabBar = this . TabBar || { } ; this . TabBar . Mode = this . TabBar . Mode || { } ; this . TabBar . Mode . getIsVerticalMode = function getIsVerticalMode ( ) { return gBrowser . hasAttribute ( "vertitabbar" ) ; } ; this . DomUtility = this . DomUtility || { } ; this . DomUtility . insertBefore = function insertBefore ( referenceNode , newNode ) { referenceNode . parentNode . insertBefore ( newNode , referenceNode ) ; } ; this . DomUtility . insertAfter = function insertAfter ( referenceNode , newNode ) { referenceNode . parentNode . insertBefore ( newNode , referenceNode . nextSibling ) ; } ; this . VerticalTabBarScrollbar = this . VerticalTabBarScrollbar || { } ; this . VerticalTabBarScrollbar . getElement = function ( ) { if ( ! tk . TabBar . Mode . getIsVerticalMode ( ) ) { return null ; } const innerBox = ( document as mozDocument ) . getAnonymousElementByAttribute ( gBrowser . tabContainer . mTabstrip . _scrollbox , "class" , "box-inherit scrollbox-innerbox" ) ; return ( innerBox as any ) . mVerticalScrollbar ; } ; this . preInitListeners = [ ] as Array < ( e : mozEvent ) => void > ; this . initListeners = [ ] as Array < ( e : mozEvent ) => void > ; this . postInitListeners = [ ] as Array < ( e : mozEvent ) => void > ; this . tryListener = function tryListener ( type , listener , event ) { try { listener ( event ) ; } catch ( ex ) { const listenerString = "name" in listener ? listener . name : listener . toSource ( ) . substring ( 0 , 78 ) ; tk . dump ( type + " listener '" + listenerString + "' failed with exception:\n" + ex , ex ) ; } } ; this . onDOMContentLoaded = function onDOMContentLoaded ( event ) { if ( ( event . originalTarget as any ) !== document ) return ; window . removeEventListener ( "DOMContentLoaded" , tk . onDOMContentLoaded , false ) ; if ( ! _prefs . getBoolPref ( "firstRunWizardDone" ) ) { window . setTimeout ( function __startfirstRunWizard ( ) { gBrowser . selectedTab = gBrowser . addTab ( "chrome://tabkit/content/firstRunWizard.xul" ) ; } , 1500 ) ; } tk . preInitListeners . forEach ( function ( listener ) { tk . tryListener ( "DOMContentLoaded" , listener , event ) ; } ) ; } ; this . onLoad = function onLoad ( event ) { if ( ( event . originalTarget as any ) !== document ) return ; window . removeEventListener ( "load" , tk . onLoad , false ) ; tk . initListeners . forEach ( function ( listener ) { tk . tryListener ( "load" , listener , event ) ; } ) ; window . setTimeout ( function __runPostInitListeners ( ) { tk . postInitListeners . forEach ( function ( listener ) { listener ( event ) ; } ) ; } , 0 ) ; } ; this . UAStyleSheets = [ "chrome://tabkit/content/ua.css" ] ; this . preInitUAStyleSheets = function preInitUAStyleSheets ( ) { const sss = Cc [ "@mozilla.org/content/style-sheet-service;1" ] . getService ( Ci . nsIStyleSheetService ) ; const ios = Cc [ "@mozilla.org/network/io-service;1" ] . getService ( Ci . nsIIOService ) ; tk . UAStyleSheets . forEach ( function ( s ) { const uri = ios . newURI ( s , null , null ) ; if ( ! sss . sheetRegistered ( uri , sss . AGENT_SHEET ) ) sss . loadAndRegisterSheet ( uri , sss . AGENT_SHEET ) ; } ) ; } ; this . preInitListeners . push ( this . preInitUAStyleSheets ) ; this . getCSSRule = function getCSSRule ( ruleName , deleteIt = false ) { ruleName = ruleName . toLowerCase ( ) ; for ( let i = 0 ; i < document . styleSheets . length ; i ++ ) { const styleSheet = ( document . styleSheets [ i ] as any ) ; for ( let j = 0 ; j < styleSheet . cssRules . length ; j ++ ) { const cssRule = styleSheet . cssRules [ j ] ; if ( "selectorText" in cssRule && cssRule . selectorText && cssRule . selectorText . toLowerCase ( ) === ruleName ) { if ( deleteIt ) { styleSheet . deleteRule ( j ) ; return true ; } return cssRule ; } } } return false ; } ; let _tabContainer ; let _tabs ; let _tabBar ; this . preInitShortcuts = function preInitShortcuts ( ) { if ( window . location . toString ( ) !== "chrome://browser/content/browser.xul" ) tk . dump ( "preInitShortcuts should only be run in browser windows, as tabkit.js is only loaded into browser.xul" ) ; if ( ! gBrowser ) tk . dump ( "gBrowser must not be null after preInitShortcuts!" ) ; _tabContainer = gBrowser . tabContainer ; _tabs = gBrowser . tabs ; _tabBar = document . getElementById ( "TabsToolbar" ) ; } ; this . preInitListeners . push ( this . preInitShortcuts ) ; type LocalPrefListenerFunc = ( changedPref : string ) => any ; interface GlobalPrefObserver { listeners : Array < LocalPrefListenerFunc > ; observe : ( aSubject : any , aTopic : string , aData : any ) => void ; } const _globalPrefObservers = new Map ( ) ; const _localPrefListeners = new Map ( ) ; this . preInitPrefsObservers = function preInitPrefsObservers ( ) { gPrefService . QueryInterface ( Ci . nsIPrefBranch ) ; tk . addGlobalPrefListener ( PREF_BRANCH , tk . localPrefsListener ) ; } ; this . preInitListeners . push ( this . preInitPrefsObservers ) ; this . localPrefsListener = function localPrefsListener ( changedPref ) { changedPref = changedPref . substring ( PREF_BRANCH . length ) ; _localPrefListeners . forEach ( ( listeners , prefName ) => { if ( changedPref . substring ( 0 , prefName . length ) !== prefName ) { return ; } listeners . forEach ( function ( listener ) { listener ( changedPref ) ; } ) ; } ) ; } ; this . addGlobalPrefListener = function addGlobalPrefListener ( prefString , prefListener ) { let the_global_pref_observer = _globalPrefObservers . get ( prefString ) ; if ( ! the_global_pref_observer ) { the_global_pref_observer = { listeners : [ ] as Array < LocalPrefListenerFunc > , observe : function ( aSubject , aTopic , aData ) { if ( aTopic !== "nsPref:changed" ) return ; this . listeners . forEach ( function ( listener ) { listener ( aData ) ; } ) ; } , } as GlobalPrefObserver ; gPrefService . addObserver ( prefString , the_global_pref_observer , false ) ; window . addEventListener ( "unload" , function ( ) { gPrefService . removeObserver ( prefString , the_global_pref_observer ) ; } , false ) ; } the_global_pref_observer . listeners . push ( prefListener ) ; _globalPrefObservers . set ( prefString , the_global_pref_observer ) ; } ; this . addPrefListener = function addPrefListener ( prefName , listener ) : void { let the_local_pref_listeners = _localPrefListeners . get ( prefName ) ; if ( ! the_local_pref_listeners ) { the_local_pref_listeners = [ ] ; } the_local_pref_listeners . push ( listener ) ; _localPrefListeners . set ( prefName , the_local_pref_listeners ) ; } ; const TK_DATA_KEY = "tabkit_data" ; this . TKData = { } ; this . TKData . getDataWithKey = function ( obj , key ) { if ( obj === null || typeof obj === "undefined" ) { tk . dump ( "obj is blank" ) ; return { status : "failed" , data : null } ; } if ( typeof key !== "string" ) { tk . dump ( "key is NOT a string" ) ; return { status : "failed" , data : null } ; } if ( ! ( TK_DATA_KEY in obj ) ) { return { status : "success" , data : null } ; } const tk_data = obj [ TK_DATA_KEY ] ; if ( typeof tk_data !== "object" ) { tk . dump ( "obj." + TK_DATA_KEY + " is NOT an object" ) ; return { status : "failed" , data : null } ; } if ( ! ( key in tk_data ) ) { return { status : "success" , data : null } ; } return { status : "success" , data : tk_data [ key ] } ; } ; this . TKData . setDataWithKey = function ( obj , key , data ) { if ( obj === null || typeof obj === "undefined" ) { tk . dump ( "obj is blank" ) ; return { status : "failed" } ; } if ( typeof key !== "string" ) { tk . dump ( "key is NOT a string" ) ; return { status : "failed" } ; } if ( ! ( TK_DATA_KEY in obj ) ) { obj [ TK_DATA_KEY ] = { } ; } const tk_data = obj [ TK_DATA_KEY ] ; if ( typeof tk_data !== "object" ) { tk . dump ( "obj." + TK_DATA_KEY + " is NOT an object" ) ; tk . debug ( "Resetting obj." + TK_DATA_KEY + " to an object" ) ; obj [ TK_DATA_KEY ] = { } ; return { status : "failed" } ; } tk_data [ key ] = data ; return { status : "success" } ; } ; this . TKData . removeDataWithKey = function ( obj , key ) { if ( obj === null || typeof obj === "undefined" ) { tk . dump ( "obj is blank" ) ; return { status : "failed" , data : null } ; } if ( typeof key !== "string" ) { tk . dump ( "key is NOT a string" ) ; return { status : "failed" , data : null } ; } if ( ! ( TK_DATA_KEY in obj ) ) { return { status : "success" , data : null } ; } const tk_data = obj [ TK_DATA_KEY ] ; if ( typeof tk_data !== "object" ) { tk . dump ( "obj." + TK_DATA_KEY + " is NOT an object" ) ; tk . debug ( "Resetting obj." + TK_DATA_KEY + " to an object" ) ; obj [ TK_DATA_KEY ] = { } ; return { status : "failed" , data : null } ; } return { status : "success" , data : tk_data [ key ] , } ; } ; this . mapPrefToAttribute = function mapPrefToAttribute ( prefName , test , node , attributeName ) { const listener = function ( ) { const value = test ( prefName ) ; if ( value != null ) { node . setAttribute ( attributeName , value ) ; } else { node . removeAttribute ( attributeName ) ; } } ; tk . addPrefListener ( prefName , listener ) ; listener ( ) ; } ; this . mapBoolPrefToAttribute = function mapBoolPrefToAttribute ( prefName , node , attributeName ) { tk . mapPrefToAttribute ( prefName , function ( ) { return _prefs . getBoolPref ( prefName ) ? "true" : null ; } , node , attributeName ) ; } ; this . Sorts = { creation : "tabid" , lastLoaded : "lastLoadedKey" , lastViewed : "lastViewedKey" , origin : "possibleparent" , title : "label" , uri : "uriKey" , } ; this . Groupings = { none : "" , opener : "openerGroup" , domain : "uriGroup" , } ; this . RelativePositions = { left : 1 , right : 2 , rightOfRecent : 3 , rightOfConsecutive : 4 , } ; this . ReverseSorts = new Map ( ) ; this . NumericSorts = new Map ( ) ; this . NumericSorts . set ( "lastLoaded" , true ) ; this . NumericSorts . set ( "lastViewed" , true ) ; this . __defineGetter__ ( "activeSort" , function __get_activeSort ( ) { let sortName = _prefs . getCharPref ( "lastActiveSort" ) ; if ( ! ( sortName in tk . Sorts ) ) { sortName = "creation" ; } return sortName ; } ) ; this . __defineSetter__ ( "activeSort" , function __set_activeSort ( sortName ) { if ( sortName in tk . Sorts ) { _prefs . setCharPref ( "lastActiveSort" , sortName ) ; } else { tk . dump ( "activeSort - invalid sort name: " + sortName ) ; } return sortName ; } ) ; this . __defineGetter__ ( "activeGrouping" , function __get_activeGrouping ( ) { let groupingName = _prefs . getCharPref ( "lastActiveGrouping" ) ; if ( ! ( groupingName in tk . Groupings ) ) { groupingName = "none" ; } return groupingName ; } ) ; this . __defineSetter__ ( "activeGrouping" , function __set_activeGrouping ( groupingName ) { if ( groupingName in tk . Groupings ) { _prefs . setCharPref ( "lastActiveGrouping" , groupingName ) ; } else { tk . dump ( "activeGrouping - invalid grouping name: " + groupingName ) ; } return groupingName ; } ) ; this . __defineGetter__ ( "openRelativePosition" , function __get_openRelativePosition ( ) { let positionName = _prefs . getCharPref ( "openRelativePosition" ) ; if ( ! ( positionName in tk . RelativePositions ) ) positionName = "rightOfRecent" ; return positionName ; } ) ; this . __defineSetter__ ( "openRelativePosition" , function __set_openRelativePosition ( positionName ) { if ( positionName in tk . RelativePositions ) { _prefs . setCharPref ( "openRelativePosition" , positionName ) ; } else { tk . dump ( "openRelativePosition - invalid position name: " + positionName ) ; } return positionName ; } ) ; this . __defineGetter__ ( "newTabPosition" , function __get_newTabPosition ( ) { const position = _prefs . getIntPref ( "newTabPosition" ) ; if ( position >= 0 && position <= 2 ) { return position ; } else { tk . log ( "newTabPosition - invalid pref value: " + position ) ; return 0 ; } } ) ; this . __defineSetter__ ( "newTabPosition" , function __set_newTabPosition ( position ) { if ( position >= 0 && position <= 2 ) { _prefs . setIntPref ( "newTabPosition" , position ) ; } else { tk . dump ( "newTabPosition - invalid position: " + position ) ; return position ; } } ) ; this . __defineGetter__ ( "autoGroupNewTabs" , function __get_autoGroupNewTabs ( ) { return _prefs . getBoolPref ( "autoGroupNewTabs" ) ; } ) ; this . __defineSetter__ ( "autoGroupNewTabs" , function __set_autoGroupNewTabs ( bool ) { _prefs . setBoolPref ( "autoGroupNewTabs" , bool ) ; return bool ; } ) ; this . initSortingAndGrouping = function initSortingAndGrouping ( ) { tk . detectTheme ( ) ; _tabContainer . addEventListener ( "TabOpen" , tk . sortgroup_onTabAdded , false ) ; _tabContainer . addEventListener ( "TabSelect" , tk . sortgroup_onTabSelect , true ) ; gBrowser . addEventListener ( "DOMContentLoaded" , tk . sortgroup_onTabLoading , true ) ; gBrowser . addEventListener ( "load" , tk . sortgroup_onTabLoaded , true ) ; document . addEventListener ( "SSTabRestoring" , tk . sortgroup_onSSTabRestoring , false ) ; _tabContainer . addEventListener ( "TabMove" , tk . sortgroup_onTabMoved , false ) ; _tabContainer . addEventListener ( "TabClose" , tk . sortgroup_onTabRemoved , false ) ; gBrowser . tabContainer . addEventListener ( "mousedown" , tk . sortgroup_onTabMousedown , true ) ; gBrowser . tabContainer . addEventListener ( "click" , tk . sortgroup_onClickTab , true ) ; gBrowser . tabContainer . addEventListener ( "dblclick" , tk . sortgroup_onDblclickTab , true ) ; const el_menupopup_tabkit_sortgroup = document . getElementById ( "menupopup_tabkit-sortgroup" ) ; if ( el_menupopup_tabkit_sortgroup != null ) { el_menupopup_tabkit_sortgroup . addEventListener ( "popupshowing" , tk . updateSortGroupMenu , true ) ; } tk . addPrefListener ( "forceThemeCompatibility" , tk . detectTheme ) ; tk . addPrefListener ( "colorTabNotLabel" , tk . detectTheme ) ; tk . addPrefListener ( "disableTabGroupColor" , tk . detectTheme ) ; tk . addPrefListener ( "minSaturation" , tk . regenSaturationLightness ) ; tk . addPrefListener ( "maxSaturation" , tk . regenSaturationLightness ) ; tk . addPrefListener ( "minLightness" , tk . regenSaturationLightness ) ; tk . addPrefListener ( "maxLightness" , tk . regenSaturationLightness ) ; tk . addPrefListener ( "indentedTree" , tk . toggleIndentedTree ) ; tk . addPrefListener ( "maxTreeLevel" , tk . updateIndents ) ; tk . addPrefListener ( "indentAmount" , tk . updateIndents ) ; tk . addPrefListener ( "autoCollapse" , tk . updateAutoCollapse ) ; tk . mapPrefToAttribute ( "colorTabNotLabel" , function ( ) { return _prefs . getBoolPref ( "colorTabNotLabel" ) ? null : "true" ; } , gBrowser . tabContainer , "tabkit-color-label-not-tab" ) ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { tk . sortgroup_onTabAdded ( { target : _tabs [ i ] , fromInitSortingAndGrouping : true } ) ; _tabs [ i ] . setAttribute ( tk . Sorts . lastLoaded , Date . now ( ) . toString ( ) ) ; } window . setTimeout ( function __selectInitial ( ) { tk . sortgroup_onTabSelect ( { target : gBrowser . selectedTab } ) ; if ( ! gBrowser . selectedTab . hasAttribute ( "groupid" ) ) tk . updateAutoCollapse ( ) ; } , 0 ) ; const tabContextMenu = gBrowser . tabContextMenu ; tabContextMenu . insertBefore ( document . getElementById ( "menu_tabkit-sortgroup" ) , tabContextMenu . childNodes [ 1 ] ) ; ( function ( ) { "use strict" ; if ( ! ( "faviconize" in window ) || ! ( "quickFav" in window . faviconize ) || typeof window . faviconize . quickFav . dblclick !== "function" ) { tk . debug ( "faviconize.quickFav.dblclick doesn't exists, replacing function failed" ) ; return ; } const old_func = window . faviconize . quickFav . dblclick ; window . faviconize . quickFav . dblclick = function ( e ) { "use strict" ; let result ; tk . debug ( ">>> faviconize.quickFav.dblclick >>>" ) ; const tab = e . target as Tab ; if ( tab . hasAttribute ( "groupid" ) && tk . localPrefService . getBoolPref ( "doubleClickCollapseExpand" ) ) { tk . debug ( "faviconize cancelled" ) ; return ; } else { result = old_func . apply ( this , arguments ) ; } tk . debug ( "<<< faviconize.quickFav.dblclick <<<" ) ; return result ; } ; } ) ( ) ; } ; this . initListeners . push ( this . initSortingAndGrouping ) ; this . getTabAttributesForTabKit = function ( tab ) { if ( typeof tab !== "object" || typeof tab . getAttribute !== "function" ) { return ; } const data = { } as { [ key ] : string ; } ; const attr_names = [ "tabid" , "possibleparent" , "outoforder" , "groupid" , "singletonid" , "groupcollapsed" , ] ; attr_names . forEach ( function ( attr_name ) { if ( tab . hasAttribute ( attr_name ) ) { data [ attr_name ] = tab . getAttribute ( attr_name ) ; } } ) ; for ( const key in tk . Sorts ) { const attr_name = tk . Sorts [ key ] ; if ( tk . endsWith ( attr_name , "Key" ) ) { data [ attr_name ] = tab . getAttribute ( attr_name ) ; } } for ( const key in tk . Groupings ) { const attr_name = tk . Groupings [ key ] ; if ( tk . endsWith ( attr_name , "Group" ) ) { data [ attr_name ] = tab . getAttribute ( attr_name ) ; } } return data ; } ; this . setTabAttributesForTabKit = function ( tab , tab_attributes ) { if ( typeof tab !== "object" || typeof tab . setAttribute !== "function" ) { return ; } if ( typeof tab_attributes !== "object" ) { return ; } const attr_names = [ "tabid" , "possibleparent" , "outoforder" , "groupid" , "singletonid" , "groupcollapsed" , ] ; attr_names . forEach ( function ( attr_name ) { if ( attr_name in tab_attributes ) { tab . setAttribute ( attr_name , tab_attributes [ attr_name ] ) ; } } ) ; for ( const key in tk . Sorts ) { const attr_name = tk . Sorts [ key ] ; if ( tk . endsWith ( attr_name , "Key" ) ) { if ( attr_name in tab_attributes ) { tab . setAttribute ( attr_name , tab_attributes [ attr_name ] ) ; } } } for ( const key in tk . Groupings ) { const attr_name = tk . Groupings [ key ] ; if ( tk . endsWith ( attr_name , "Group" ) ) { if ( attr_name in tab_attributes ) { tab . setAttribute ( attr_name , tab_attributes [ attr_name ] ) ; } } } } ; this . postInitSortingAndGrouping = function postInitSortingAndGrouping ( ) { if ( _ss ) { _ss . persistTabAttribute ( "tabid" ) ; _ss . persistTabAttribute ( "possibleparent" ) ; _ss . persistTabAttribute ( "outoforder" ) ; for ( const key in tk . Sorts ) { const attr_name = tk . Sorts [ key ] ; if ( tk . endsWith ( attr_name , "Key" ) ) _ss . persistTabAttribute ( attr_name ) ; } _ss . persistTabAttribute ( "groupid" ) ; _ss . persistTabAttribute ( "singletonid" ) ; _ss . persistTabAttribute ( "groupcollapsed" ) ; for ( const key in tk . Groupings ) { const attr_name = tk . Groupings [ key ] ; if ( tk . endsWith ( attr_name , "Group" ) ) _ss . persistTabAttribute ( attr_name ) ; } } ( function init_BrowserSearch_loadSearchFromContext ( ) { "use strict" ; if ( ! ( "loadSearchFromContext" in BrowserSearch ) || typeof BrowserSearch . loadSearchFromContext !== "function" ) { tk . debug ( "BrowserSearch.loadSearchFromContext doesn't exists, replacing function failed" ) ; return ; } const old_func = BrowserSearch . loadSearchFromContext ; BrowserSearch . loadSearchFromContext = function ( _terms ) { "use strict" ; let result ; tk . debug ( ">>> BrowserSearch.loadSearchFromContext >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "unrelated" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "unrelated" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< BrowserSearch.loadSearchFromContext <<<" ) ; return result ; } ; } ) ( ) ; ( function init_BrowserSearch_loadAddEngines ( ) { "use strict" ; if ( typeof BrowserSearch !== "object" || typeof BrowserSearch . loadAddEngines !== "function" ) { tk . debug ( "BrowserSearch.loadAddEngines doesn't exists, replacing function failed" ) ; return ; } const old_func = BrowserSearch . loadAddEngines ; BrowserSearch . loadAddEngines = function ( ) { "use strict" ; let result ; tk . debug ( ">>> BrowserSearch.loadAddEngines >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "unrelated" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "unrelated" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< BrowserSearch.loadAddEngines <<<" ) ; return result ; } ; } ) ( ) ; ( function init_gotoHistoryIndex ( ) { "use strict" ; if ( ! ( "gotoHistoryIndex" in window ) || typeof window . gotoHistoryIndex !== "function" ) { tk . debug ( "window.gotoHistoryIndex doesn't exists, replacing function failed" ) ; return ; } const old_func = window . gotoHistoryIndex ; window . gotoHistoryIndex = function ( _aEvent ) { "use strict" ; let result ; tk . debug ( ">>> window.gotoHistoryIndex >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< window.gotoHistoryIndex <<<" ) ; return result ; } ; } ) ( ) ; ( function init_BrowserBack ( ) { "use strict" ; if ( ! ( "BrowserBack" in window ) || typeof window . BrowserBack !== "function" ) { tk . debug ( "window.BrowserBack doesn't exists, replacing function failed" ) ; return ; } const old_func = window . BrowserBack ; window . BrowserBack = function ( _aEvent ) { "use strict" ; let result ; tk . debug ( ">>> window.BrowserBack >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< window.BrowserBack <<<" ) ; return result ; } ; } ) ( ) ; ( function init_BrowserForward ( ) { "use strict" ; if ( ! ( "BrowserForward" in window ) || typeof window . BrowserForward !== "function" ) { tk . debug ( "window.BrowserForward doesn't exists, replacing function failed" ) ; return ; } const old_func = window . BrowserForward ; window . BrowserForward = function ( _aEvent ) { "use strict" ; let result ; tk . debug ( ">>> window.BrowserForward >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< window.BrowserForward <<<" ) ; return result ; } ; } ) ( ) ; ( function init_BrowserReloadOrDuplicate ( ) { "use strict" ; if ( ! ( "BrowserReloadOrDuplicate" in window ) || typeof window . BrowserReloadOrDuplicate !== "function" ) { tk . debug ( "window.BrowserReloadOrDuplicate doesn't exists, replacing function failed" ) ; return ; } const old_func = window . BrowserReloadOrDuplicate ; window . BrowserReloadOrDuplicate = function ( _aEvent ) { "use strict" ; let result ; tk . debug ( ">>> window.BrowserReloadOrDuplicate >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< window.BrowserReloadOrDuplicate <<<" ) ; return result ; } ; } ) ( ) ; ( function init_BrowserSearch_loadSearch ( ) { "use strict" ; if ( ! ( "loadSearch" in BrowserSearch ) || typeof BrowserSearch . loadSearch !== "function" ) { tk . debug ( "BrowserSearch.loadSearch doesn't exists, replacing function failed" ) ; return ; } const old_func = BrowserSearch . loadSearch ; BrowserSearch . loadSearch = function ( searchText , useNewTab , purpose ) { "use strict" ; let result ; tk . debug ( ">>> BrowserSearch.loadSearch >>>" ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; try { result = old_func . apply ( this , [ searchText , useNewTab , purpose ] ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , parent_tab : selected_tab_before_operation , } ) ; } tk . debug ( "<<< BrowserSearch.loadSearch <<<" ) ; return result ; } ; } ) ( ) ; ( function init_searchbar_handleSearchCommand ( ) { "use strict" ; const searchbar = BrowserSearch . searchBar ; if ( typeof searchbar !== "object" || searchbar === null || ! ( "handleSearchCommand" in searchbar ) || typeof searchbar . handleSearchCommand !== "function" ) { tk . debug ( "searchbar.handleSearchCommand doesn't exists, replacing function failed" ) ; return ; } const old_func = searchbar . handleSearchCommand ; searchbar . handleSearchCommand = function ( aEvent , aEngine , aForceNewTab ) { "use strict" ; let result ; tk . debug ( ">>> searchbar.handleSearchCommand >>>" ) ; tk . addingTab ( { added_tab_type : "unrelated" , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , [ aEvent , aEngine , aForceNewTab ] ) ; } finally { tk . addingTabOver ( { added_tab_type : "unrelated" , } ) ; } tk . debug ( "<<< searchbar.handleSearchCommand <<<" ) ; return result ; } ; } ) ( ) ; ( function init_window_middleMousePaste ( ) { "use strict" ; if ( ! ( "middleMousePaste" in window ) || typeof window . middleMousePaste !== "function" ) { tk . debug ( "window.middleMousePaste doesn't exists, replacing function failed" ) ; return ; } const old_func = window . middleMousePaste ; window . middleMousePaste = function ( _event ) { "use strict" ; let result ; tk . debug ( ">>> window.middleMousePaste >>>" ) ; tk . addingTab ( { added_tab_type : "newtab" , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "newtab" , } ) ; } tk . debug ( "<<< window.middleMousePaste <<<" ) ; return result ; } ; } ) ( ) ; ( function init_window_newTabButtonObserver_onDrop ( ) { "use strict" ; if ( ! ( "newTabButtonObserver" in window ) || typeof window . newTabButtonObserver !== "object" || ! ( "onDrop" in window . newTabButtonObserver ) || typeof window . newTabButtonObserver . onDrop !== "function" ) { tk . debug ( "window.newTabButtonObserver.onDrop doesn't exists, replacing function failed" ) ; return ; } const old_func = window . newTabButtonObserver . onDrop ; window . newTabButtonObserver . onDrop = function ( _event ) { "use strict" ; let result ; tk . debug ( ">>> window.newTabButtonObserver.onDrop >>>" ) ; tk . addingTab ( { added_tab_type : "newtab" , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "newtab" , } ) ; } tk . debug ( "<<< window.newTabButtonObserver.onDrop <<<" ) ; return result ; } ; } ) ( ) ; ( function init_window_BrowserOpenTab ( ) { "use strict" ; if ( ! ( "BrowserOpenTab" in window ) || typeof window . BrowserOpenTab !== "function" ) { tk . debug ( "window.BrowserOpenTab doesn't exists, replacing function failed" ) ; return ; } const old_func = window . BrowserOpenTab ; window . BrowserOpenTab = function ( optional_options ) { "use strict" ; let result ; let added_tab_type = "newtab" ; if ( typeof optional_options === "object" && "tab_kit_options" in optional_options && typeof optional_options . tab_kit_options === "object" && "added_tab_type" in optional_options . tab_kit_options && typeof optional_options . tab_kit_options . added_tab_type === "string" ) { added_tab_type = optional_options . tab_kit_options . added_tab_type ; } let parent_tab = gBrowser . selectedTab ; if ( typeof optional_options === "object" && "tab_kit_options" in optional_options && typeof optional_options . tab_kit_options === "object" && "parent_tab" in optional_options . tab_kit_options && typeof optional_options . tab_kit_options . parent_tab !== "undefined" ) { parent_tab = optional_options . tab_kit_options . parent_tab ; } tk . debug ( ">>> window.BrowserOpenTab >>>" ) ; tk . addingTab ( { added_tab_type : added_tab_type , parent_tab : parent_tab , } ) ; try { result = old_func . apply ( this , [ ] ) ; } finally { tk . addingTabOver ( { added_tab_type : added_tab_type , parent_tab : parent_tab , } ) ; } tk . debug ( "<<< window.BrowserOpenTab <<<" ) ; return result ; } ; } ) ( ) ; ( function init_window_delayedOpenTab ( ) { "use strict" ; if ( ! ( "delayedOpenTab" in window ) || typeof window . delayedOpenTab !== "function" ) { tk . debug ( "window.delayedOpenTab doesn't exists, replacing function failed" ) ; return ; } const old_func = window . delayedOpenTab ; window . delayedOpenTab = function ( aUrl , aReferrer , aCharset , aPostData , aAllowThirdPartyFixup ) { "use strict" ; let result ; tk . debug ( ">>> window.delayedOpenTab >>>" ) ; tk . addingTab ( { added_tab_type : "newtab" , } ) ; try { result = old_func . apply ( this , [ aUrl , aReferrer , aCharset , aPostData , aAllowThirdPartyFixup ] ) ; } finally { tk . addingTabOver ( { added_tab_type : "newtab" , } ) ; } tk . debug ( "<<< window.delayedOpenTab <<<" ) ; return result ; } ; } ) ( ) ; ( function init_window_gURLBar_handleCommand ( ) { "use strict" ; if ( ! ( "gURLBar" in window ) || typeof window . gURLBar !== "object" || ! ( "handleCommand" in window . gURLBar ) || typeof window . gURLBar . handleCommand !== "function" ) { tk . debug ( "window.gURLBar.handleCommand doesn't exists, replacing function failed" ) ; return ; } const old_func = window . gURLBar . handleCommand ; window . gURLBar . handleCommand = function ( aTriggeringEvent ) { "use strict" ; let result ; let finalTriggeringEvent = aTriggeringEvent ; tk . debug ( ">>> window.gURLBar.handleCommand >>>" ) ; tk . addingTab ( { added_tab_type : "newtab" , } ) ; try { if ( _prefs . getBoolPref ( "openTabsFrom.addressBar" ) ) { if ( aTriggeringEvent instanceof KeyboardEvent ) { finalTriggeringEvent = new KeyboardEvent ( aTriggeringEvent . type , { key : aTriggeringEvent . key , code : aTriggeringEvent . code , location : aTriggeringEvent . location , ctrlKey : aTriggeringEvent . ctrlKey , shiftKey : aTriggeringEvent . shiftKey , altKey : ! aTriggeringEvent . altKey , metaKey : aTriggeringEvent . metaKey , repeat : aTriggeringEvent . repeat , isComposing : aTriggeringEvent . isComposing , charCode : aTriggeringEvent . charCode , keyCode : aTriggeringEvent . keyCode , which : aTriggeringEvent . which , detail : aTriggeringEvent . detail , view : aTriggeringEvent . view , bubbles : aTriggeringEvent . bubbles , cancelable : aTriggeringEvent . cancelable , } ) ; } else if ( aTriggeringEvent instanceof MouseEvent ) { finalTriggeringEvent = new MouseEvent ( aTriggeringEvent . type , { screenX : aTriggeringEvent . screenX , screenY : aTriggeringEvent . screenY , clientX : aTriggeringEvent . clientX , clientY : aTriggeringEvent . clientY , ctrlKey : aTriggeringEvent . ctrlKey , shiftKey : aTriggeringEvent . shiftKey , altKey : ! aTriggeringEvent . altKey , metaKey : aTriggeringEvent . metaKey , button : aTriggeringEvent . button , buttons : aTriggeringEvent . buttons , relatedTarget : aTriggeringEvent . relatedTarget , detail : aTriggeringEvent . detail , view : aTriggeringEvent . view , bubbles : aTriggeringEvent . bubbles , cancelable : aTriggeringEvent . cancelable , } ) ; } } result = old_func . apply ( this , [ finalTriggeringEvent ] ) ; } finally { tk . addingTabOver ( { added_tab_type : "newtab" , } ) ; } tk . debug ( "<<< window.gURLBar.handleCommand <<<" ) ; return result ; } ; } ) ( ) ; ( function init_window_gBrowser__endRemoveTab ( ) { "use strict" ; if ( ! ( "gBrowser" in window ) || typeof window . gBrowser !== "object" || ! ( "_endRemoveTab" in window . gBrowser ) || typeof window . gBrowser . _endRemoveTab !== "function" ) { tk . debug ( "window.gBrowser._endRemoveTab doesn't exists, replacing function failed" ) ; return ; } const old_func = window . gBrowser . _endRemoveTab ; window . gBrowser . _endRemoveTab = function ( _aTab ) { "use strict" ; let result ; tk . debug ( ">>> window.gBrowser._endRemoveTab >>>" ) ; tk . addingTab ( { added_tab_type : "newtab" , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "newtab" , } ) ; } tk . debug ( "<<< window.gBrowser._endRemoveTab <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "nsContextMenu" in window ) || typeof window . nsContextMenu !== "function" || typeof window . nsContextMenu . prototype . openLinkInTab !== "function" ) { tk . debug ( "window.nsContextMenu.prototype.openLinkInTab doesn't exists, replacing function failed" ) ; return ; } const old_func = window . nsContextMenu . prototype . openLinkInTab ; window . nsContextMenu . prototype . openLinkInTab = function ( ) { "use strict" ; let result ; tk . debug ( ">>> window.nsContextMenu.prototype.openLinkInTab >>>" ) ; tk . addingTab ( { added_tab_type : "related" , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , } ) ; } tk . debug ( "<<< window.nsContextMenu.prototype.openLinkInTab <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "openLinkIn" in window ) || typeof window . openLinkIn !== "function" ) { tk . debug ( "window.openLinkIn doesn't exists, replacing function failed" ) ; return ; } const old_func = window . openLinkIn ; window . openLinkIn = function ( url , where , _params ) { "use strict" ; let result ; tk . debug ( ">>> window.openLinkIn >>>" ) ; if ( where === "tab" ) { tk . addingTab ( { added_tab_type : "related" , parent_tab : gBrowser . selectedTab , } ) ; } try { result = old_func . apply ( this , [ url , where , _params ] ) ; } finally { if ( where === "tab" ) { tk . addingTabOver ( { added_tab_type : "related" , } ) ; } } tk . debug ( "<<< window.openLinkIn <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "nsContextMenu" in window ) || typeof window . nsContextMenu !== "function" || typeof window . nsContextMenu . prototype . openFrameInTab !== "function" ) { tk . debug ( "window.nsContextMenu.prototype.openFrameInTab doesn't exists, replacing function failed" ) ; return ; } const old_func = window . nsContextMenu . prototype . openFrameInTab ; window . nsContextMenu . prototype . openFrameInTab = function ( ) { "use strict" ; let result ; tk . debug ( ">>> window.nsContextMenu.prototype.openFrameInTab >>>" ) ; tk . addingTab ( { added_tab_type : "related" , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , } ) ; } tk . debug ( "<<< window.nsContextMenu.prototype.openFrameInTab <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "nsContextMenu" in window ) || typeof window . nsContextMenu !== "function" || typeof window . nsContextMenu . prototype . viewBGImage !== "function" ) { tk . debug ( "window.nsContextMenu.prototype.viewBGImage doesn't exists, replacing function failed" ) ; return ; } const old_func = window . nsContextMenu . prototype . viewBGImage ; window . nsContextMenu . prototype . viewBGImage = function ( _e ) { "use strict" ; let result ; tk . debug ( ">>> window.nsContextMenu.prototype.viewBGImage >>>" ) ; tk . addingTab ( { added_tab_type : "related" , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , } ) ; } tk . debug ( "<<< window.nsContextMenu.prototype.viewBGImage <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "nsContextMenu" in window ) || typeof window . nsContextMenu !== "function" || typeof window . nsContextMenu . prototype . addDictionaries !== "function" ) { tk . debug ( "window.nsContextMenu.prototype.addDictionaries doesn't exists, replacing function failed" ) ; return ; } const old_func = window . nsContextMenu . prototype . addDictionaries ; window . nsContextMenu . prototype . addDictionaries = function ( ) { "use strict" ; let result ; tk . debug ( ">>> window.nsContextMenu.prototype.addDictionaries >>>" ) ; tk . addingTab ( { added_tab_type : "related" , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , } ) ; } tk . debug ( "<<< window.nsContextMenu.prototype.addDictionaries <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "nsContextMenu" in window ) || typeof window . nsContextMenu !== "function" || typeof window . nsContextMenu . prototype . viewMedia !== "function" ) { tk . debug ( "window.nsContextMenu.prototype.viewMedia doesn't exists, replacing function failed" ) ; return ; } const old_func = window . nsContextMenu . prototype . viewMedia ; window . nsContextMenu . prototype . viewMedia = function ( _e ) { "use strict" ; let result ; tk . debug ( ">>> window.nsContextMenu.prototype.viewMedia >>>" ) ; tk . addingTab ( { added_tab_type : "related" , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , arguments ) ; } finally { tk . addingTabOver ( { added_tab_type : "related" , } ) ; } tk . debug ( "<<< window.nsContextMenu.prototype.viewMedia <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "nsBrowserAccess" in window ) || typeof window . nsBrowserAccess !== "function" || typeof window . nsBrowserAccess . prototype . openURI !== "function" ) { tk . debug ( "window.nsBrowserAccess.prototype.openURI doesn't exists, replacing function failed" ) ; return ; } const old_func = window . nsBrowserAccess . prototype . openURI ; window . nsBrowserAccess . prototype . openURI = function ( aURI , aOpener , aWhere , aContext ) { "use strict" ; let result ; tk . debug ( ">>> window.nsBrowserAccess.prototype.openURI >>>" ) ; const added_tab_type = aContext === Ci . nsIBrowserDOMWindow . OPEN_EXTERNAL ? "unrelated" : "related" ; tk . addingTab ( { added_tab_type : added_tab_type , parent_tab : gBrowser . selectedTab , } ) ; try { result = old_func . apply ( this , [ aURI , aOpener , aWhere , aContext ] ) ; } finally { tk . addingTabOver ( { added_tab_type : added_tab_type , } ) ; } tk . debug ( "<<< window.nsBrowserAccess.prototype.openURI <<<" ) ; return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "loadTabs" in gBrowser ) || typeof gBrowser . loadTabs !== "function" ) { tk . debug ( "gBrowser.loadTabs doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . loadTabs ; gBrowser . loadTabs = function ( aURIs , aLoadInBackground , aReplace ) { "use strict" ; let result ; tk . addingTabs ( { first_tab : aReplace ? gBrowser . selectedTab : null , } ) ; try { result = old_func . apply ( this , [ aURIs , aLoadInBackground , aReplace ] ) ; } finally { tk . addingTabsOver ( ) ; } return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( typeof gBrowser . tabContainer !== "object" || typeof gBrowser . tabContainer . _animateTabMove !== "function" ) { tk . debug ( "gBrowser.tabContainer._animateTabMove doesn't exists, replacing function failed" ) ; return ; } gBrowser . tabContainer . _animateTabMove = function ( ) { "use strict" ; this . _handleTabSelect ( ) ; } ; } ) ( ) ; } ; this . postInitListeners . push ( this . postInitSortingAndGrouping ) ; this . nextType = null ; this . isBookmarkGroup = false ; this . nextParent = null ; this . lastParent = null ; this . dontMoveNextTab = false ; this . ignoreOvers = 0 ; this . addedTabs = [ ] ; this . preInitSortingAndGroupingMethodHooks = function preInitSortingAndGroupingMethodHooks ( ) { ( function ( ) { if ( typeof gBrowser . addTab !== "function" ) { tk . debug ( "gBrowser.addTab doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . addTab ; gBrowser . addTab = function ( _aURI , _aReferrerURI , _aCharset , _aPostData , _aOwner , _aAllowThirdPartyFixup ) { let aSkipAnimation = false ; if ( arguments . length === 2 && typeof _aReferrerURI === "object" && ( "skipAnimation" in _aReferrerURI ) ) { aSkipAnimation = _aReferrerURI . skipAnimation ; } if ( aSkipAnimation && ! tk . isBookmarkGroup ) { tk . addingTab ( { added_tab_type : "sessionrestore" , should_keep_added_tab_position : true , } ) ; } const result = old_func . apply ( this , arguments ) ; const tab = result ; if ( aSkipAnimation && ! tk . isBookmarkGroup && tab != null ) { tk . addingTabOver ( { added_tab : tab , added_tab_type : "sessionrestore" , should_keep_added_tab_position : true , } ) ; } return result ; } ; } ) ( ) ; const reportPhishing = document . getElementById ( "menu_HelpPopup_reportPhishingtoolmenu" ) ; if ( typeof reportPhishing === "object" && reportPhishing != null && "setAttribute" in reportPhishing ) { const original_command_str = reportPhishing . getAttribute ( "oncommand" ) ; reportPhishing . setAttribute ( "oncommand" , "tk.addingTab({ \
            added_tab_type: 'related', \
            parent_tab: gBrowser.selectedTab \
          }) try {" + original_command_str + "} \
          finally { \
            tk.addingTabOver({ \
              added_tab_type: 'related' \
            }) \
          }" ) ; } let goMenu = document . getElementById ( "history-menu" ) ; if ( ! ( typeof goMenu === "object" && goMenu != null && "setAttribute" in goMenu ) ) { goMenu = document . getElementById ( "go-menu" ) ; } if ( typeof goMenu === "object" && goMenu != null && "setAttribute" in goMenu ) { const original_command_str = goMenu . getAttribute ( "oncommand" ) ; goMenu . setAttribute ( "oncommand" , "tk.addingTab({ \
            added_tab_type: 'history', \
            parent_tab: gBrowser.selectedTab \
          }) try {" + original_command_str + "} \
          finally { \
            tk.addingTabOver({ \
              added_tab_type: 'history' \
            }) \
          }" ) ; } } ; this . preInitListeners . push ( this . preInitSortingAndGroupingMethodHooks ) ; this . addingTab = function addingTab ( type_or_options , parent , dontMoveNextTab ) { try { if ( tk . nextType ) { tk . ignoreOvers ++ ; return ; } if ( typeof type_or_options === "object" ) { const options = type_or_options ; if ( ( "added_tab_type" in options ) && options . added_tab_type != null ) { tk . nextType = options . added_tab_type ; } if ( ( "parent_tab" in options ) && typeof options . parent_tab !== "undefined" ) { tk . nextParent = options . parent_tab ; } if ( ( "should_keep_added_tab_position" in options ) && options . should_keep_added_tab_position != null ) { tk . dontMoveNextTab = options . should_keep_added_tab_position ; } } else { tk . nextType = type_or_options ; tk . isBookmarkGroup = false ; tk . nextParent = parent != null ? parent : gBrowser . selectedTab ; tk . dontMoveNextTab = ! ! dontMoveNextTab ; } } catch ( ex ) { tk . dump ( ex ) ; } } ; this . addingTabOver = function addingTabOver ( options ? ) { let added_tabs_from_params_or_global ; let parent_tab_from_params_or_global = null ; let added_tab_type_from_params_or_global ; let should_keep_added_tab_position_from_params_or_global ; if ( typeof options === "object" && ( "added_tab" in options ) && options . added_tab != null ) { added_tabs_from_params_or_global = [ options . added_tab ] ; } else { added_tabs_from_params_or_global = tk . addedTabs ; } if ( typeof options === "object" && ( "added_tab_type" in options ) && options . added_tab_type != null ) { added_tab_type_from_params_or_global = options . added_tab_type ; } else { added_tab_type_from_params_or_global = tk . nextType ; } if ( added_tab_type_from_params_or_global === "sessionrestore" ) { } else { if ( typeof options === "object" && ( "parent_tab" in options ) && options . parent_tab != null ) { parent_tab_from_params_or_global = options . parent_tab ; } else { parent_tab_from_params_or_global = tk . nextParent ; } } if ( typeof options === "object" && ( "should_keep_added_tab_position" in options ) && options . should_keep_added_tab_position != null ) { should_keep_added_tab_position_from_params_or_global = options . should_keep_added_tab_position ; } else { should_keep_added_tab_position_from_params_or_global = tk . dontMoveNextTab ; } try { if ( tk . ignoreOvers > 0 ) { return ; } if ( added_tabs_from_params_or_global . length > 1 ) { tk . dump ( "addingTabsOver: More than one tab was added (" + added_tabs_from_params_or_global . length + " tabs, to be precise)!" ) ; tk . addingTabsOver ( ) ; return ; } if ( added_tabs_from_params_or_global . length === 1 ) { let type = added_tab_type_from_params_or_global ; const parent = parent_tab_from_params_or_global ; const tab = added_tabs_from_params_or_global . pop ( ) ; if ( ! parent || parent !== tk . lastParent ) for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . removeAttribute ( "recentlyadded" ) ; tk . lastParent = tk . nextParent ; if ( type === "sessionrestore" ) { return ; } const pid = parent ? tk . getTabId ( parent ) : null ; if ( pid != null ) { tab . setAttribute ( "possibleparent" , pid ) ; } else if ( type !== "unrelated" ) { tk . dump ( "addingTabOver: no parent for " + ( type || "" ) + " tab" ) ; } if ( type === "bookmark" && _prefs . getBoolPref ( "bookmarkTabsAreRelated" ) ) { type = "related" ; } else if ( type === "history" && _prefs . getBoolPref ( "historyTabsAreRelated" ) ) { type = "related" ; } else if ( type === "newtab" && _prefs . getBoolPref ( "newTabsAreRelated" ) ) { type = "related" ; } else if ( type === "sessionrestore" ) { type = "unrelated" ; } let openerGroup = "" ; let ogAttr = null as any ; if ( type === "related" && pid && parent ) { ogAttr = tk . Groupings . opener ; openerGroup = parent . getAttribute ( ogAttr ) ; if ( openerGroup ) { tab . setAttribute ( ogAttr , openerGroup ) ; } else { openerGroup = ":oG-" + pid ; parent . setAttribute ( ogAttr , openerGroup ) ; tab . setAttribute ( ogAttr , openerGroup ) ; } } let tabNeedsPlacing = ! should_keep_added_tab_position_from_params_or_global ; if ( tk . autoGroupNewTabs ) { if ( ! tabNeedsPlacing && tab . previousSibling && tab . nextSibling && tab . previousSibling . getAttribute ( "groupid" ) && tab . previousSibling . getAttribute ( "groupid" ) === tab . nextSibling . getAttribute ( "groupid" ) ) { if ( type === "unrelated" ) { tk . keepGroupsTogether ( ) ; } else { const gid = tab . previousSibling . getAttribute ( "groupid" ) ; tk . setGID ( tab , gid ) ; tab . setAttribute ( "outoforder" , "true" ) ; } } else if ( tk . activeGrouping === "opener" ) { if ( type === "related" && pid && parent ) { const pgid = parent . getAttribute ( "groupid" ) ; let nearbyTabHasSameParentGroup ; if ( pgid ) { nearbyTabHasSameParentGroup = ( tab . previousSibling && tab . previousSibling . getAttribute ( "groupid" ) === pgid ) || ( tab . nextSibling && tab . nextSibling . getAttribute ( "groupid" ) === pgid ) ; } else { nearbyTabHasSameParentGroup = ( tab . previousSibling && tab . previousSibling === parent ) || ( tab . nextSibling && tab . nextSibling === parent ) ; } if ( tabNeedsPlacing || nearbyTabHasSameParentGroup ) { let grouped = false ; if ( pgid ) { if ( pgid . indexOf ( ":oG-" ) !== - 1 || pgid . indexOf ( ":tmpOG-" ) !== - 1 ) { tk . setGID ( tab , pgid ) ; grouped = true ; } } else if ( ! pgid ) { if ( tk . getGroupById ( openerGroup ) . length !== 0 || tk . getUngroupedTabsByAttr ( ogAttr , openerGroup ) . length !== 2 ) { openerGroup = ":tmpOG-" + pid ; } tk . setGID ( parent , openerGroup ) ; tk . setGID ( tab , openerGroup ) ; grouped = true ; } if ( tabNeedsPlacing && grouped ) { const gid = parent . getAttribute ( "groupid" ) ; const newPos = tk . newTabPosition ; const autoSortOpenerGroups = _prefs . getBoolPref ( "autoSortOpenerGroups" ) ; if ( ( autoSortOpenerGroups && ( tk . countGroups ( gid ) === 1 || tk . activeSort === "origin" ) ) || newPos === 1 || ( newPos === 2 && tk . activeSort === "origin" ) ) { switch ( tk . openRelativePosition ) { case "left" : tk . moveBefore ( tab , parent ) ; break ; case "right" : tk . moveAfter ( tab , parent ) ; break ; default : { let target = parent ; while ( target . nextSibling && target . nextSibling . getAttribute ( "groupid" ) === gid && target . nextSibling . hasAttribute ( "recentlyadded" ) ) target = target . nextSibling ; tk . moveAfter ( tab , target ) ; tab . setAttribute ( "recentlyadded" , "true" ) ; } } tab . setAttribute ( "outoforder" , "true" ) ; } else if ( newPos === 0 ) { let target = parent ; while ( target . nextSibling && target . nextSibling . getAttribute ( "groupid" ) === gid ) target = target . nextSibling ; tk . moveAfter ( tab , target ) ; tab . setAttribute ( "outoforder" , "true" ) ; } else { tk . insertTab ( tab , gid ) ; } tabNeedsPlacing = false ; } } } } else if ( tk . activeGrouping === "domain" ) { let domain = tab . getAttribute ( tk . Groupings . domain ) ; if ( domain ) { let group = tk . getGroupById ( domain ) ; let nearbyTabHasSameDomain ; if ( group . length > 0 ) { nearbyTabHasSameDomain = ( tab . previousSibling && tab . previousSibling . getAttribute ( "groupid" ) . indexOf ( domain ) !== - 1 ) || ( tab . nextSibling && tab . nextSibling . getAttribute ( "groupid" ) . indexOf ( domain ) !== - 1 ) ; } else { nearbyTabHasSameDomain = ( tab . previousSibling && tab . previousSibling . getAttribute ( tk . Groupings . domain ) === domain ) || ( tab . nextSibling && tab . nextSibling . getAttribute ( tk . Groupings . domain ) === domain ) ; } if ( tabNeedsPlacing || nearbyTabHasSameDomain ) { if ( group . length === 0 ) { group = tk . getUngroupedTabsByAttr ( tk . Groupings . domain , domain ) ; if ( group . length === 2 ) for ( let i = 0 ; i < group . length ; i ++ ) tk . setGID ( group [ i ] , domain ) ; else group = [ ] ; } else { domain = group [ 0 ] . getAttribute ( "groupid" ) ; tk . setGID ( tab , domain ) ; } if ( tabNeedsPlacing && group . length > 0 ) { const autoSortDomainGroups = _prefs . getBoolPref ( "autoSortDomainGroups" ) ; if ( autoSortDomainGroups && tk . countGroups ( domain ) === 1 ) { tk . insertTab ( tab , domain , "uri" ) ; } else { let newPos = tk . newTabPosition ; if ( newPos === 2 ) newPos = ( tk . activeSort === "origin" ) ? 1 : ( autoSortDomainGroups ? 0 : newPos ) ; if ( newPos === 1 && ( ! pid || parent && parent . getAttribute ( "groupid" ) . indexOf ( domain ) === - 1 ) ) newPos = 0 ; if ( newPos === 1 ) { switch ( tk . openRelativePosition ) { case "left" : tk . moveBefore ( tab , parent ) ; break ; case "right" : tk . moveAfter ( tab , parent ) ; break ; default : { let target = parent ; if ( target == null ) { break ; } while ( target . nextSibling && target . nextSibling . getAttribute ( "groupid" ) === domain && target . nextSibling . hasAttribute ( "recentlyadded" ) ) target = target . nextSibling ; tk . moveAfter ( tab , target ) ; tab . setAttribute ( "recentlyadded" , "true" ) ; } } tab . setAttribute ( "outoforder" , "true" ) ; } else if ( newPos === 0 && parent ) { const gid = parent . getAttribute ( "groupid" ) ; let target = parent ; while ( target . nextSibling && target . nextSibling . getAttribute ( "groupid" ) === gid ) target = target . nextSibling ; tk . moveAfter ( tab , target ) ; tab . setAttribute ( "outoforder" , "true" ) ; } else { tk . insertTab ( tab , domain ) ; } } tabNeedsPlacing = false ; } } } } } if ( tabNeedsPlacing ) { let newPos = tk . newTabPosition ; if ( newPos === 2 && tk . activeSort === "origin" ) newPos = 1 ; if ( newPos === 1 && ! pid ) newPos = 0 ; switch ( newPos ) { case 1 : { let target = parent ; if ( parent == null || target == null ) { break ; } const pagid = parent . getAttribute ( "groupid" ) ; if ( tk . openRelativePosition === "left" ) { if ( pagid ) while ( target . previousSibling && target . previousSibling . getAttribute ( "groupid" ) === pagid ) target = target . previousSibling ; tk . moveBefore ( tab , target ) ; } else { if ( pagid ) while ( target . nextSibling && target . nextSibling . getAttribute ( "groupid" ) === pagid ) target = target . nextSibling ; while ( tk . openRelativePosition !== "right" && target . nextSibling && ! target . nextSibling . hasAttribute ( "groupid" ) && target . nextSibling . hasAttribute ( "recentlyadded" ) ) target = target . nextSibling ; tk . moveAfter ( tab , target ) ; if ( tk . openRelativePosition !== "right" ) tab . setAttribute ( "recentlyadded" , "true" ) ; } tab . setAttribute ( "outoforder" , "true" ) ; break ; } case 2 : tk . insertTab ( tab ) ; break ; default : tab . setAttribute ( "outoforder" , "true" ) ; } } } } catch ( ex ) { tk . dump ( ex ) ; } finally { if ( tk . ignoreOvers > 0 ) { tk . ignoreOvers -- ; } else { tk . nextType = null ; tk . isBookmarkGroup = false ; tk . nextParent = null ; tk . dontMoveNextTab = false ; } } } ; this . addingTabs = function addingTabs ( options = { } ) { const first_tab = options . first_tab ; try { if ( tk . nextType ) { tk . ignoreOvers ++ ; return ; } if ( first_tab ) { tk . addedTabs = [ first_tab ] ; tk . nextType = "loadOneOrMoreURIs" ; } else { tk . nextType = "loadTabs" ; tk . nextParent = gBrowser . selectedTab ; } } catch ( ex ) { tk . dump ( ex ) ; } } ; this . addingTabsOver = function addingTabsOver ( ) { if ( tk . ignoreOvers > 0 ) { tk . ignoreOvers -- ; return ; } try { if ( tk . addedTabs . length > 1 ) { const firstTab = tk . addedTabs [ 0 ] ; const pid = firstTab . getAttribute ( "possibleparent" ) ; let openerGroup = firstTab . getAttribute ( tk . Groupings . opener ) ; let gid = firstTab . getAttribute ( "groupid" ) ; if ( ! openerGroup ) { openerGroup = ":oG-bookmarkGroup-" + tk . getTabId ( firstTab ) ; firstTab . setAttribute ( tk . Groupings . opener , openerGroup ) ; if ( tk . autoGroupNewTabs && ! gid ) { gid = openerGroup ; tk . setGID ( firstTab , gid ) ; } } else if ( tk . autoGroupNewTabs && ! gid ) { gid = ":oG-bookmarkGroup-" + tk . generateId ( ) ; tk . setGID ( firstTab , gid ) ; } for ( let i = tk . addedTabs . length - 1 ; i >= 1 ; i -- ) { const tab = tk . addedTabs [ i ] ; tk . moveAfter ( tab , firstTab ) ; if ( tk . autoGroupNewTabs ) tk . setGID ( tab , gid ) ; if ( pid ) { tab . setAttribute ( "possibleparent" , pid ) ; tk . updateIndents ( ) ; } tab . setAttribute ( tk . Groupings . opener , openerGroup ) ; } } for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . removeAttribute ( "recentlyadded" ) ; } catch ( ex ) { tk . dump ( ex ) ; } finally { tk . nextType = null ; tk . isBookmarkGroup = false ; tk . nextParent = null ; tk . dontMoveNextTab = false ; tk . addedTabs . length = 0 ; } } ; this . sourceTypes = [ { depth : 5 , name : "goup_up" , type : "related" } , { depth : 4 , name : "diggerLoadURL" , type : "related" } , { depth : 3 , name : "mlb_common_Utils_openUrlInNewTab" , type : "related" } , { depth : 3 , name : "activateLinks" , type : "related" } , { depth : 3 , name : "gotoHistoryIndex" , type : "related" } , { depth : 3 , name : "BrowserBack" , type : "related" } , { depth : 3 , name : "BrowserForward" , type : "related" } , { depth : 3 , name : "BrowserReloadOrDuplicate" , type : "related" } , { depth : 2 , name : "BrowserSearch_search" , type : "related" } , { depth : 3 , name : "handleLinkClick" , type : "related" } , { depth : 1 , name : "webdeveloper_generateDocument" , type : "related" } , { depth : 1 , name : "openSelectedLinks" , type : "related" } , { depth : 5 , name : "BM_onCommand" , type : "newtab" } , { depth : 5 , name : "ondragdrop" , type : "newtab" } , { depth : 4 , name : "middleMousePaste" , type : "newtab" } , { depth : 4 , name : "handleCommand" , type : "newtab" } , { depth : 2 , name : "BrowserOpenTab" , type : "newtab" } , { depth : 2 , name : "delayedOpenTab" , type : "newtab" } , { depth : 2 , name : "handleCommand" , type : "newtab" } , { depth : 1 , name : "_endRemoveTab" , type : "newtab" } , { depth : 4 , name : "openReleaseNotes" , type : "unrelated" } , { depth : 1 , name : "sss_duplicateTab" , type : "sessionrestore" , DontMoveTab : true } , { depth : 1 , name : "sss_undoCloseTab" , type : "sessionrestore" , DontMoveTab : true } , { depth : 1 , name : "sss_restoreWindow" , type : "sessionrestore" , DontMoveTab : true } , ] ; this . sourceTypes . sort ( function __compareSourceDepths ( a , b ) { return b . depth - a . depth ; } ) ; this . sortgroup_onTabAdded = function sortgroup_onTabAdded ( event ) { const tab = event . target as Tab ; const tid = tk . generateNewTabId ( tab ) ; tab . setAttribute ( tk . Sorts . lastViewed , new Date ( ) . setFullYear ( 2030 ) . toString ( ) ) ; tab . setAttribute ( tk . Sorts . lastLoaded , new Date ( ) . setFullYear ( 2030 ) . toString ( ) ) ; tk . setTabUriKey ( tab ) ; if ( tk . nextType ) { tk . addedTabs . push ( tab ) ; if ( tk . nextType === "loadTabs" && tk . addedTabs . length === 1 ) { tk . nextType = tk . isBookmarkGroup ? "bookmark" : "newtab" ; tk . dontMoveNextTab = false ; tk . addingTabOver ( { added_tab : tab , added_tab_type : tk . isBookmarkGroup ? "bookmark" : "newtab" , should_keep_added_tab_position : false , } ) ; tk . addedTabs = [ tab ] ; tk . nextType = "loadTabs" ; } } else if ( ! ( "fromInitSortingAndGrouping" in event ) ) { if ( ! tk . nextType ) { tk . debug ( "No nextType for added tab: " + tid ) ; tk . nextType = "newtab" ; } tk . isBookmarkGroup = false ; tk . addingTabOver ( { parent_tab : gBrowser . selectedTab , added_tab : tab , } ) ; } } ; this . sortgroup_onTabSelect = function sortgroup_onTabSelect ( event ) { const tab = event . target as Tab ; tab . setAttribute ( tk . Sorts . lastViewed , Date . now ( ) . toString ( ) ) ; if ( tk . openRelativePosition === "rightOfRecent" ) for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . removeAttribute ( "recentlyadded" ) ; if ( _prefs . getBoolPref ( "autoCollapse" ) ) { if ( tab . hasAttribute ( "groupid" ) ) tk . updateAutoCollapse ( ) ; } else if ( tab . hidden && tab . hasAttribute ( "groupcollapsed" ) ) { tk . toggleGroupCollapsed ( tab ) ; } } ; this . updateAutoCollapse = function updateAutoCollapse ( group ? ) { if ( ! _prefs . getBoolPref ( "autoCollapse" ) ) return ; if ( ! group || ! ( "length" in group ) ) { const all_groups = tk . getAllGroups ( ) ; for ( const gid in all_groups ) { const g = all_groups [ gid ] ; tk . updateAutoCollapse ( g ) ; } return ; } const gid = gBrowser . selectedTab . getAttribute ( "groupid" ) ; const fixIndents = tk . subtreesEnabled ( ) ; const indent = _prefs . getIntPref ( "indentAmount" ) ; if ( group [ 0 ] . getAttribute ( "groupid" ) === gid ) { group . forEach ( function ( t ) { t . removeAttribute ( "groupcollapsed" ) ; tk . tabSetHidden ( t , false ) ; if ( fixIndents && ( "treeLevel" in t ) ) t . style . setProperty ( "margin-left" , ( indent * t . treeLevel ) + "px" , "important" ) ; } ) ; } else { const visible = [ ] as Tab [ ] ; group . forEach ( function ( t ) { if ( ! t . hidden ) visible . push ( t ) ; if ( fixIndents ) t . style . marginLeft = "" ; } ) ; if ( visible . length === 0 ) { group . sort ( tk . compareTabViewedExceptUnread ) ; tk . tabSetHidden ( group [ group . length - 1 ] , false ) ; } else if ( visible . length > 1 ) { visible . sort ( tk . compareTabViewedExceptUnread ) ; visible . forEach ( function ( t ) { tk . tabSetHidden ( t , true ) ; } ) ; const firstTab = group [ 0 ] ; const targetTab = _prefs . getCharPref ( "collapsedGroupVisibleTab" ) === "selected" ? visible . pop ( ) : firstTab ; tk . tabSetHidden ( targetTab , false ) ; } group . forEach ( function ( t ) { t . setAttribute ( "groupcollapsed" , "true" ) ; } ) ; } } ; this . sortgroup_onTabLoading = function sortgroup_onTabLoading ( event ) { try { const index = gBrowser . getBrowserIndexForDocument ( event . originalTarget ) ; const tab = _tabs [ index ] ; const uriKey = tab . getAttribute ( tk . Sorts . uri ) ; tk . setTabUriKey ( tab ) ; if ( event . originalTarget . nodeName === "#document" && uriKey === "zzzzzzzzzzzzzzz/about/blank" && uriKey !== tab . getAttribute ( tk . Sorts . uri ) && ! tab . hasAttribute ( "groupid" ) && tk . autoGroupNewTabs && tk . activeGrouping === "domain" ) { const pid = tab . getAttribute ( "possibleparent" ) ; tk . nextType = pid ? "pageload" : "unrelated" ; tk . dontMoveNextTab = false ; tk . nextParent = pid ? tk . getTabById ( pid ) : null ; tk . isBookmarkGroup = false ; tk . addedTabs = [ tab ] ; tk . addingTabOver ( ) ; } } catch ( ex ) { } } ; this . sortgroup_onTabLoaded = function sortgroup_onTabLoaded ( event ) { try { if ( event . originalTarget . nodeName === "#document" ) { const index = gBrowser . getBrowserIndexForDocument ( event . originalTarget ) ; const tab = _tabs [ index ] ; tab . setAttribute ( tk . Sorts . lastLoaded , Date . now ( ) . toString ( ) ) ; } } catch ( ex ) { } } ; interface ListerTimeoutPair { listener : ( ) => void ; timeout : number ; } const _sortgroup_onSSTabRestoring_timers = [ ] as ListerTimeoutPair [ ] ; this . sortgroup_onSSTabRestoring = function sortgroup_onSSTabRestoring ( event ) { const tab = event . originalTarget as Tab ; if ( tab . getAttribute ( "selected" ) === "true" ) tab . setAttribute ( tk . Sorts . lastViewed , Date . now ( ) . toString ( ) ) ; _sortgroup_onSSTabRestoring_timers . forEach ( function ( lt ) { window . clearTimeout ( lt . timeout ) ; lt . timeout = window . setTimeout ( function ( lt2 ) { lt2 . listener ( ) ; } , 100 , lt ) ; } ) ; tab . groupNotChecked = true ; const listener = ( function __sortgroup_onTabRestored ( ) { _sortgroup_onSSTabRestoring_timers . shift ( ) ; let gid = tab . getAttribute ( "groupid" ) ; if ( ! gid ) { gid = tab . getAttribute ( "singletonid" ) ; if ( gid ) { tk . setGID ( tab , gid ) ; } } if ( gid ) { const group = tk . getGroupById ( gid , true ) ; if ( group . length === 1 ) { tk . removeGID ( tab , true ) ; } else { let last = null ; let before = true ; for ( let i = 0 ; i < group . length ; i ++ ) { const t = group [ i ] ; if ( t === tab ) { if ( last ) break ; before = false ; } else if ( ! ( "groupNotChecked" in t ) ) { last = t ; if ( ! before ) break ; } } if ( last ) { if ( last . _tPos < tab . _tPos ) { let target = last ; while ( target . nextSibling && target . nextSibling !== tab && target . nextSibling . getAttribute ( "groupid" ) === gid ) { target = target . nextSibling ; } if ( target . nextSibling !== tab ) tk . moveAfter ( tab , last ) ; } else { let target = last ; while ( target . previousSibling && target . previousSibling !== tab && target . previousSibling . getAttribute ( "groupid" ) === gid ) { target = target . previousSibling ; } if ( target . previousSibling !== tab ) tk . moveBefore ( tab , last ) ; } if ( last . hasAttribute ( "groupcollapsed" ) ) { tab . setAttribute ( "groupcollapsed" , "true" ) ; if ( tab . getAttribute ( "selected" ) === "true" ) { group . forEach ( function ( t ) { tk . tabSetHidden ( t , true ) ; } ) ; tk . tabSetHidden ( tab , false ) ; } else { tk . tabSetHidden ( tab , true ) ; } } else { tk . tabSetHidden ( tab , false ) ; } } else { tk . keepGroupsTogether ( ) ; tk . tabSetHidden ( tab , false ) ; } } } if ( tab . hasAttribute ( "groupid" ) ) { tk . colorizeTab ( tab ) ; } else if ( tk . ignoreOvers === 0 ) { tk . nextType = "unrelated" ; tk . nextParent = null ; tk . dontMoveNextTab = true ; tk . addedTabs = [ tab ] ; tk . addingTabOver ( ) ; } tk . colorizeTab ( tab ) ; tk . updateIndents ( ) ; delete tab . groupNotChecked ; } ) ; _sortgroup_onSSTabRestoring_timers . push ( { listener : listener , timeout : window . setTimeout ( function ( listener2 ) { listener2 ( ) ; } , 100 , listener ) , } ) ; } ; this . sortgroup_onTabMoved = function sortgroup_onTabMoved ( event ) { const tab = event . target as Tab ; if ( tab . hasAttribute ( "groupid" ) ) tk . colorizeTab ( tab ) ; tk . keepGroupsTogether ( ) ; tk . updateIndents ( ) ; } ; this . sortgroup_onTabRemoved = function sortgroup_onTabRemoved ( event ) { const tab = event . target as Tab ; const gid = tab . getAttribute ( "groupid" ) ; const tid = tk . getTabId ( tab ) ; const pid = tab . getAttribute ( "possibleparent" ) ; tk . chosenNextTab = tk . chooseNextTab ( tab ) ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; if ( t . getAttribute ( "possibleparent" ) === tid ) { t . setAttribute ( "possibleparent" , pid ) ; } } tk . updateIndents ( ) ; if ( gid ) { if ( ! tab . previousSibling || tab . previousSibling . getAttribute ( "groupid" ) !== gid ) { const next = tab . nextSibling ; if ( ! next || next . getAttribute ( "groupid" ) !== gid ) tk . dump ( "Group was already a singleton?! (no next)" ) ; else if ( ! next . nextSibling || next . nextSibling . getAttribute ( "groupid" ) !== gid ) tk . removeGID ( next , true ) ; } else if ( ! tab . nextSibling || tab . nextSibling . getAttribute ( "groupid" ) !== gid ) { const prev = tab . previousSibling ; if ( ! prev || prev . getAttribute ( "groupid" ) !== gid ) tk . dump ( "Group was already a singleton?! (no prev)" ) ; else if ( ! prev . previousSibling || prev . previousSibling . getAttribute ( "groupid" ) !== gid ) tk . removeGID ( prev , true ) ; } } if ( tab . hasAttribute ( "groupcollapsed" ) && ! tab . hidden ) { window . setTimeout ( function __uncollapseTab ( gid2 , next2 , prev2 ) { if ( gBrowser . selectedTab . getAttribute ( "groupid" ) !== gid2 ) { if ( next2 && next2 . getAttribute ( "groupid" ) === gid2 ) tk . tabSetHidden ( next2 , false ) ; else if ( prev2 && prev2 . getAttribute ( "groupid" ) === gid2 ) tk . tabSetHidden ( prev2 , false ) ; } } , 0 , gid , tab . nextSibling , tab . previousSibling ) ; } } ; this . sortgroup_onTabMousedown = function sortgroup_onTabMousedown ( event ) { if ( ( event . target as Element ) . localName !== "tab" ) return ; const tab = event . target as Tab ; if ( tab . hasAttribute ( "groupid" ) && tab . hasAttribute ( "groupcollapsed" ) && event . originalTarget . className === "tab-icon-image" && event . button === 0 && ! event . ctrlKey && ! event . shiftKey && ! event . altKey ) { tk . toggleGroupCollapsed ( event . target ) ; event . stopPropagation ( ) ; event . preventDefault ( ) ; } } ; this . sortgroup_onClickTab = function sortgroup_onClickTab ( event ) { if ( ( event . target as Element ) . localName !== "tab" ) return ; const tab = event . target as Tab ; if ( tab . hasAttribute ( "groupid" ) && ( event . button === 1 || event . button === 0 && ( event as any ) . originalTarget . getAttribute ( "anonid" ) === "close-button" ) && ! event . altKey && navigator . platform . indexOf ( "Mac" ) === - 1 ? event . ctrlKey : event . metaKey ) { if ( event . shiftKey ) tk . closeChildren ( event . target ) ; else tk . closeGroup ( event . target ) ; event . stopPropagation ( ) ; event . preventDefault ( ) ; } } ; this . sortgroup_onDblclickTab = function sortgroup_onDblclickTab ( event ) { const tab = event . target as Tab ; if ( tab . localName === "tab" ) { if ( event . originalTarget . getAttribute ( "anonid" ) === "close-button" ) { if ( ! event . ctrlKey && ! event . altKey && ! event . shiftKey && ! event . metaKey && tk . TabBar . Mode . getIsVerticalMode ( ) ) { gBrowser . removeTab ( tab ) ; event . stopPropagation ( ) ; } } else if ( tab . hasAttribute ( "groupid" ) && _prefs . getBoolPref ( "doubleClickCollapseExpand" ) ) { if ( ! tab . hasAttribute ( "groupcollapsed" ) && _prefs . getBoolPref ( "warnOnDoubleClickCollapse" ) ) { window . focus ( ) ; const check = { value : false } ; const strings = ( document . getElementById ( "bundle_tabkit" ) as any ) ; if ( strings != null ) { _ps . alertCheck ( window , strings . getString ( "tab_kit" ) , strings . getString ( "doubleclickcollapse_warning" ) , strings . getString ( "doubleclickcollapse_dont_mention_again" ) , check ) ; } if ( check . value ) _prefs . setBoolPref ( "warnOnDoubleClickCollapse" , false ) ; } tk . toggleGroupCollapsed ( tab ) ; event . stopPropagation ( ) ; } } } ; this . updateSortGroupMenu = function updateSortGroupMenu ( event ) { if ( event . target !== event . currentTarget ) return ; const popup = event . target ; const contextTab = gBrowser . mContextTab ? gBrowser . mContextTab : gBrowser . selectedTab ; const menu_item_tabkit_tab_toggleUnread = document . getElementById ( "menu_tabkit-tab-toggleUnread" ) ; const bundle_tabkit_el = ( document . getElementById ( "bundle_tabkit" ) as any ) ; if ( menu_item_tabkit_tab_toggleUnread != null && bundle_tabkit_el != null ) { menu_item_tabkit_tab_toggleUnread . setAttribute ( "label" , bundle_tabkit_el . getString ( contextTab . hasAttribute ( "read" ) ? "mark_as_unread" : "mark_as_read" ) ) ; } ( document . getElementById ( "cmd_tabkit-sortgroup-tab-makeGroup" ) as any ) . setAttribute ( "disabled" , ( contextTab === gBrowser . selectedTab ) ) ; const isGroup = contextTab . hasAttribute ( "groupid" ) ; const groupsOnly = ( popup as any ) . getElementsByAttribute ( "groupsonly" , "true" ) ; for ( let i = groupsOnly . length - 1 ; i >= 0 ; i -- ) { groupsOnly [ i ] . hidden = ! isGroup ; } const couldBeSubtree = ( isGroup && tk . subtreesEnabled ( ) ) ; ( document . getElementById ( "menu_tabkit-sortgroup-group-closeChildren" ) as any ) . hidden = ! couldBeSubtree ; const isSubtree = ( couldBeSubtree && tk . getSubtreeFromTab ( contextTab ) . length > 1 ) ; ( document . getElementById ( "cmd_tabkit-sortgroup-group-closeChildren" ) as any ) . setAttribute ( "disabled" , ! isSubtree ) ; if ( isGroup ) { const groupCollapsed = contextTab . hasAttribute ( "groupcollapsed" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-group-collapse" ) as any ) . collapsed = groupCollapsed ; ( document . getElementById ( "menu_tabkit-sortgroup-group-expand" ) as any ) . collapsed = ! groupCollapsed ; } switch ( tk . newTabPosition === 2 ? tk . activeSort : "none" ) { case "uri" : ( document . getElementById ( "menu_tabkit-sortgroup-sortByUri" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case "lastLoaded" : ( document . getElementById ( "menu_tabkit-sortgroup-sortByLastLoaded" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case "lastViewed" : ( document . getElementById ( "menu_tabkit-sortgroup-sortByLastViewed" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case "creation" : ( document . getElementById ( "menu_tabkit-sortgroup-sortByCreation" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case "origin" : ( document . getElementById ( "menu_tabkit-sortgroup-sortByOrigin" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case "title" : ( document . getElementById ( "menu_tabkit-sortgroup-sortByTitle" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; default : ( document . getElementById ( "menu_tabkit-sortgroup-sortByUri" ) as any ) . removeAttribute ( "checked" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-sortByLastLoaded" ) as any ) . removeAttribute ( "checked" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-sortByLastViewed" ) as any ) . removeAttribute ( "checked" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-sortByCreation" ) as any ) . removeAttribute ( "checked" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-sortByOrigin" ) as any ) . removeAttribute ( "checked" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-sortByTitle" ) as any ) . removeAttribute ( "checked" ) ; } switch ( tk . autoGroupNewTabs ? tk . activeGrouping : "none" ) { case "domain" : ( document . getElementById ( "menu_tabkit-sortgroup-groupByDomain" ) as any ) . setAttribute ( "checked" , "true" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-groupByOpener" ) as any ) . removeAttribute ( "checked" ) ; break ; case "opener" : ( document . getElementById ( "menu_tabkit-sortgroup-groupByOpener" ) as any ) . setAttribute ( "checked" , "true" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-groupByDomain" ) as any ) . removeAttribute ( "checked" ) ; break ; default : ( document . getElementById ( "menu_tabkit-sortgroup-groupByDomain" ) as any ) . removeAttribute ( "checked" ) ; ( document . getElementById ( "menu_tabkit-sortgroup-groupByOpener" ) as any ) . removeAttribute ( "checked" ) ; } switch ( tk . newTabPosition ) { default : ( document . getElementById ( "menu_tabkit-sortgroup-newtabs-farRight" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case 1 : ( document . getElementById ( "menu_tabkit-sortgroup-newtabs-nextToCurrent" ) as any ) . setAttribute ( "checked" , "true" ) ; break ; case 2 : ( document . getElementById ( "menu_tabkit-sortgroup-newtabs-lastSort" ) as any ) . setAttribute ( "checked" , "true" ) ; } if ( tk . autoGroupNewTabs ) ( document . getElementById ( "menu_tabkit-sortgroup-newtabs-autoGroup" ) as any ) . setAttribute ( "checked" , "true" ) ; else ( document . getElementById ( "menu_tabkit-sortgroup-newtabs-autoGroup" ) as any ) . removeAttribute ( "checked" ) ; } ; let _keepGroupsTogether_timeoutID = - 1 ; this . keepGroupsTogether = function keepGroupsTogether ( ) { if ( _keepGroupsTogether_timeoutID !== - 1 ) window . clearTimeout ( _keepGroupsTogether_timeoutID ) ; _keepGroupsTogether_timeoutID = window . setTimeout ( function ( ) { const all_groups = tk . getAllGroups ( ) ; for ( const gid in all_groups ) { const group = all_groups [ gid ] ; for ( let i = group . length - 2 ; i >= 0 ; i -- ) if ( group [ i ] . nextSibling !== group [ i + 1 ] ) tk . moveBefore ( group [ i ] , group [ i + 1 ] ) ; } _keepGroupsTogether_timeoutID = - 1 ; } , 250 ) ; } ; this . preInitBlurTabModifications = function preInitBlurTabModifications ( ) { ( function ( ) { "use strict" ; if ( typeof gBrowser . _blurTab !== "function" ) { tk . debug ( "gBrowser._blurTab doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . _blurTab ; gBrowser . _blurTab = function ( aTab ) { "use strict" ; let result ; if ( ! aTab . selected ) { } else if ( tk . chosenNextTab != null ) { tk . blurTab ( aTab ) ; } else { result = old_func . apply ( this , arguments ) ; } return result ; } ; } ) ( ) ; } ; this . preInitListeners . push ( this . preInitBlurTabModifications ) ; this . chosenNextTab = null ; this . pickNextIndex = function pickNextIndex ( index , tabCount ) { if ( tk . chosenNextTab ) { const pos = tk . chosenNextTab . _tPos ; tk . chosenNextTab = null ; return pos > index ? pos - 1 : pos ; } else { tk . dump ( "Hadn't chosen next tab!" ) ; return index === tabCount ? index - 1 : index ; } } ; this . blurTab = function blurTab ( ) { if ( tk . chosenNextTab ) { gBrowser . selectedTab = tk . chosenNextTab ; tk . chosenNextTab = null ; } else { tk . debug ( "Hadn't chosen next tab!" ) ; } } ; this . chooseNextTab = function chooseNextTab ( tab ) { let prev = tab . previousSibling ; let next = tab . nextSibling ; const gid = tab . getAttribute ( "groupid" ) ; while ( prev && "_removingTabs" in gBrowser && gBrowser . _removingTabs . indexOf ( prev ) !== - 1 ) prev = prev . previousSibling ; while ( next && "_removingTabs" in gBrowser && gBrowser . _removingTabs . indexOf ( next ) !== - 1 ) next = next . nextSibling ; const oldPrev = prev , oldNext = next ; while ( prev && ( prev . hidden && prev . getAttribute ( "groupid" ) !== gid || "_removingTabs" in gBrowser && gBrowser . _removingTabs . indexOf ( prev ) !== - 1 ) ) prev = prev . previousSibling ; while ( next && ( next . hidden && next . getAttribute ( "groupid" ) !== gid || "_removingTabs" in gBrowser && gBrowser . _removingTabs . indexOf ( next ) !== - 1 ) ) next = next . nextSibling ; if ( ! prev && ! next ) { prev = oldPrev ; next = oldNext ; } if ( ! next ) return prev ; if ( ! prev ) return next ; switch ( _prefs . getIntPref ( "customCloseOrder" ) ) { case 1 : if ( ! gid || prev . getAttribute ( "groupid" ) === gid || next . getAttribute ( "groupid" ) !== gid ) return prev ; else return next ; case 2 : if ( gid && next . getAttribute ( "groupid" ) !== gid && prev . getAttribute ( "groupid" ) === gid ) return prev ; else return next ; case 3 : return prev ; case 4 : return next ; default : { const defaultTab = ( tk . openRelativePosition === "left" ) ? prev : next ; if ( ! gid ) return defaultTab ; if ( next . getAttribute ( "groupid" ) !== gid ) { if ( prev . getAttribute ( "groupid" ) !== gid ) { tk . log ( "Might have been a singleton group at position " + tab . _tPos + " ?" ) ; return defaultTab ; } else { return prev ; } } if ( prev . getAttribute ( "groupid" ) !== gid ) { return next ; } if ( gid . indexOf ( ":oG-" ) === - 1 && gid . indexOf ( ":tmpOG-" ) === - 1 ) { return defaultTab ; } const tid = tk . getTabId ( tab ) ; const pid = tab . getAttribute ( "possibleparent" ) ; const openerGroup = tab . getAttribute ( tk . Groupings . opener ) ; if ( prev . getAttribute ( tk . Groupings . opener ) === openerGroup ) { if ( next . getAttribute ( tk . Groupings . opener ) !== openerGroup ) { return prev ; } else { if ( defaultTab === next ) { if ( next . getAttribute ( "possibleparent" ) === pid || next . getAttribute ( "possibleparent" ) === tid ) { return next ; } else { return prev ; } } else { if ( prev . getAttribute ( "possibleparent" ) === pid || prev . getAttribute ( "possibleparent" ) === tid ) { return prev ; } else { return next ; } } } } if ( next . getAttribute ( tk . Groupings . opener ) === openerGroup ) { return next ; } return defaultTab ; } } } ; this . sortByUri = function sortByUri ( ) { tk . sortTabsBy ( "uri" ) ; } ; this . sortByLastLoaded = function sortByLastLoaded ( ) { tk . sortTabsBy ( "lastLoaded" ) ; } ; this . sortByLastViewed = function sortByLastViewed ( ) { tk . sortTabsBy ( "lastViewed" ) ; } ; this . sortByCreation = function sortByCreation ( ) { tk . sortTabsBy ( "creation" ) ; } ; this . sortByOrigin = function sortByOrigin ( ) { tk . sortTabsBy ( "origin" ) ; } ; this . sortByTitle = function sortByTitle ( ) { tk . sortTabsBy ( "title" ) ; } ; this . toggleGroupByDomain = function toggleGroupByDomain ( ) { if ( tk . autoGroupNewTabs && tk . activeGrouping === "domain" ) tk . activeGrouping = "none" ; else tk . groupTabsBy ( "domain" ) ; } ; this . toggleGroupByOpener = function toggleGroupByOpener ( ) { if ( tk . autoGroupNewTabs && tk . activeGrouping === "opener" ) tk . activeGrouping = "none" ; else tk . groupTabsBy ( "opener" ) ; } ; this . ungroupAll = function ungroupAll ( ) { tk . activeGrouping = "none" ; for ( let i = 0 ; i < _tabs . length ; i ++ ) tk . removeGID ( _tabs [ i ] ) ; } ; this . openOptions = function openOptions ( ) { let instantApply = false ; const dialog = _wm . getMostRecentWindow ( "mozilla:tabkitsettings" ) ; if ( dialog ) { dialog . focus ( ) ; return ; } try { instantApply = Preferences . get ( "browser.preferences.instantApply" ) ; } catch ( ex ) { } openDialog ( "chrome://tabkit/content/settings.xul" , "_blank" , "chrome,titlebar,toolbar,centerscreen," + ( instantApply ? "dialog=no" : "modal" ) ) ; } ; this . placeNewTabsAtFarRight = function placeNewTabsAtFarRight ( ) { tk . newTabPosition = 0 ; } ; this . placeNewTabsNextToCurrent = function placeNewTabsNextToCurrent ( ) { tk . newTabPosition = 1 ; } ; this . placeNewTabsByLastSort = function placeNewTabsByLastSort ( ) { tk . newTabPosition = 2 ; } ; this . toggleAutoGroupNewTabs = function toggleAutoGroupNewTabs ( ) { tk . autoGroupNewTabs = ! tk . autoGroupNewTabs ; } ; this . openNewTabHere = function openNewTabHere ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; tk . addingTab ( "related" , contextTab ) ; BrowserOpenTab ( { tab_kit_options : { added_tab_type : "related" , parent_tab : contextTab , } , } ) ; const newTab = ( document as mozDocument ) . getAnonymousElementByAttribute ( gBrowser , "linkedpanel" , gBrowser . mPanelContainer . lastChild . id ) ; tk . addingTabOver ( ) ; const gid = contextTab . getAttribute ( "groupid" ) ; if ( tk . openRelativePosition === "left" ) { if ( gid && contextTab . previousSibling && contextTab . previousSibling . getAttribute ( "groupid" ) === gid ) { tk . setGID ( newTab , gid ) ; newTab . setAttribute ( "outoforder" , "true" ) ; } tk . moveBefore ( newTab , contextTab ) ; } else { if ( gid && contextTab . nextSibling && contextTab . nextSibling . getAttribute ( "groupid" ) === gid ) { tk . setGID ( newTab , gid ) ; newTab . setAttribute ( "outoforder" , "true" ) ; } tk . moveAfter ( newTab , contextTab ) ; } } ; this . duplicateTab = function duplicateTab ( contextTab ) { if ( ! contextTab ) { contextTab = gBrowser . selectedTab ; } const newTab = tk . _duplicateTab ( contextTab ) ; function runPostDuplicateTabOperations ( new_tab , parent_tab ) { tk . addingTabOver ( { added_tab_type : "related" , added_tab : new_tab , parent_tab : parent_tab , should_keep_added_tab_position : false , } ) ; const gid = tk . getTabGroupId ( parent_tab ) ; if ( gid && gid !== tk . getTabGroupId ( new_tab ) ) { tk . setGID ( new_tab , gid ) ; new_tab . setAttribute ( "outoforder" , "true" ) ; } if ( tk . openRelativePosition === "left" ) { tk . moveBefore ( new_tab , parent_tab ) ; } else { tk . moveAfter ( new_tab , parent_tab ) ; } } runPostDuplicateTabOperations ( newTab , contextTab ) ; const tab_attributes_backup = tk . getTabAttributesForTabKit ( newTab ) ; if ( typeof TabStateFlusher === "object" && "flush" in TabStateFlusher && typeof TabStateFlusher . flush === "function" ) { const browser = contextTab . linkedBrowser ; TabStateFlusher . flush ( browser ) . then ( ( ) => { tk . setTabAttributesForTabKit ( newTab , tab_attributes_backup ) ; } ) ; } gBrowser . selectedTab = newTab ; } ; this . makeGroup = function makeGroup ( contextTab ) { if ( ! contextTab || contextTab === gBrowser . selectedTab ) return ; tk . makeGroupBetweenTwoTabs ( contextTab , gBrowser . selectedTab ) ; } ; this . makeGroupBetweenTwoTabs = function makeGroupBetweenTwoTabs ( tabA , tabB ) { if ( ! tabA || ! tabB ) { return ; } if ( tabA === tabB ) { return ; } const start = Math . min ( tabA . _tPos , tabB . _tPos ) ; const end = Math . max ( tabA . _tPos , tabB . _tPos ) ; let reallyRegroup = false ; for ( let i = start ; i <= end ; i ++ ) { if ( _tabs [ i ] . hasAttribute ( "groupid" ) ) { reallyRegroup = true ; if ( _prefs . getBoolPref ( "warnOnRegroup" ) ) { const warnOnRegroup = { value : true } ; window . focus ( ) ; const strings = ( document . getElementById ( "bundle_tabkit" ) as any ) ; if ( strings != null ) { reallyRegroup = _ps . confirmCheck ( window , strings . getString ( "tab_kit" ) , strings . getString ( "regroup_warning" ) , strings . getString ( "regroup_warning_prompt_me" ) , warnOnRegroup ) ; } if ( ! reallyRegroup ) return ; if ( ! warnOnRegroup . value ) _prefs . setBoolPref ( "warnOnRegroup" , false ) ; break ; } } } const first = _tabs [ start ] ; const firstGID = first . getAttribute ( "groupid" ) ; const firstPrev = first . previousSibling ? first . previousSibling . getAttribute ( "groupid" ) : "" ; const last = _tabs [ end ] ; const lastGID = last . getAttribute ( "groupid" ) ; const lastNext = last . nextSibling ? last . nextSibling . getAttribute ( "groupid" ) : "" ; if ( firstGID !== lastGID || firstPrev !== firstGID || lastNext !== lastGID ) { if ( firstGID && firstPrev === firstGID && ( ! first . previousSibling . previousSibling || first . previousSibling . previousSibling . getAttribute ( "groupid" ) !== firstGID ) ) { tk . removeGID ( first . previousSibling , true ) ; } if ( lastGID && lastNext === lastGID && ( ! last . nextSibling . nextSibling || last . nextSibling . nextSibling . getAttribute ( "groupid" ) !== lastGID ) ) { tk . removeGID ( last . nextSibling , true ) ; } } const gid = ":oG-manualGroup-" + tk . generateId ( ) ; for ( let i = start ; i <= end ; i ++ ) tk . setGID ( _tabs [ i ] , gid ) ; if ( reallyRegroup ) tk . keepGroupsTogether ( ) ; } ; this . toggleUnread = function toggleUnread ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; if ( contextTab . hasAttribute ( "read" ) ) contextTab . removeAttribute ( "read" ) ; else contextTab . setAttribute ( "read" , "true" ) ; } ; this . closeFromHereToCurrent = function closeFromHereToCurrent ( contextTab ) { if ( ! contextTab || contextTab === gBrowser . selectedTab ) return ; const start = Math . min ( contextTab . _tPos , gBrowser . selectedTab . _tPos ) ; const end = Math . max ( contextTab . _tPos , gBrowser . selectedTab . _tPos ) ; const tabsToClose = [ ] ; for ( let i = start ; i <= end ; i ++ ) { tabsToClose . push ( gBrowser . tabs [ i ] ) ; } for ( let i = tabsToClose . length - 1 ; i >= 0 ; i -- ) { if ( tabsToClose [ i ] === gBrowser . selectedTab ) { continue ; } gBrowser . removeTab ( tabsToClose [ i ] ) ; } } ; this . toggleGroupCollapsed = function toggleGroupCollapsed ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const group = tk . getGroupFromTab ( contextTab ) ; if ( ! group ) { tk . dump ( "toggleGroupCollapsed: Group was null for tab in pos " + contextTab . _tPos ) ; return ; } if ( contextTab . hasAttribute ( "groupcollapsed" ) ) { group . forEach ( function ( tab ) { tab . removeAttribute ( "groupcollapsed" ) ; tk . tabSetHidden ( tab , false ) ; } ) ; } else { const firstTab = group [ 0 ] ; const targetTab = _prefs . getCharPref ( "collapsedGroupVisibleTab" ) === "selected" ? contextTab : firstTab ; group . forEach ( function ( tab ) { tab . setAttribute ( "groupcollapsed" , "true" ) ; if ( tab !== targetTab ) { if ( tab === gBrowser . selectedTab ) gBrowser . selectedTab = targetTab ; tk . tabSetHidden ( tab , true ) ; } } ) ; } tk . updateIndents ( ) ; if ( gBrowser . selectedTab . hidden ) gBrowser . selectedTab = contextTab ; tk . updateMultiRowTabs ( ) ; } ; this . flattenGroup = function flattenGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const group = tk . getGroupFromTab ( contextTab ) ; if ( ! group ) { tk . dump ( "flattenGroup: Group was null for tab in pos " + contextTab . _tPos ) ; return ; } group . forEach ( function ( tab ) { tab . removeAttribute ( "possibleparent" ) ; tab . treeLevel = 0 ; tab . style . marginLeft = "" ; } ) ; } ; this . flattenSubGroup = function flattenSubGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const possibleparent = contextTab . getAttribute ( "possibleparent" ) ; const tabsToClose = tk . getSubtreeFromTab ( contextTab ) ; for ( let i = tabsToClose . length - 1 ; i >= 0 ; i -- ) { const tab = tabsToClose [ i ] ; if ( tab !== contextTab ) { tab . setAttribute ( "possibleparent" , possibleparent ) ; } tab . treeLevel = contextTab . treeLevel || 0 ; tab . style . marginLeft = contextTab . style . marginLeft || "" ; } } ; this . bookmarkGroup = function bookmarkGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const group = tk . getGroupFromTab ( contextTab ) ; if ( ! group ) { tk . dump ( "bookmarkGroup: Group was null for tab in pos " + contextTab . _tPos ) ; return ; } if ( "showBookmarkDialog" in PlacesUIUtils ) { const aURIList = group . map ( function __getUri ( tab ) { return tab . linkedBrowser . webNavigation . currentURI ; } ) ; if ( aURIList . length === 0 ) throw ( new Error ( "bookmarkGroup expects a list of nsIURI objects" ) ) ; const info = { action : "add" , type : "folder" , hiddenRows : [ "description" ] , URIList : aURIList , } ; PlacesUIUtils . showBookmarkDialog ( info , window . top , true ) ; } else { tk . dump ( "showBookmarkDialog NOT in PlacesUIUtils." ) ; } } ; this . reColorGroup = function reColorGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const tabGroupID = contextTab . getAttribute ( "groupid" ) ; if ( ! tabGroupID ) { return ; } const knownColorKey = "knownColor:" + tabGroupID ; tk . deleteWindowValue ( knownColorKey ) ; tk . allocateColor ( contextTab , true ) ; tk . getGroupFromTab ( contextTab ) . forEach ( function ( tab ) { tk . colorizeTab ( tab ) ; } ) ; } ; this . reColorAllGroups = function reColorAllGroups ( ) { const groups = tk . getAllGroups ( ) ; for ( const gid in groups ) { const knownColorKey = "knownColor:" + gid ; tk . deleteWindowValue ( knownColorKey ) ; } Object . values ( groups ) . forEach ( function ( g ) { g . forEach ( ( tab ) => { tk . colorizeTab ( tab ) ; } ) ; } ) ; } ; this . copyGroupURIs = function copyGroupURIs ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const group = tk . getGroupFromTab ( contextTab ) ; if ( ! group ) { tk . dump ( "copyGroupURIs: Group was null for tab in pos " + contextTab . _tPos ) ; return ; } const aURIList = group . map ( function __getUri ( tab ) { return tab . linkedBrowser . webNavigation . currentURI ; } ) ; if ( aURIList . length === 0 ) throw ( new Error ( "copyGroupURIs expects a list of nsIURI objects" ) ) ; let urisStringToCopy = "''" ; aURIList . forEach ( function ( uri ) { urisStringToCopy = urisStringToCopy + uri . spec + "\n" ; } ) ; const transferable = ( ( Transferable as any ) ( ) as any ) ; const unicodeString = Components . classes [ "@mozilla.org/supports-string;1" ] . createInstance ( Components . interfaces . nsISupportsString ) ; const clipboardId = Components . interfaces . nsIClipboard ; const clipboard = Components . classes [ "@mozilla.org/widget/clipboard;1" ] . getService ( clipboardId ) ; unicodeString . data = urisStringToCopy ; transferable . addDataFlavor ( "text/unicode" ) ; transferable . setTransferData ( "text/unicode" , unicodeString , urisStringToCopy . length * 2 ) ; clipboard . setData ( transferable , null , clipboardId . kGlobalClipboard ) ; } ; this . protectAllTabsForGroupOfTab = function protectAllTabsForGroupOfTab ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const tabsInGroup = tk . getGroupFromTab ( contextTab ) ; for ( let i = tabsInGroup . length - 1 ; i >= 0 ; i -- ) { tk . setTabProtected ( tabsInGroup [ i ] ) ; } } ; this . unprotectAllTabsForGroupOfTab = function unprotectAllTabsForGroupOfTab ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const tabsInGroup = tk . getGroupFromTab ( contextTab ) ; for ( let i = tabsInGroup . length - 1 ; i >= 0 ; i -- ) { tk . setTabUnprotected ( tabsInGroup [ i ] ) ; } } ; this . closeGroup = function closeGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const tabsToClose = tk . getGroupFromTab ( contextTab ) ; for ( let i = tabsToClose . length - 1 ; i >= 0 ; i -- ) { gBrowser . removeTab ( tabsToClose [ i ] ) ; } } ; this . closeSubGroup = function closeSubGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; tk . closeChildren ( contextTab ) ; gBrowser . removeTab ( contextTab ) ; } ; this . closeChildren = function closeChildren ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; const tabsToClose = tk . getSubtreeFromTab ( contextTab ) ; for ( let i = tabsToClose . length - 1 ; i >= 0 ; i -- ) { if ( tabsToClose [ i ] !== contextTab ) gBrowser . removeTab ( tabsToClose [ i ] ) ; } } ; this . ungroupGroup = function ungroupGroup ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; tk . getGroupFromTab ( contextTab ) . forEach ( function ( tab ) { tk . removeGID ( tab ) ; } ) ; } ; this . compareTabCreated = function compareTabCreated ( a , b ) { if ( Number ( a . getAttribute ( tk . Sorts . creation ) ) < Number ( b . getAttribute ( tk . Sorts . creation ) ) ) return - 1 ; if ( Number ( a . getAttribute ( tk . Sorts . creation ) ) > Number ( b . getAttribute ( tk . Sorts . creation ) ) ) return 1 ; return 0 ; } ; this . compareTabViewed = function compareTabViewed ( a , b ) { return Number ( a . getAttribute ( tk . Sorts . lastViewed ) ) - Number ( b . getAttribute ( tk . Sorts . lastViewed ) ) ; } ; this . compareTabViewedExceptUnread = function compareTabViewedExceptUnread ( a , b ) { const aV = Number ( a . getAttribute ( tk . Sorts . lastViewed ) ) ; const bV = Number ( b . getAttribute ( tk . Sorts . lastViewed ) ) ; if ( Date . now ( ) < aV && Date . now ( ) >= bV ) return - 1 ; if ( Date . now ( ) >= aV && Date . now ( ) < bV ) return 1 ; return aV - bV ; } ; this . setTabUriKey = function setTabUriKey ( aTab , options = { } ) { if ( typeof options !== "object" ) { options = { } ; } let uri = aTab . linkedBrowser . currentURI ; let uriGroup ; let uriKey ; const initial_uri = "initial_uri" in options ? options . initial_uri : null ; if ( initial_uri != null ) { if ( ! uri || isBlankPageURL ( uri . asciiSpec ) ) { const mURIFixup = gBrowser . mURIFixup ; uri = mURIFixup . createFixupURI ( initial_uri , mURIFixup . FIXUP_FLAGS_MAKE_ALTERNATE_URI ) ; } } if ( ! uri ) uri = _ios . newURI ( "about:blank" , null , null ) ; if ( isBlankPageURL ( uri . asciiSpec ) ) { uriKey = "zzzzzzzzzzzzzzz/about/blank" ; } else if ( uri . asciiHost === "" ) { uriKey = uri . scheme + "/" + uri . path ; uriGroup = uri . asciiSpec . replace ( /^[^:]*:\/*([^/]+).*$/ , "$1" ) ; } else { try { const eTLDService = Cc [ "@mozilla.org/network/effective-tld-service;1" ] . getService ( Ci . nsIEffectiveTLDService ) ; const baseDomain = eTLDService . getBaseDomain ( uri ) ; const subDomain = uri . asciiHost . substring ( 0 , uri . asciiHost . length - baseDomain . length ) ; uriKey = baseDomain + subDomain . split ( "." ) . reverse ( ) . join ( "." ) + "/" + uri . scheme + "/" + uri . path ; uriGroup = baseDomain . substring ( 0 , baseDomain . indexOf ( "." ) ) ; } catch ( ex ) { if ( uri . asciiSpec !== "chrome://speeddial/content/speeddial.xul" ) tk . debug ( "Error using nsIEffectiveTLDService:\n" + ex ) ; const parts = /^(.*\.)?(([^.]+)\.[^.]{2,8}\.(?:a[ru]|c[kory]|do|eg|fj|gu|i[dl]|k[hr]|lb|m[moty]|n[ipz]|p[aey]|sv|t[hr]|u[gky]|ve|yu|za))$|^(.*\.)?(([^.]+)\.[^.0-9]{2,})$|^(.*)$/i . exec ( uri . asciiHost ) ; const key = parts [ 2 ] ? ( parts [ 1 ] ? parts [ 2 ] + parts [ 1 ] . split ( "." ) . reverse ( ) . join ( "." ) : parts [ 2 ] ) : parts [ 5 ] ? ( parts [ 4 ] ? parts [ 5 ] + parts [ 4 ] . split ( "." ) . reverse ( ) . join ( "." ) : parts [ 5 ] ) : parts [ 7 ] ; uriKey = key + "/" + uri . scheme + "/" + uri . path ; uriGroup = parts [ 3 ] || parts [ 6 ] || parts [ 7 ] || uri . asciiSpec . replace ( /^[^:]*:\/*([^/]+).*$/ , "$1" ) ; } } aTab . setAttribute ( tk . Sorts . uri , uriKey ) ; if ( uriKey === "zzzzzzzzzzzzzzz/about/blank" ) aTab . removeAttribute ( tk . Groupings . domain ) ; else aTab . setAttribute ( tk . Groupings . domain , ":dG-" + ( uriGroup || "" ) + ":" ) ; } ; this . preInitListeners . push ( function ( ) { ( function ( ) { if ( typeof gBrowser . addTab !== "function" ) { tk . debug ( "gBrowser.addTab doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . addTab ; gBrowser . addTab = function ( aURI , _aReferrerURI , _aCharset , _aPostData , _aOwner , _aAllowThirdPartyFixup ) { let initialURI = aURI ; if ( aURI == null || isBlankPageURL ( aURI ) || aURI === "about:customizing" ) { initialURI = null ; } const result = old_func . apply ( this , arguments ) ; const tab = result ; if ( tab != null ) { tk . setTabUriKey ( tab , { initial_uri : initialURI } ) ; } return result ; } ; } ) ( ) ; } ) ; let _seed = 0 ; this . generateId = function generateId ( ) { return String ( Date . now ( ) . toString ( ) ) + "-" + String ( ++ _seed ) ; } ; this . getTabById = function getTabById ( tid ) { for ( let i = 0 ; i < _tabs . length ; i ++ ) { const tab = _tabs [ i ] ; if ( tk . getTabId ( tab ) === tid ) return tab ; } tk . debug ( "Tab id not found: " + tid + "\n" + tk . quickStack ( ) ) ; return null ; } ; this . countGroups = function countGroups ( gid ) { const match = gid . match ( /\|/g ) ; return match ? match . length + 1 : 1 ; } ; this . getUngroupedTabsByAttr = function getUngroupedTabsByAttr ( attr , value ) { const tabs = [ ] ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; if ( ! t . hasAttribute ( "groupid" ) && ( value ? t . getAttribute ( attr ) === value : t . hasAttribute ( attr ) ) ) tabs . push ( t ) ; } return tabs ; } ; this . getGroupById = function getGroupById ( gid , lookForSingletons ) { const group = [ ] ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; if ( t . getAttribute ( "groupid" ) . indexOf ( gid ) !== - 1 ) { group . push ( t ) ; } else if ( lookForSingletons && t . getAttribute ( "singletonid" ) . indexOf ( gid ) !== - 1 ) { tk . setGID ( t , gid ) ; group . push ( t ) ; } } return group ; } ; /**
         * Returns the tab group containing tab, as an array of
         * tabs, or null if the tab is not part of a group.
         *
         * FROZEN since 0.4.3 - this method will not be changed.
         */ this . getGroupFromTab = function getGroupFromTab ( tab ) { const gid = tab . getAttribute ( "groupid" ) ; if ( ! gid ) { tk . debug ( "getGroupFromTab called on an ungrouped tab (_tPos=" + tab . _tPos + ") returning null\n" + tk . quickStack ( ) ) ; return null ; } const group = [ tab ] ; let cur = null as null | Tab ; while ( ( cur = group [ group . length - 1 ] . previousSibling ) && cur . getAttribute ( "groupid" ) === gid ) { group . push ( cur ) ; } group . reverse ( ) ; while ( ( cur = group [ group . length - 1 ] . nextSibling ) && cur . getAttribute ( "groupid" ) === gid ) { group . push ( cur ) ; } if ( group . length <= 1 ) tk . dump ( "getGroupFromTab found a singleton group (_tPos=" + tab . _tPos + ") returning [tab]\n" + tk . quickStack ( ) ) ; return group ; } ; this . getAllGroups = function getAllGroups ( ) : { [ key ] : Tab [ ] ; } { const groups = { } as { [ key ] : Tab [ ] ; } ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; const gid = t . getAttribute ( "groupid" ) ; if ( ! gid ) continue ; if ( gid in groups ) groups [ gid ] . push ( t ) ; else groups [ gid ] = [ t ] ; } return groups ; } ; this . setGID = function setGID ( tab , gid ) { if ( ! gid ) { tk . dump ( "setGID: Bad groupid \"" + gid + "\"" ) ; return ; } tk . removeCollapsedTab ( tab ) ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; if ( t . getAttribute ( "groupid" ) === gid ) { if ( t . hasAttribute ( "groupcollapsed" ) ) { tab . setAttribute ( "groupcollapsed" , "true" ) ; for ( const st of tk . getGroupById ( gid ) ) { if ( ! st . hidden ) { if ( st . getAttribute ( "selected" ) === "true" ) { tk . tabSetHidden ( tab , true ) ; } else { tk . tabSetHidden ( st , true ) ; } break ; } } } break ; } } tab . removeAttribute ( "singletonid" ) ; tab . setAttribute ( "groupid" , gid ) ; tk . colorizeTab ( tab ) ; tk . updateIndents ( ) ; } ; this . removeGID = function removeGID ( tab , becauseSingleton ) { tk . removeCollapsedTab ( tab ) ; if ( becauseSingleton ) tab . setAttribute ( "singletonid" , tab . getAttribute ( "groupid" ) ) ; else tab . removeAttribute ( "singletonid" ) ; tab . removeAttribute ( "groupid" ) ; tk . colorizeTab ( tab ) ; tk . updateIndents ( ) ; } ; this . removeCollapsedTab = function removeCollapsedTab ( tab ) { if ( tab . hasAttribute ( "groupcollapsed" ) ) { tab . removeAttribute ( "groupcollapsed" ) ; const tgid = tab . getAttribute ( "groupid" ) ; if ( tgid ) { const oldGroup = [ ] ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; if ( t !== tab && t . getAttribute ( "groupid" ) === tgid ) oldGroup . push ( t ) ; } if ( oldGroup . length > 0 ) tk . ensureCollapsedGroupVisible ( oldGroup ) ; } } tk . tabSetHidden ( tab , false ) ; } ; this . ensureCollapsedGroupVisible = function ensureCollapsedGroupVisible ( group ) { const should_return = group . some ( function ( t ) { return ! t . hidden ; } ) ; if ( should_return ) return ; let mostRecent = group [ 0 ] ; for ( let i = 1 ; i < group . length ; i ++ ) if ( tk . compareTabViewedExceptUnread ( group [ i ] , mostRecent ) > 0 ) mostRecent = group [ i ] ; tk . tabSetHidden ( mostRecent , false ) ; } ; this . subtreesEnabled = function subtreesEnabled ( ) { return ( tk . TabBar . Mode . getIsVerticalMode ( ) && _prefs . getBoolPref ( "indentedTree" ) ) ; } ; this . getSubtreeFromTab = function getSubtreeFromTab ( tab ) { if ( ! tk . subtreesEnabled ( ) ) return [ tab ] ; const group = tk . getGroupFromTab ( tab ) ; if ( ! group ) return [ tab ] ; const gid = tab . getAttribute ( "groupid" ) ; tk . updateIndents ( group ) ; const tabs = [ tab ] ; for ( let t = tab . nextSibling ; t && t . treeLevel && t . treeLevel > tab . treeLevel && t . getAttribute ( "groupid" ) === gid ; t = t . nextSibling ) tabs . push ( t ) ; return tabs ; } ; let _lastUpdateIndents = 0 ; let _updateIndentsRequested = false ; this . updateIndents = function updateIndents ( group ? ) { const subtreesEnabled = tk . subtreesEnabled ( ) ; if ( ! group ) { if ( ! subtreesEnabled ) return ; if ( ! _updateIndentsRequested ) { const timeSinceLastUpdate = Date . now ( ) - _lastUpdateIndents ; if ( timeSinceLastUpdate >= 150 ) { for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . style . marginLeft = "" ; const all_groups = tk . getAllGroups ( ) ; for ( const gid in all_groups ) { const g = all_groups [ gid ] ; tk . updateIndents ( g ) ; } _lastUpdateIndents = Date . now ( ) ; } else { _updateIndentsRequested = true ; window . setTimeout ( function __updateAllIndents ( ) { _updateIndentsRequested = false ; tk . updateIndents ( ) ; } , 150 - timeSinceLastUpdate ) ; } } return ; } if ( ! group . length ) { tk . debug ( "updateIndents called for zero length group: " + group . toString ( ) + "\n" + tk . quickStack ( ) ) ; return ; } const groupcollapsed = group [ 0 ] . hasAttribute ( "groupcollapsed" ) ; if ( groupcollapsed ) { group . forEach ( function ( t ) { t . style . marginLeft = "" ; } ) ; } let stack = [ ] as string [ ] ; const maxlevel = _prefs . getIntPref ( "maxTreeLevel" ) ; const indent = _prefs . getIntPref ( "indentAmount" ) ; group . forEach ( function ( tab ) { const pp = tab . getAttribute ( "possibleparent" ) ; if ( pp ) { let j = stack . length - 1 ; for ( ; j >= 0 ; j -- ) { if ( stack [ j ] === pp ) { stack . push ( tk . getTabId ( tab ) ) ; tab . treeLevel = Math . min ( j + 1 , maxlevel ) ; if ( ! groupcollapsed && subtreesEnabled ) { tab . style . setProperty ( "margin-left" , ( indent * tab . treeLevel ) + "px" , "important" ) ; } break ; } stack . pop ( ) ; } if ( j >= 0 ) return ; } tab . treeLevel = 0 ; tab . style . marginLeft = "" ; stack = [ tk . getTabId ( tab ) ] ; } ) ; } ; this . toggleIndentedTree = function toggleIndentedTree ( ) { if ( tk . TabBar . Mode . getIsVerticalMode ( ) && _prefs . getBoolPref ( "indentedTree" ) ) tk . updateIndents ( ) ; else for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . style . marginLeft = "" ; } ; this . detectTheme = function detectTheme ( ) { let forceThemeCompatibility = _prefs . getIntPref ( "forceThemeCompatibility" ) ; let darkTheme ; const theme = gPrefService . getCharPref ( "general.skins.selectedSkin" ) ; const goodThemes = { "classic/1.0" : { platform : /Win32/ } , "abstractPCNightly" : { } , "abstract_zune" : { dark : true } , "aero_fox" : { dark : true } , "aero_silver_fox" : { } , "aquatint_gloss" : { dark : true } , "aquatintII" : { } , "AzertyIII" : { } , "blackx" : { } , "cfoxmodern" : { } , "kempelton" : { } , "MacOSX" : { } , "pitchdark" : { } , "phoenityreborn" : { } , "qute" : { } , "vistaxp" : { } , "xpvista" : { } , } as { [ key ] : { platform ? : RegExp ; dark ? : true ; } ; } ; const badThemes = { "nasanightlaunch" : { dark : true } , "NG_Classic" : { dark : true } , } as { [ key ] : { platform ? : RegExp ; dark ? : true ; } ; } ; if ( theme in goodThemes && ( ! ( "platform" in goodThemes [ theme ] ) || goodThemes [ theme ] . platform . test ( navigator . platform ) ) ) { if ( forceThemeCompatibility === 1 ) forceThemeCompatibility = 0 ; darkTheme = ( "dark" in goodThemes [ theme ] ) ; } else { if ( forceThemeCompatibility === 1 ) forceThemeCompatibility = 2 ; darkTheme = ( theme in badThemes ) ? ( "dark" in badThemes [ theme ] ) : false ; } if ( forceThemeCompatibility > 0 ) gBrowser . tabContainer . setAttribute ( "tk-forcethemecompatibility" , "true" ) ; else gBrowser . tabContainer . removeAttribute ( "tk-forcethemecompatibility" ) ; if ( darkTheme ) gBrowser . tabContainer . setAttribute ( "tk-darktheme" , "true" ) ; else gBrowser . tabContainer . removeAttribute ( "tk-darktheme" ) ; if ( _prefs . getBoolPref ( "colorTabNotLabel" ) ) { for ( let i = 0 ; i < gBrowser . tabs . length ; i ++ ) { const tab = gBrowser . tabs [ i ] ; tk . colorizeTab ( tab ) ; } } else { for ( let i = 0 ; i < gBrowser . tabs . length ; i ++ ) { const tab = gBrowser . tabs [ i ] ; tk . colorizeTab ( tab ) ; } } } ; this . regenSaturationLightness = function regenSaturationLightness ( ) { const groups = tk . getAllGroups ( ) ; Object . keys ( groups ) . forEach ( function ( gid ) { try { const hue = /hsl\((\d+),/ . exec ( tk . getWindowValue ( "knownColor:" + gid ) ) [ 1 ] ; const sat = tk . randInt ( _prefs . getIntPref ( "minSaturation" ) , _prefs . getIntPref ( "maxSaturation" ) ) ; const lum = tk . randInt ( _prefs . getIntPref ( "minLightness" ) , _prefs . getIntPref ( "maxLightness" ) ) ; tk . setWindowValue ( "knownColor:" + gid , "hsl(" + hue + ", " + sat + "%, " + lum + "%)" ) ; groups [ gid ] . forEach ( ( tab ) => { tk . colorizeTab ( tab ) ; } ) ; } catch ( ex ) { tk . dump ( ex ) ; } } ) ; } ; this . allocateColor = function allocateColor ( tab , ignoreSurroundingGroups ) { let hue ; if ( ignoreSurroundingGroups == null ) { ignoreSurroundingGroups = false ; } const tabGroupID = tab . getAttribute ( "groupid" ) ; if ( ! tabGroupID ) { return "" ; } const knownColorKey = "knownColor:" + tabGroupID ; if ( tk . getWindowValue ( knownColorKey ) ) { return tk . getWindowValue ( knownColorKey ) ; } const gids = [ ] ; const gidDist = { } as { [ key ] : number ; } ; for ( let i = tab . _tPos - 1 , n = 0 ; i >= 0 && n < 3 ; i -- ) { const gid = _tabs [ i ] . getAttribute ( "groupid" ) ; if ( gid && gids . indexOf ( gid ) === - 1 && tk . getWindowValue ( "knownColor:" + gid ) ) { gids . push ( gid ) ; gidDist [ gid ] = n ; n ++ ; } } for ( let i = tab . _tPos + 1 , n = 0 ; i < _tabs . length && n < 3 ; i ++ ) { const gid = _tabs [ i ] . getAttribute ( "groupid" ) ; if ( gid && gids . indexOf ( gid ) === - 1 && tk . getWindowValue ( "knownColor:" + gid ) ) { gids . push ( gid ) ; gidDist [ gid ] = n ; n ++ ; } } if ( gids . length < 1 || ignoreSurroundingGroups ) { hue = Math . floor ( Math . random ( ) * 360 ) ; } else { const hues = [ ] as number [ ] ; const hueDistance = { } as { [ key ] : number ; } ; gids . forEach ( function ( gidInEach ) { const match = /\d+/ . exec ( tk . getWindowValue ( "knownColor:" + gidInEach ) ) ; const hueInEach = Number ( match [ 0 ] ) ; hues . push ( hueInEach ) ; switch ( gidDist [ gidInEach ] ) { case 0 : hueDistance [ hueInEach ] = 60 ; break ; case 1 : hueDistance [ hueInEach ] = 60 ; break ; case 2 : hueDistance [ hueInEach ] = 45 ; } } ) ; hues . sort ( function __compareNumbers ( a , b ) { return a - b ; } ) ; let chosenGapStart = hues [ hues . length - 1 ] ; let chosenGapEnd = hues [ 0 ] ; let chosenGap = chosenGapEnd - chosenGapStart + 360 - hueDistance [ chosenGapStart ] - hueDistance [ chosenGapEnd ] ; let totalGap = chosenGap ; for ( let i = 1 ; i < hues . length ; i ++ ) { const gapStart = hues [ i - 1 ] ; const gapEnd = hues [ i ] ; const gap = gapEnd - gapStart - hueDistance [ gapStart ] - hueDistance [ gapEnd ] ; if ( totalGap <= 0 ) { if ( gap > chosenGap ) { chosenGapStart = gapStart ; chosenGapEnd = gapEnd ; totalGap = chosenGap = gap ; } } else if ( gap > 0 ) { totalGap += gap ; if ( Math . random ( ) < gap / totalGap ) { chosenGapStart = gapStart ; chosenGapEnd = gapEnd ; chosenGap = gap ; } } } if ( totalGap > 0 ) { chosenGapStart = ( chosenGapStart + hueDistance [ chosenGapStart ] ) % 360 ; hue = ( chosenGapStart + Math . floor ( Math . random ( ) * chosenGap ) ) % 360 ; } else { chosenGap += hueDistance [ chosenGapStart ] + hueDistance [ chosenGapEnd ] ; hue = Math . round ( chosenGapStart + ( chosenGap / 2 ) ) % 360 ; } } const saturation = tk . randInt ( _prefs . getIntPref ( "minSaturation" ) , _prefs . getIntPref ( "maxSaturation" ) ) ; const lightness = tk . randInt ( _prefs . getIntPref ( "minLightness" ) , _prefs . getIntPref ( "maxLightness" ) ) ; const resultColor = "hsl(@hue, @saturation%, @lightness%)" . replace ( "@hue" , hue . toString ( ) ) . replace ( "@saturation" , saturation ) . replace ( "@lightness" , lightness ) ; tk . setWindowValue ( knownColorKey , resultColor ) ; return resultColor ; } ; this . colorizeTab = function colorizeTab ( tab ) { try { const tabText = ( tab . ownerDocument as mozDocument ) . getAnonymousElementByAttribute ( tab , "class" , "tab-text tab-label" ) ; let node = tab as Tab | HTMLElement ; if ( ! _prefs . getBoolPref ( "colorTabNotLabel" ) ) { node = tabText ; } tab . style . removeProperty ( "background-image" ) ; tabText . style . removeProperty ( "background-image" ) ; if ( _prefs . getBoolPref ( "disableTabGroupColor" ) ) { return ; } let bgColor = tk . allocateColor ( tab ) ; if ( bgColor !== "" ) { bgColor = "-moz-linear-gradient(@HSL_Top, @HSL_Bottom)" . replace ( "@HSL_Top" , bgColor ) . replace ( "@HSL_Bottom" , bgColor ) ; } else { } node . style . setProperty ( "background-image" , bgColor , "important" ) ; } catch ( ex ) { tk . dump ( ex ) ; } } ; this . colorAllTabsMenuItem = function colorAllTabsMenuItem ( tab , menuItem ) { try { const bgSample = tab ; if ( _prefs . getBoolPref ( "colorTabNotLabel" ) ) { menuItem . style . backgroundImage = bgSample . style . backgroundImage ; } else if ( ( gBrowser . tabContainer . hasAttribute ( "tabkit-highlight-unread-tab" ) && ! tab . hasAttribute ( "read" ) ) || ( gBrowser . tabContainer . hasAttribute ( "tabkit-highlight-current-tab" ) && tab . getAttribute ( "selected" ) === "true" ) ) { const bgStyle = window . getComputedStyle ( bgSample , null ) ; menuItem . style . backgroundImage = bgStyle . backgroundImage ; } menuItem . style . appearance = "none" ; window . setTimeout ( function __colorAllTabsMenuText ( tabInTimeout , menuItemInTimeout ) { try { const menuText = ( document as mozDocument ) . getAnonymousElementByAttribute ( menuItemInTimeout , "class" , "menu-iconic-text" ) ; if ( menuText == null ) { return ; } const style = ( ( menuText as any ) . style as any ) ; style . fontStyle = tabInTimeout . hasAttribute ( "read" ) ? "normal" : "italic" ; const fgStyle = window . getComputedStyle ( ( document as mozDocument ) . getAnonymousElementByAttribute ( tabInTimeout , "class" , "tab-text tab-label" ) , null ) ; style . setProperty ( "background-color" , fgStyle . backgroundColor , "important" ) ; style . setProperty ( "color" , fgStyle . color , "important" ) ; } catch ( ex ) { tk . dump ( ex ) ; } } , 20 , tab , menuItem ) ; } catch ( ex ) { tk . dump ( ex ) ; } } ; this . colorAllTabsMenu = function colorAllTabsMenu ( ) { if ( ! gBrowser . mCurrentTab . mCorrespondingMenuitem ) return ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const tab = _tabs [ i ] ; if ( tab . mCorrespondingMenuitem ) tk . colorAllTabsMenuItem ( tab , tab . mCorrespondingMenuitem ) ; } } ; this . postInitAllTabsMenuColors = function postInitAllTabsMenuColors ( event ) { if ( _tabContainer . mAllTabsPopup ) { _tabContainer . mAllTabsPopup . addEventListener ( "popupshowing" , tk . colorAllTabsMenu , false ) ; } _tabContainer . addEventListener ( "TabClose" , tk . colorAllTabsMenu , false ) ; _tabContainer . addEventListener ( "TabSelect" , tk . colorAllTabsMenu , false ) ; tk . colorAllTabsMenu ( event ) ; } ; this . postInitListeners . push ( this . postInitAllTabsMenuColors ) ; this . groupTabsBy = function groupTabsBy ( groupingName ) { const groupingAttr = tk . Groupings [ groupingName ] ; const newGroups = { } as { [ key ] : Tab [ ] ; } ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { const t = _tabs [ i ] ; if ( ! t . hasAttribute ( "groupid" ) ) { const gid = t . getAttribute ( groupingAttr ) ; if ( gid !== "" ) { const group = tk . getGroupById ( gid ) ; if ( group . length > 0 ) { if ( tk . newTabPosition === 2 && tk . activeSort !== "origin" && ( groupingName !== "domain" || ! _prefs . getBoolPref ( "autoSortDomainGroups" ) ) && ( groupingName !== "opener" || ! _prefs . getBoolPref ( "autoSortOpenerGroups" ) ) ) { tk . insertTab ( t , gid ) ; } else { tk . moveAfter ( t , group [ group . length - 1 ] ) ; } tk . setGID ( t , group [ 0 ] . getAttribute ( "groupid" ) ) ; } else { if ( gid in newGroups ) newGroups [ gid ] . push ( t ) ; else newGroups [ gid ] = [ t ] ; } } } } for ( const _gid in newGroups ) { const group = newGroups [ _gid ] ; if ( group . length > 1 ) { for ( const tab in group ) { tk . setGID ( tab , _gid ) ; } const mi = group . length >> 1 ; const median = group [ mi ] ; for ( let i = 0 ; i < mi ; i ++ ) tk . moveBefore ( group [ i ] , median ) ; for ( let i = group . length - 1 ; i > mi ; i -- ) tk . moveAfter ( group [ i ] , median ) ; } } if ( groupingName === "domain" ) { if ( _prefs . getBoolPref ( "autoSortDomainGroups" ) ) { const groups = tk . getAllGroups ( ) ; for ( const gidInGroup in groups ) { if ( gidInGroup . indexOf ( ":dG-" ) !== - 1 ) tk . sortTabsBy ( "uri" , gidInGroup ) ; } } } else if ( groupingName === "opener" ) { if ( _prefs . getBoolPref ( "autoSortOpenerGroups" ) ) { const groups = tk . getAllGroups ( ) ; for ( const gidInGroup in groups ) { if ( gidInGroup . indexOf ( ":oG-" ) !== - 1 || gidInGroup . indexOf ( ":tmpOG-" ) !== - 1 ) tk . sortTabsBy ( "origin" , gidInGroup ) ; } } } if ( groupingName !== tk . activeGrouping ) tk . activeGrouping = groupingName ; } ; this . sortTabsBy = function sortTabsBy ( sortName , gid ) { if ( ! ( sortName in tk . Sorts ) ) { tk . dump ( "sortTabsBy: Bad sortName: \"" + sortName + "\"" ) ; return ; } const isReverse = tk . ReverseSorts . has ( sortName ) ; const isNumeric = tk . NumericSorts . has ( sortName ) ; const isOrigin = ( sortName === "origin" ) ; let tabset = [ ] ; let attr = "" ; if ( isOrigin ) { if ( gid ) { tabset = tk . getGroupById ( gid ) ; } else { for ( let i = 0 ; i < _tabs . length ; i ++ ) tabset . push ( _tabs [ i ] ) ; } tabset . sort ( tk . compareTabCreated ) ; let lastParent = null as null | string ; let recentChildren = 0 ; for ( let i = 1 ; i < tabset . length ; i ++ ) { const pp = tabset [ i ] . getAttribute ( "possibleparent" ) ; if ( pp ) { for ( let j = i - 1 ; j >= 0 ; j -- ) { if ( tk . getTabId ( tabset [ j ] ) === pp ) { if ( tk . openRelativePosition === "left" ) { tabset . splice ( j , 0 , tabset . splice ( i , 1 ) [ 0 ] ) ; } else { if ( tk . openRelativePosition !== "right" ) { if ( lastParent === pp ) { j += recentChildren ++ ; } else { lastParent = pp ; recentChildren = 1 ; } } tabset . splice ( j + 1 , 0 , tabset . splice ( i , 1 ) [ 0 ] ) ; } break ; } } } } for ( let i = 0 ; i < tabset . length ; i ++ ) { tabset [ i ] . key = i ; } } else { attr = tk . Sorts [ sortName ] ; } let groups = { } as { [ key ] : Tab [ ] ; } ; if ( gid ) { groups [ gid ] = tk . getGroupById ( gid ) ; } else { groups = tk . getAllGroups ( ) ; } for ( const groupid in groups ) { const g = groups [ groupid ] ; if ( ! gid && ! isOrigin ) for ( let i = 0 ; i < g . length ; i ++ ) g [ i ] . key = isNumeric ? Number ( g [ i ] . getAttribute ( attr ) ) : g [ i ] . getAttribute ( attr ) . toLowerCase ( ) ; if ( gid || tk . countGroups ( groupid ) !== 1 || ( ( groupid . indexOf ( ":dG-" ) === - 1 || ! _prefs . getBoolPref ( "autoSortDomainGroups" ) ) && ( ( groupid . indexOf ( ":oG-" ) === - 1 && groupid . indexOf ( ":tmpOG-" ) === - 1 ) || ! _prefs . getBoolPref ( "autoSortOpenerGroups" ) ) ) ) { for ( let i = 1 ; i < g . length ; i ++ ) { const gi = g [ i ] ; let j = i - 1 ; for ( ; j >= 0 ; j -- ) { if ( isReverse ? g [ j ] . key >= gi . key : g [ j ] . key <= gi . key ) break ; g [ j + 1 ] = g [ j ] ; } g [ j + 1 ] = gi ; gi . removeAttribute ( "outoforder" ) ; } } if ( ! gid ) { let representative = "" as any ; if ( sortName === "origin" ) representative = ( tk . openRelativePosition === "left" ) ? g . concat ( ) . sort ( tk . compareTabCreated ) [ 0 ] . key : g [ 0 ] . key ; else representative = g [ g . length >> 1 ] . key ; for ( let k = 0 ; k < g . length ; k ++ ) g [ k ] . key = representative ; } } if ( gid ) { const group = groups [ gid ] ; const firstTab = group [ 0 ] ; for ( let i = 1 ; i < group . length ; i ++ ) tk . moveAfter ( group [ i ] , firstTab ) ; } else { for ( let i = 0 ; i < _tabs . length ; i ++ ) { const ti = _tabs [ i ] ; const tig = ti . getAttribute ( "groupid" ) ; if ( ! tig && ! isOrigin ) ti . key = isNumeric ? Number ( ti . getAttribute ( attr ) ) : ti . getAttribute ( attr ) . toLowerCase ( ) ; let j = i - 1 ; for ( ; j >= 0 ; j -- ) { if ( isReverse ? _tabs [ j ] . key >= ti . key : _tabs [ j ] . key <= ti . key ) break ; } if ( ! tig ) { gBrowser . moveTabTo ( ti , j + 1 ) ; ti . removeAttribute ( "outoforder" ) ; } else { const g = groups [ tig ] ; for ( let k = 0 ; k < g . length ; k ++ ) { gBrowser . moveTabTo ( g [ k ] , j + k + 1 ) ; g [ k ] . removeAttribute ( "outoforder" ) ; } i += g . length - 1 ; } } if ( tk . activeSort !== sortName ) tk . activeSort = sortName ; } } ; this . insertTab = function insertTab ( tab , gid , sortName ) { let tabset = [ ] as Tab [ ] ; if ( gid ) { tabset = tk . getGroupById ( gid ) ; } else { for ( let i = 0 ; i < _tabs . length ; i ++ ) tabset . push ( _tabs [ i ] ) ; } const tabIndex = tabset . indexOf ( tab ) ; if ( tabIndex !== - 1 ) tabset . splice ( tabIndex , 1 ) ; if ( tabset . length === 0 ) { tk . dump ( "insertTab: tabset is empty!" ) ; return ; } if ( ! sortName ) sortName = tk . activeSort ; if ( ! ( sortName in tk . Sorts ) || sortName === "origin" ) { tk . dump ( "Cannot insert by \"" + sortName + "\"" ) ; return ; } const isReverse = tk . ReverseSorts . has ( sortName ) ; const isNumeric = tk . NumericSorts . has ( sortName ) ; const attr = tk . Sorts [ sortName ] ; tab . key = isNumeric ? Number ( tab . getAttribute ( attr ) ) : tab . getAttribute ( attr ) ; let i = 0 ; while ( i < tabset . length ) { const ti = tabset [ i ] ; if ( ! ti . hasAttribute ( "outoforder" ) && ( gid || ! ti . hasAttribute ( "groupid" ) ) ) { ti . key = isNumeric ? Number ( ti . getAttribute ( attr ) ) : ti . getAttribute ( attr ) ; if ( isReverse ? ti . key < tab . key : ti . key > tab . key ) break ; } i ++ ; } if ( i < tabset . length ) tk . moveBefore ( tab , tabset [ i ] ) ; else tk . moveAfter ( tab , tabset [ tabset . length - 1 ] ) ; tab . removeAttribute ( "outoforder" ) ; } ; this . preInitListeners . push ( function ( ) { "use strict" ; ( function ( ) { "use strict" ; if ( typeof gBrowser . createTooltip !== "function" ) { tk . debug ( "gBrowser.createTooltip doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . createTooltip ; gBrowser . createTooltip = function ( event ) { "use strict" ; const tab = ( document as mozDocument ) . tooltipNode ; if ( tab . localName !== "tab" ) { event . preventDefault ( ) ; return ; } const result = old_func . apply ( this , arguments ) ; if ( ( tab as any ) . hasAttribute ( "groupcollapsed" ) ) { const new_label = tk . getGroupFromTab ( tab ) . map ( function __getLabel ( ctab ) { return ctab === tab ? "> " + ctab . label : " - " + ctab . label ; } ) . join ( "\n" ) ; event . target . setAttribute ( "label" , new_label ) ; } return result ; } ; } ) ( ) ; } ) ; this . _duplicateTab = function _duplicateTab ( aTab ) { "use strict" ; if ( typeof _ss !== "object" || ! ( "duplicateTab" in _ss ) || typeof _ss . duplicateTab !== "function" ) { return gBrowser . loadOneTab ( gBrowser . getBrowserForTab ( aTab ) . currentURI . spec ) ; } const newTab = _ss . duplicateTab ( window , aTab ) ; tk . generateNewTabId ( newTab ) ; tk . removeGID ( newTab ) ; return newTab ; } ; this . _onDrop = function _onDrop ( event ) { const dt = ( event as any ) . dataTransfer ; const dropEffect = dt . dropEffect ; const targetTab = event . target . localName === "tab" ? event . target : null ; if ( ! targetTab || targetTab . hasAttribute ( "pinned" ) ) return ; const draggedTab = dt . mozGetDataAt ( TAB_DROP_TYPE , 0 ) ; if ( ! draggedTab || draggedTab === targetTab || draggedTab . hasAttribute ( "pinned" ) ) return ; this . _tabDropIndicator . collapsed = true ; const draggedGid = draggedTab . getAttribute ( "groupid" ) ; const dPrev = draggedTab . previousSibling ; const dNext = draggedTab . nextSibling ; let newIndex = tk . _getDropIndex ( event ) ; let beforeTab = newIndex > 0 ? _tabs [ newIndex - 1 ] : null ; let afterTab = newIndex < _tabs . length ? _tabs [ newIndex ] : null ; let bGid = beforeTab ? beforeTab . getAttribute ( "groupid" ) : null ; let aGid = afterTab ? afterTab . getAttribute ( "groupid" ) : null ; const cleanupCallbacks = [ ] as Array < ( ( ) => void ) > ; if ( aGid && aGid === bGid && afterTab && afterTab . hasAttribute ( "groupcollapsed" ) ) { for ( const t of tk . getGroupFromTab ( afterTab ) ) { if ( ! t . hidden ) { if ( t . _tPos < afterTab . _tPos ) { beforeTab = afterTab ; while ( beforeTab . nextSibling && beforeTab . nextSibling . getAttribute ( "groupid" ) === aGid ) beforeTab = beforeTab . nextSibling ; afterTab = beforeTab . nextSibling ; newIndex = beforeTab . _tPos + 1 ; } else { afterTab = ( beforeTab as any ) ; while ( afterTab . previousSibling && afterTab . previousSibling . getAttribute ( "groupid" ) === bGid ) afterTab = afterTab . previousSibling ; beforeTab = afterTab . previousSibling ; newIndex = afterTab . _tPos ; } bGid = beforeTab ? beforeTab . getAttribute ( "groupid" ) : null ; aGid = afterTab ? afterTab . getAttribute ( "groupid" ) : null ; break ; } } } let tabs = [ ] as Tab [ ] ; let shiftDragSubtree = false ; const tabIsCopied = ( dropEffect === "copy" ) ; const tabIsFromAnotherWindow = ( draggedTab . parentNode !== gBrowser . tabContainer ) ; if ( draggedGid && ( event . shiftKey && _prefs . getBoolPref ( "shiftDragGroups" ) || draggedTab . hasAttribute ( "groupcollapsed" ) ) ) { shiftDragSubtree = _prefs . getBoolPref ( "shiftDragSubtrees" ) && ! draggedTab . hasAttribute ( "groupcollapsed" ) && ! tabIsFromAnotherWindow ; if ( shiftDragSubtree ) { tabs = tk . getSubtreeFromTab ( draggedTab ) ; if ( tabs . length < 2 ) shiftDragSubtree = false ; } else { tabs = tk . getGroupFromTab ( draggedTab ) ; tk . updateIndents ( tabs ) ; } } else { tabs = [ draggedTab ] ; } const singleTab = ( tabs . length === 1 ) ; const tabsReverse = tabs . slice ( ) ; tabsReverse . reverse ( ) ; const newTabs = [ ] as Tab [ ] ; const tabIdMapping = { } as { [ key ] : string ; } ; const should_return_early = tabsReverse . some ( function ( tab ) { if ( tabIsCopied || tabIsFromAnotherWindow ) { const added_tab_type = singleTab && draggedTab === beforeTab ? "related" : "unrelated" ; const parent_tab = singleTab && draggedTab === beforeTab ? draggedTab : null ; const should_keep_added_tab_position = true ; tk . addingTab ( { added_tab_type : added_tab_type , parent_tab : parent_tab , should_keep_added_tab_position : should_keep_added_tab_position , } ) ; const copiedTab = tk . _duplicateTab ( tab ) ; gBrowser . moveTabTo ( copiedTab , newIndex ) ; newTabs . unshift ( copiedTab ) ; tk . addingTabOver ( { added_tab_type : added_tab_type , added_tab : copiedTab , parent_tab : parent_tab , should_keep_added_tab_position : should_keep_added_tab_position , } ) ; const tab_attributes_backup = tk . getTabAttributesForTabKit ( copiedTab ) ; if ( typeof TabStateFlusher === "object" && "flush" in TabStateFlusher && typeof TabStateFlusher . flush === "function" ) { const browser = tab . linkedBrowser ; TabStateFlusher . flush ( browser ) . then ( ( ) => { tk . setTabAttributesForTabKit ( copiedTab , tab_attributes_backup ) ; } ) ; } if ( tabIsFromAnotherWindow && ! tabIsCopied ) { const draggedTabWindow = draggedTab . ownerDocument . defaultView ; if ( draggedGid ) cleanupCallbacks . push ( function ( ) { draggedTabWindow . tabkit . closeGroup ( draggedTab ) ; } ) ; else cleanupCallbacks . push ( function ( ) { draggedTabWindow . gBrowser . removeTab ( draggedTab ) ; } ) ; } if ( singleTab && draggedTab === beforeTab ) { return true ; } } else { if ( tab . _tPos < newIndex ) newIndex -- ; gBrowser . moveTabTo ( tab , newIndex ) ; newTabs . unshift ( tab ) ; } newTabs [ 0 ] . originalTreeLevel = singleTab ? 0 : tab . treeLevel ; return false ; } ) ; if ( should_return_early ) { return ; } event . preventDefault ( ) ; event . stopPropagation ( ) ; if ( tabIsCopied || tabIsFromAnotherWindow ) gBrowser . selectedTab = newTabs [ 0 ] ; window . setTimeout ( function ___onDropCallback ( ) { if ( tabIsCopied || tabIsFromAnotherWindow ) for ( let i = 0 ; i < newTabs . length ; i ++ ) tabIdMapping [ tk . getTabId ( tabs [ i ] ) ] = tk . getTabId ( newTabs [ i ] ) ; let newGid = null as null | string ; let app = null as null | string ; const draggedIntoGroup = ( aGid && aGid === bGid ) ; const draggedIntoSameGroup = ( draggedGid && ( aGid === draggedGid || bGid === draggedGid ) ) ; if ( draggedIntoGroup || draggedIntoSameGroup ) { if ( draggedIntoSameGroup ) newGid = draggedGid ; else newGid = aGid ; if ( aGid ) { app = afterTab . getAttribute ( "possibleparent" ) ; const parent = tk . getTabById ( app ) ; if ( ! parent || parent . getAttribute ( "groupid" ) !== aGid ) app = null ; } } else if ( singleTab ) { if ( newTabs [ 0 ] . hasAttribute ( "groupid" ) ) tk . removeGID ( newTabs [ 0 ] ) ; } else if ( tabIsCopied || tabIsFromAnotherWindow ) { newGid = ":oG-copiedGroupOrSubtree-" + tk . generateId ( ) ; } else { if ( shiftDragSubtree ) newGid = ":oG-draggedSubtree-" + tk . generateId ( ) ; } for ( let i = 0 ; i < newTabs . length ; i ++ ) { const newTab = newTabs [ i ] ; if ( newGid ) { tk . setGID ( newTab , newGid ) ; if ( draggedIntoGroup ) newTab . setAttribute ( "outoforder" , "true" ) ; } if ( app && newTab . originalTreeLevel <= newTabs [ 0 ] . originalTreeLevel ) { newTab . setAttribute ( "possibleparent" , app ) ; } else if ( ( tabIsCopied || tabIsFromAnotherWindow ) && ! singleTab ) { let tpp = tabs [ i ] . getAttribute ( "possibleparent" ) ; if ( tpp in tabIdMapping ) tpp = tabIdMapping [ tpp ] ; newTab . setAttribute ( "possibleparent" , tpp ) ; } delete newTab . originalTreeLevel ; } tk . updateIndents ( ) ; if ( singleTab ) { if ( draggedGid ) { if ( dPrev && dPrev . getAttribute ( "groupid" ) === draggedGid && ( ! dPrev . previousSibling || dPrev . previousSibling . getAttribute ( "groupid" ) !== draggedGid ) && ( ! dPrev . nextSibling || dPrev . nextSibling . getAttribute ( "groupid" ) !== draggedGid ) ) { tk . removeGID ( dPrev , true ) ; } else if ( dNext && dNext . getAttribute ( "groupid" ) === draggedGid && ( ! dNext . previousSibling || dNext . previousSibling . getAttribute ( "groupid" ) !== draggedGid ) && ( ! dNext . nextSibling || dNext . nextSibling . getAttribute ( "groupid" ) !== draggedGid ) ) { tk . removeGID ( dNext , true ) ; } } } else if ( ! ( tabIsCopied || tabIsFromAnotherWindow ) ) { if ( shiftDragSubtree ) { const group = tk . getGroupById ( draggedGid ) ; if ( group . length === 1 ) tk . removeGID ( group [ 0 ] , true ) ; } } for ( let i = 0 ; i < cleanupCallbacks . length ; i ++ ) { const cleanupCallback = cleanupCallbacks [ i ] ; if ( typeof cleanupCallback === "function" ) cleanupCallback ( ) ; } cleanupCallbacks . length = 0 ; } , 200 ) ; } ; this . preInitTabDragModifications = function preInitTabDragModifications ( ) { gBrowser . tabContainer . addEventListener ( "dragover" , function ( event ) { const ind = this . _tabDropIndicator . parentNode ; ind . style . position = "fixed" ; ind . style . zIndex = 100 ; const newIndex = tk . _getDropIndex ( event ) ; const targetTab = this . childNodes [ newIndex < this . childNodes . length ? newIndex : newIndex - 1 ] ; const isVertical = tk . TabBar . Mode . getIsVerticalMode ( ) ; if ( typeof targetTab === "undefined" || ! targetTab ) { return ; } if ( isVertical ) { if ( event . screenY <= targetTab . boxObject . screenY + targetTab . boxObject . height / 2 ) { ind . style . top = targetTab . getBoundingClientRect ( ) . top - targetTab . boxObject . height + "px" ; } else { ind . style . top = targetTab . getBoundingClientRect ( ) . top + "px" ; } } else { ind . style . left = targetTab . getBoundingClientRect ( ) . left + "px" ; ind . style . top = targetTab . getBoundingClientRect ( ) . top + "px" ; } ind . style . lineHeight = targetTab . getBoundingClientRect ( ) . height + "px" ; ind . firstChild . style . verticalAlign = "bottom" ; this . _tabDropIndicator . collapsed = false ; event . preventDefault ( ) ; event . stopPropagation ( ) ; } , true ) ; gBrowser . tabContainer . addEventListener ( "dragexit" , function ( ) { this . _tabDropIndicator . collapsed = true ; } , true ) ; gBrowser . tabContainer . addEventListener ( "dragend" , function ( ) { this . _tabDropIndicator . collapsed = true ; } , true ) ; gBrowser . tabContainer . addEventListener ( "drop" , tk . _onDrop , true ) ; } ; this . preInitListeners . push ( this . preInitTabDragModifications ) ; this . postInitTabDragModifications = function postInitTabDragModifications ( ) { } ; this . postInitListeners . push ( this . postInitTabDragModifications ) ; this . _getDropIndex = function _getDropIndex ( event ) { const targetTab = event . target . localName === "tab" ? ( event . target as Tab ) : null ; if ( ! targetTab || targetTab . hasAttribute ( "pinned" ) ) return ; const dt = ( event as any ) . dataTransfer ; const draggedTab = dt . mozGetDataAt ( TAB_DROP_TYPE , 0 ) ; if ( ! draggedTab || draggedTab === targetTab || draggedTab . hasAttribute ( "pinned" ) ) return ; const isVertical = tk . TabBar . Mode . getIsVerticalMode ( ) ; let resultIndex ; const targetPos = targetTab . _tPos ; if ( isVertical ) { if ( event . screenY <= targetTab . boxObject . screenY + targetTab . boxObject . height / 2 ) { resultIndex = targetPos ; } else { resultIndex = targetPos + 1 ; } } else { if ( event . screenX <= targetTab . boxObject . screenX + targetTab . boxObject . width / 2 ) { resultIndex = targetPos ; } else { resultIndex = targetPos + 1 ; } } return resultIndex ; } ; this . initProtectedTabs = function initProtectedTabs ( ) { ( function ( ) { "use strict" ; if ( typeof gBrowser . removeTab !== "function" ) { tk . debug ( "gBrowser.removeTab doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . removeTab ; gBrowser . removeTab = function ( aTab , aParams ? ) { "use strict" ; let result ; if ( tk . getTabIsProtected ( aTab ) ) { tk . beep ( ) ; } else { result = old_func . apply ( this , [ aTab , aParams ] ) ; } return result ; } ; } ) ( ) ; _tabContainer . addEventListener ( "click" , tk . protectedTabs_onClick , true ) ; const tabContextMenu = _tabContainer . contextMenu ; tabContextMenu . addEventListener ( "popupshowing" , tk . protectedTabs_updateContextMenu , false ) ; tk . context_closeTab = document . getElementById ( "context_closeTab" ) ; } ; this . initListeners . push ( this . initProtectedTabs ) ; this . postInitProtectedTabs = function postInitProtectedTabs ( ) { if ( _ss ) _ss . persistTabAttribute ( "protected" ) ; } ; this . postInitListeners . push ( this . postInitProtectedTabs ) ; this . protectedTabs_onClick = function protectedTabs_onClick ( event ) { const isCloseButton = ( event . originalTarget . className . indexOf ( "tab-close-button" ) > - 1 ) ; const isMouseClick = ( event . button === 0 || event . button === 1 ) ; const isProtectedTab = ( ( event . target . localName === "tab" ) && event . target . getAttribute ( "protected" ) === "true" ) ; if ( isCloseButton && isMouseClick && isProtectedTab ) { event . target . removeAttribute ( "protected" ) ; event . stopPropagation ( ) ; } } ; this . protectedTabs_updateContextMenu = function protectedTabs_updateContextMenu ( ) { const tab = gBrowser . mContextTab || gBrowser . selectedTab ; const isProtected = tab . getAttribute ( "protected" ) === "true" ; const el_menu_tabkit_tab_toggleProtected = document . getElementById ( "menu_tabkit-tab-toggleProtected" ) ; if ( el_menu_tabkit_tab_toggleProtected != null ) { ( document . getElementById ( "menu_tabkit-tab-toggleProtected" ) as any ) . setAttribute ( "checked" , isProtected ) ; } if ( tk . context_closeTab != null ) { tk . context_closeTab . setAttribute ( "disabled" , isProtected . toString ( ) ) ; } } ; this . getTabIsProtected = function getTabIsProtected ( contextTab ) { if ( ! contextTab ) { contextTab = gBrowser . selectedTab ; } return ( contextTab . getAttribute ( "protected" ) === "true" ) ; } ; this . toggleProtected = function toggleProtected ( contextTab ) { if ( ! contextTab ) { contextTab = gBrowser . selectedTab ; } if ( tk . getTabIsProtected ( contextTab ) ) { tk . setTabUnprotected ( contextTab ) ; } else { tk . setTabProtected ( contextTab ) ; } } ; this . setTabProtected = function setTabProtected ( contextTab ) { if ( ! contextTab ) { contextTab = gBrowser . selectedTab ; } if ( tk . getTabIsProtected ( contextTab ) ) { return false ; } else { contextTab . setAttribute ( "protected" , "true" ) ; return true ; } } ; this . setTabUnprotected = function setTabUnprotected ( contextTab ) { if ( ! contextTab ) { contextTab = gBrowser . selectedTab ; } if ( tk . getTabIsProtected ( contextTab ) ) { contextTab . removeAttribute ( "protected" ) ; return true ; } else { return false ; } } ; this . postInitNewTabsByDefault = function postInitNewTabsByDefault ( ) { ( function ( ) { "use strict" ; if ( ! ( "PlacesUIUtils" in window ) || typeof window . PlacesUIUtils . _openNodeIn !== "function" ) { tk . debug ( "PlacesUIUtils._openNodeIn doesn't exists, replacing function failed" ) ; return ; } const old_func = window . PlacesUIUtils . _openNodeIn ; if ( tk . TKData . getDataWithKey ( old_func , "patched" ) . data ) { return ; } window . PlacesUIUtils . _openNodeIn = function ( aNode , aWhere , aWindow , aPrivate = false ) { let result ; const browserWindow = aWindow && aWindow . document . documentElement . getAttribute ( "windowtype" ) === "navigator:browser" ? aWindow : RecentWindow . getMostRecentBrowserWindow ( ) ; const selected_tab_before_operation = browserWindow . gBrowser . selectedTab ; browserWindow . tabkit . debug ( ">>> PlacesUIUtils._openNodeIn >>>" ) ; browserWindow . tabkit . addingTab ( { added_tab_type : "bookmark" , parent_tab : selected_tab_before_operation , } ) ; aWhere = browserWindow . tabkit . returnWhereWhenOpenPlaces ( aWhere , aNode ) ; try { result = old_func . apply ( this , [ aNode , aWhere , aWindow , aPrivate ] ) ; } finally { browserWindow . tabkit . addingTabOver ( { added_tab_type : "bookmark" , parent_tab : selected_tab_before_operation , } ) ; } browserWindow . tabkit . debug ( "<<< PlacesUIUtils._openNodeIn <<<" ) ; return result ; } ; tk . TKData . setDataWithKey ( window . PlacesUIUtils . _openNodeIn , "patched" , true ) ; } ) ( ) ; ( function ( ) { "use strict" ; if ( ! ( "PlacesUIUtils" in window ) || typeof window . PlacesUIUtils . _openTabset !== "function" ) { tk . debug ( "PlacesUIUtils._openTabset doesn't exists, replacing function failed" ) ; return ; } const old_func = window . PlacesUIUtils . _openTabset ; if ( tk . TKData . getDataWithKey ( old_func , "patched" ) . data ) { return ; } window . PlacesUIUtils . _openTabset = function ( aItemsToOpen , aEvent , aWindow ) { "use strict" ; const browserWindow = aWindow && aWindow . document . documentElement . getAttribute ( "windowtype" ) === "navigator:browser" ? aWindow : RecentWindow . getMostRecentBrowserWindow ( ) ; browserWindow . tabkit . debug ( ">>> PlacesUIUtils._openTabset >>>" ) ; browserWindow . tabkit . isBookmarkGroup = true ; const result = old_func . apply ( this , [ aItemsToOpen , aEvent , aWindow ] ) ; browserWindow . tabkit . debug ( "<<< PlacesUIUtils._openTabset <<<" ) ; return result ; } ; tk . TKData . setDataWithKey ( window . PlacesUIUtils . _openTabset , "patched" , true ) ; } ) ( ) ; } ; this . postInitListeners . push ( this . postInitNewTabsByDefault ) ; this . returnWhereWhenOpenPlaces = function returnWhereWhenOpenPlaces ( aWhere , aNode ) { if ( ! gPrefService . getBoolPref ( "extensions.tabkit.openTabsFrom.places" ) ) { return aWhere ; } if ( aNode . uri . indexOf ( "javascript:" ) === 0 ) { return aWhere ; } if ( ( aWhere === "tab" ) || ( aWhere === "tabshifted" ) ) { return "current" ; } if ( aWhere === "current" ) { return "tab" ; } return aWhere ; } ; this . initTabMinWidth = function initTabMinWidth ( ) { tk . addGlobalPrefListener ( "extensions.tabkit.tabs.tabMinWidth" , tk . resetTabMinWidth ) ; const ss = document . styleSheets ; for ( let i = ss . length - 1 ; i >= 0 ; i -- ) { if ( ss [ i ] . href === "chrome://tabkit/content/variable.css" ) { tk . tabWidthStyleSheet = ss [ i ] ; break ; } } } ; this . initListeners . push ( this . initTabMinWidth ) ; this . resetTabMinWidth = function resetTabMinWidth ( ) { tk . setTabMinWidth ( gPrefService . getIntPref ( "extensions.tabkit.tabs.tabMinWidth" ) ) ; } ; this . setTabMinWidth = function setTabMinWidth ( minWidth ) { minWidth = Math . max ( minWidth , TAB_MIN_WIDTH ) ; for ( let i = 0 ; i < _tabs . length ; i ++ ) { _tabs [ i ] . minWidth = minWidth ; const style = ( tk . tabWidthStyleSheet as any ) . cssRules [ 1 ] . style ; style . setProperty ( "min-width" , minWidth + "px" , "important" ) ; } _tabContainer . adjustTabstrip ( ) ; } ; this . Positions = { TOP : 0 , LEFT : 1 , RIGHT : 2 , BOTTOM : 3 , } ; this . initTabbarPosition = function initTabbarPosition ( ) { const tabs_toolbar = document . getElementById ( "TabsToolbar" ) ; tk . moveSidebar ( ) ; tk . addPrefListener ( "tabbarPosition" , tk . moveSidebar ) ; tk . moveTabbar ( ) ; tk . addPrefListener ( "tabbarPosition" , tk . moveTabbar ) ; _tabContainer . addEventListener ( "TabOpen" , tk . positionedTabbar_onTabOpen , false ) ; _tabContainer . addEventListener ( "TabSelect" , tk . positionedTabbar_onTabSelect , false ) ; _tabContainer . addEventListener ( "TabMove" , tk . positionedTabbar_onTabSelect , false ) ; _tabBar . tkLastMouseover = Date . now ( ) ; gBrowser . tabContainer . addEventListener ( "mouseover" , tk . positionedTabbar_onMouseover , false ) ; gBrowser . tabContainer . addEventListener ( "mouseout" , tk . positionedTabbar_onMouseout , false ) ; if ( typeof MutationObserver === "function" ) { const tabs_toolbar_mutation_observer = new MutationObserver ( function ( mutations ) { mutations . forEach ( function ( mutation ) { if ( mutation . type !== "attributes" ) { return ; } if ( mutation . attributeName !== "collapsed" ) { return ; } tk . positionedTabbar_onToggleCollapse ( { tabs_toolbar : tabs_toolbar } ) ; } ) ; } ) ; tabs_toolbar_mutation_observer . observe ( tabs_toolbar , { attributes : true , attributeFilter : [ "collapsed" ] , attributeOldValue : true , } ) ; } _tabContainer . addEventListener ( "TabClose" , tk . bug608589workaround , true ) ; } ; this . initListeners . push ( this . initTabbarPosition ) ; this . TabBar = this . TabBar || { } ; this . TabBar . Callbacks = this . TabBar . Callbacks || { } ; this . bug608589workaround = function bug608589workaround ( ) { if ( gPrefService . getBoolPref ( "browser.tabs.animate" ) ) gPrefService . setBoolPref ( "browser.tabs.animate" , false ) ; } ; this . positionedTabbar_onTabOpen = function positionedTabbar_onTabOpen ( event ) { const tab = event . target as Tab ; if ( tk . TabBar . Mode . getIsVerticalMode ( ) && document . getElementById ( "tabkit-splitter" ) ) { tab . maxWidth = 9999 ; tab . minWidth = 0 ; window . setTimeout ( function ( ) { tk . scrollToElement ( ( document as mozDocument ) . getAnonymousElementByAttribute ( gBrowser . tabContainer . mTabstrip . _scrollbox , "class" , "box-inherit scrollbox-innerbox" ) , tab ) ; } , 0 ) ; } } ; this . positionedTabbar_onTabSelect = function positionedTabbar_onTabSelect ( ) { const tab = gBrowser . selectedTab ; if ( tk . TabBar . Mode . getIsVerticalMode ( ) ) { if ( tab . previousSibling != null ) { tab . previousSibling . removeAttribute ( "beforeselected" ) ; } if ( tab . nextSibling != null ) { tab . nextSibling . removeAttribute ( "afterselected" ) ; } window . setTimeout ( function ( ) { tk . scrollToElement ( ( document as mozDocument ) . getAnonymousElementByAttribute ( gBrowser . tabContainer . mTabstrip . _scrollbox , "class" , "box-inherit scrollbox-innerbox" ) , tab ) ; } , 50 ) ; } tk . colorizeTab ( tab ) ; } ; this . positionedTabbar_onResize = function positionedTabbar_onResize ( ) { const width = parseInt ( _tabBar . width !== "" ? _tabBar . width : 250 ) ; _prefs . setIntPref ( "tabSidebarWidth" , Math . min ( width , 576 ) ) ; } ; this . positionedTabbar_onMouseover = function positionedTabbar_onMouseover ( ) { const splitter = document . getElementById ( "tabkit-splitter" ) ; if ( ! splitter || splitter . getAttribute ( "state" ) !== "collapsed" ) return ; _tabBar . tkLastMouseover = ( _tabBar . tkLastMouseover || 0 ) + 1 ; if ( _tabBar . collapsed ) { _tabBar . width = Math . max ( 200 , parseInt ( _tabBar . width ) ) ; _tabBar . collapsed = false ; } } ; this . positionedTabbar_onMouseout = function positionedTabbar_onMouseout ( ) { const splitter = document . getElementById ( "tabkit-splitter" ) ; if ( ! splitter || splitter . getAttribute ( "state" ) !== "collapsed" ) return ; window . clearTimeout ( tk . positionedTabbar_collapseTimer ) ; tk . positionedTabbar_collapseTimer = window . setTimeout ( function __collapse ( lastMouseover ) { if ( splitter . getAttribute ( "state" ) === "collapsed" && lastMouseover === _tabBar . tkLastMouseover ) { _tabBar . collapsed = true ; } } , 333 , _tabBar . tkLastMouseover ) ; } ; this . positionedTabbar_onToggleCollapse = function positionedTabbar_onToggleCollapse ( options = { } ) { if ( typeof options !== "object" ) { options = { } ; } const tabs_toolbar = options . tabs_toolbar ; if ( tabs_toolbar == null ) { return ; } const tabs_toolbar_collapsed = tabs_toolbar . getAttribute ( "collapsed" ) === "true" ; const scrollbar = tk . VerticalTabBarScrollbar . getElement ( ) ; if ( scrollbar == null ) { return ; } if ( tabs_toolbar_collapsed ) { tk . backupScrollBarPosition ( ) ; } else { window . setTimeout ( function ( ) { tk . restoreScrollBarPosition ( ) ; } , 100 ) ; } } ; this . backupScrollBarPosition = function ( ) { "use strict" ; const scrollbar = tk . VerticalTabBarScrollbar . getElement ( ) ; const curpos = parseInt ( scrollbar . getAttribute ( "curpos" ) ) ; if ( ! isNaN ( curpos ) && curpos > 0 ) { tk . TKData . setDataWithKey ( _tabBar , "scroll_bar_position" , curpos ) ; } } ; this . restoreScrollBarPosition = function ( ) { "use strict" ; const scrollbar = tk . VerticalTabBarScrollbar . getElement ( ) ; const curpos = tk . TKData . getDataWithKey ( _tabBar , "scroll_bar_position" ) . data ; if ( scrollbar != null && ! isNaN ( curpos ) && curpos > 0 ) { scrollbar . setAttribute ( "curpos" , curpos ) ; } } ; this . moveSidebar = function moveSidebar ( tabbarPosition ? ) { if ( typeof tabbarPosition !== "number" ) tabbarPosition = _prefs . getIntPref ( "tabbarPosition" ) ; let flip_direction = false ; if ( tabbarPosition === tk . Positions . LEFT ) { flip_direction = true ; } const do_not_move_sidebar = "AiOS_HELPER" in window ; if ( do_not_move_sidebar ) { return ; } const need_move_sidebar = ( ( tabbarPosition === tk . Positions . LEFT ) || ( tabbarPosition === tk . Positions . RIGHT ) ) ; const fromHorizontal = "horizontal" ; const fromVertical = "vertical" ; const fromNormal = "normal" ; const browser_border_start = document . getElementById ( "browser-border-start" ) ; const browser = document . getElementById ( "browser" ) ; if ( browser == null ) { return ; } const sidebar_box = document . getElementById ( "sidebar-box" ) ; const sidebar_splitter = document . getElementById ( "sidebar-splitter" ) ; if ( sidebar_box == null ) { return ; } const sidebar_header = ( sidebar_box . getElementsByTagName ( "sidebarheader" ) [ 0 ] as any ) ; const appcontent = document . getElementById ( "appcontent" ) ; const normallyHorizontal = [ browser , sidebar_splitter , sidebar_header , ] ; const normallyVertical = [ sidebar_box , appcontent , ] ; const normallyNormal = [ sidebar_box , sidebar_header , ] ; const new_direction = flip_direction ? "reverse" : "normal" ; if ( need_move_sidebar ) { tk . DomUtility . insertAfter ( appcontent , sidebar_box ) ; tk . DomUtility . insertBefore ( sidebar_box , sidebar_splitter ) ; } else { tk . DomUtility . insertAfter ( browser_border_start , sidebar_box ) ; tk . DomUtility . insertAfter ( sidebar_box , sidebar_splitter ) ; } normallyNormal . forEach ( function ( node ) { node . dir = fromNormal ; } ) ; sidebar_header . pack = "start" ; normallyHorizontal . forEach ( function ( node ) { node . orient = fromHorizontal ; } ) ; normallyVertical . forEach ( function ( node ) { ( node as any ) . orient = fromVertical ; } ) ; browser . removeAttribute ( "horizsidebar" ) ; browser . removeAttribute ( "vertisidebar" ) ; browser . setAttribute ( fromVertical . substring ( 0 , 5 ) + "sidebar" , new_direction ) ; } ; this . moveTabbar = function moveTabbar ( pos ? ) { if ( typeof pos !== "number" ) pos = _prefs . getIntPref ( "tabbarPosition" ) ; const tabsToolbar = document . getElementById ( "TabsToolbar" ) ; const appcontent = document . getElementById ( "appcontent" ) ; const browser = document . getElementById ( "browser" ) ; if ( appcontent == null || tabsToolbar == null ) { return ; } const flipOrient = ( pos === tk . Positions . LEFT || pos === tk . Positions . RIGHT ) ; const fromHorizontal = flipOrient ? "vertical" : "horizontal" ; const flipDirection = ( pos === tk . Positions . RIGHT || pos === tk . Positions . BOTTOM ) ; const newDirection = flipDirection ? "normal" : "reverse" ; if ( flipOrient ) { ( appcontent . parentNode as any ) . insertBefore ( tabsToolbar , appcontent ) ; } else if ( pos === tk . Positions . TOP ) { const menubar = document . getElementById ( "toolbar-menubar" ) ; if ( menubar == null ) { return ; } ( menubar . parentNode as any ) . insertBefore ( tabsToolbar , menubar ) ; } else if ( pos === tk . Positions . BOTTOM ) { const a = document . getElementById ( "browser-bottombox" ) ; ( a as any ) . parentNode . insertBefore ( tabsToolbar , a ) ; } ( browser as any ) . dir = ( newDirection as any ) ; ( tabsToolbar as any ) . orient = _tabContainer . mTabstrip . orient = fromHorizontal ; gBrowser . removeAttribute ( "horiztabbar" ) ; gBrowser . removeAttribute ( "vertitabbar" ) ; gBrowser . setAttribute ( fromHorizontal . substring ( 0 , 5 ) + "tabbar" , newDirection ) ; tabsToolbar . removeAttribute ( "horiztabbar" ) ; tabsToolbar . removeAttribute ( "vertitabbar" ) ; tabsToolbar . setAttribute ( fromHorizontal . substring ( 0 , 5 ) + "tabbar" , newDirection ) ; const mainPopupSet = document . getElementById ( "mainPopupSet" ) ; if ( mainPopupSet == null ) { return ; } mainPopupSet . removeAttribute ( "horiztabbar" ) ; mainPopupSet . removeAttribute ( "vertitabbar" ) ; mainPopupSet . setAttribute ( fromHorizontal . substring ( 0 , 5 ) + "tabbar" , newDirection ) ; let splitter = document . getElementById ( "tabkit-splitter" ) as Element | null ; if ( flipOrient ) { for ( let i = 0 ; i < tabsToolbar . children . length ; i ++ ) { const thisNode = tabsToolbar . children . item ( i ) ; if ( thisNode . localName === "toolbarspacer" || thisNode . localName === "toolbarspring" ) { ( thisNode . parentNode as any ) . removeChild ( thisNode ) ; i -- ; } } if ( ! splitter ) { splitter = document . createElementNS ( XUL_NS , "splitter" ) ; splitter . id = "tabkit-splitter" ; splitter . setAttribute ( "collapse" , "before" ) ; const grippy = document . createElementNS ( XUL_NS , "grippy" ) ; grippy . id = "tabkit-grippy" ; splitter . appendChild ( grippy ) ; splitter . addEventListener ( "mouseover" , tk . positionedTabbar_onMouseover , false ) ; splitter . addEventListener ( "mouseout" , tk . positionedTabbar_onMouseout , false ) ; _tabBar . width = _prefs . getIntPref ( "tabSidebarWidth" ) ; for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . maxWidth = 9999 ; tk . setTabMinWidth ( 0 ) ; gBrowser . mTabBox . addEventListener ( "resize" , tk . positionedTabbar_onResize , false ) ; } tk . DomUtility . insertBefore ( appcontent , splitter ) ; if ( "toggleIndentedTree" in tk ) { tk . toggleIndentedTree ( ) ; } } else { if ( "toggleIndentedTree" in tk ) { tk . toggleIndentedTree ( ) ; } if ( splitter ) { gBrowser . mTabBox . removeEventListener ( "resize" , tk . positionedTabbar_onResize , false ) ; for ( let i = 0 ; i < _tabs . length ; i ++ ) _tabs [ i ] . maxWidth = 250 ; tk . resetTabMinWidth ( ) ; ( appcontent . parentNode as any ) . removeChild ( splitter ) ; } } tk . positionedTabbar_onTabSelect ( ) ; } ; this . initMultiRowTabs = function initMultiRowTabs ( ) { _tabContainer . mTabstrip . addEventListener ( "overflow" , tk . _preventMultiRowFlowEvent , true ) ; _tabContainer . mTabstrip . addEventListener ( "underflow" , tk . _preventMultiRowFlowEvent , true ) ; tk . addPrefListener ( "tabRows" , tk . updateMultiRowTabs ) ; tk . addPrefListener ( "tabbarPosition" , tk . updateMultiRowTabs ) ; tk . addGlobalPrefListener ( "extensions.tabkit.tabs.tabMinWidth" , tk . updateMultiRowTabs ) ; tk . addGlobalPrefListener ( "extensions.tabkit.tabs.closeButtons" , tk . updateMultiRowTabs ) ; _tabContainer . addEventListener ( "TabOpen" , tk . updateMultiRowTabs , false ) ; tk . addDelayedEventListener ( _tabContainer , "TabClose" , tk . updateMultiRowTabs ) ; document . addEventListener ( "SSTabRestoring" , tk . updateMultiRowTabs , false ) ; window . addEventListener ( "resize" , tk . updateMultiRowTabs , false ) ; _tabContainer . addEventListener ( "TabSelect" , tk . multiRow_onTabSelect , false ) ; _tabContainer . addEventListener ( "TabMove" , tk . multiRow_onTabSelect , false ) ; tk . updateMultiRowTabs ( ) ; } ; this . initListeners . push ( this . initMultiRowTabs ) ; this . updateMultiRowTabs = function updateMultiRowTabs ( ) { const tabbarPosition = _prefs . getIntPref ( "tabbarPosition" ) ; let needsDisabling = false ; let availWidth = 0 ; let rows = 0 ; let minWidth = null as null | number ; let needsScrolling = false ; const closeButtonsVisibilityPrefVal = gPrefService . getIntPref ( "extensions.tabkit.tabs.closeButtons" ) ; if ( closeButtonsVisibilityPrefVal === 0 ) { gBrowser . tabContainer . setAttribute ( "tabkit-close-button-visibility" , "active-tab-only" ) ; } else if ( closeButtonsVisibilityPrefVal === 2 ) { gBrowser . tabContainer . setAttribute ( "tabkit-close-button-visibility" , "none" ) ; } else { gBrowser . tabContainer . removeAttribute ( "tabkit-close-button-visibility" ) ; } if ( ( tabbarPosition === tk . Positions . TOP || tabbarPosition === tk . Positions . BOTTOM ) && _prefs . getIntPref ( "tabRows" ) > 1 ) { if ( ! gBrowser . getStripVisibility ( ) ) { rows = 0 ; } else { let visibleTabs = _tabs . length ; for ( let i = 0 ; i < _tabs . length ; i ++ ) if ( _tabs [ i ] . hidden ) visibleTabs -- ; minWidth = _prefs . getIntPref ( "tabs.tabMinWidth" ) ; availWidth = _tabContainer . mTabstrip . _scrollbox . boxObject . width ; const tabsPerRow = Math . floor ( availWidth / Math . max ( minWidth , TAB_MIN_WIDTH ) ) ; rows = Math . ceil ( visibleTabs / tabsPerRow ) ; } if ( rows > 1 ) { if ( _tabContainer . getAttribute ( "multirow" ) !== "true" ) { _tabContainer . setAttribute ( "multirow" , "true" ) ; try { _tabContainer . mTabstrip . _scrollBoxObject . scrollTo ( 0 , 0 ) ; } catch ( ex ) { } } const maxRows = _prefs . getIntPref ( "tabRows" ) ; if ( rows > maxRows ) { _tabContainer . setAttribute ( "multirowscroll" , "true" ) ; _tabContainer . mTabstrip . style . setProperty ( "min-height" , 24 * maxRows + "px" , "important" ) ; _tabContainer . mTabstrip . style . setProperty ( "max-height" , 24 * maxRows + "px" , "important" ) ; const scrollbar = tk . VerticalTabBarScrollbar . getElement ( ) ; try { availWidth -= Math . max ( scrollbar . boxObject . width , 22 ) ; } catch ( ex ) { tk . debug ( "Oops, the scrollbar hasn't been created yet... TODO-P6: TJS use a timeout" ) ; availWidth -= 22 ; } } else { _tabContainer . removeAttribute ( "multirowscroll" ) ; _tabContainer . mTabstrip . style . setProperty ( "min-height" , 24 * rows + "px" , "important" ) ; _tabContainer . mTabstrip . style . setProperty ( "max-height" , 24 * rows + "px" , "important" ) ; } tk . setTabMinWidth ( minWidth ) ; if ( rows > maxRows ) tk . multiRow_onTabSelect ( ) ; } else { if ( _tabContainer . getAttribute ( "multirow" ) === "true" ) needsDisabling = true ; needsScrolling = true ; _tabContainer . setAttribute ( "multirow" , "false" ) ; } } else if ( _tabContainer . hasAttribute ( "multirow" ) ) { needsDisabling = true ; needsScrolling = ( _tabContainer . getAttribute ( "multirow" ) === "true" ) ; _tabContainer . removeAttribute ( "multirow" ) ; } if ( needsDisabling ) { tk . resetTabMinWidth ( ) ; if ( needsScrolling ) { try { if ( gBrowser . selectedTab . nextSibling && _prefs . getBoolPref ( "scrollOneExtra" ) ) { _tabContainer . mTabstrip . _scrollBoxObject . ensureElementIsVisible ( gBrowser . selectedTab . nextSibling ) ; } _tabContainer . mTabstrip . _scrollBoxObject . ensureElementIsVisible ( gBrowser . selectedTab ) ; } catch ( ex ) { } } _tabContainer . mTabstrip . style . removeProperty ( "min-height" ) ; _tabContainer . mTabstrip . style . removeProperty ( "max-height" ) ; } } ; this . multiRow_onTabSelect = function multiRow_onTabSelect ( ) { if ( _tabContainer . getAttribute ( "multirow" ) === "true" ) { const tab = gBrowser . selectedTab ; tk . scrollToElement ( ( document as mozDocument ) . getAnonymousElementByAttribute ( gBrowser . tabContainer . mTabstrip . _scrollbox , "class" , "box-inherit scrollbox-innerbox" ) , tab ) ; if ( tab . previousSibling != null && tab . boxObject . y !== tab . previousSibling . boxObject . y ) { tab . previousSibling . removeAttribute ( "beforeselected" ) ; } if ( tab . nextSibling != null && tab . boxObject . y !== tab . nextSibling . boxObject . y ) { tab . nextSibling . removeAttribute ( "afterselected" ) ; } } } ; this . _preventMultiRowFlowEvent = function _preventMultiRowFlowEvent ( event ) { if ( _tabContainer . hasAttribute ( "multirow" ) ) { event . preventDefault ( ) ; event . stopPropagation ( ) ; } } ; this . initSearchBar = function initSearchBar ( ) { const strings = ( document . getElementById ( "bundle_tabkit" ) as any ) ; const vbox = document . createElementNS ( XUL_NS , "vbox" ) ; vbox . setAttribute ( "id" , "tabkit-filtertabs-box" ) ; _tabBar . insertBefore ( vbox , _tabBar . firstChild ) ; const textbox = document . createElementNS ( XUL_NS , "textbox" ) ; textbox . setAttribute ( "id" , "tabkit-filtertabs-query" ) ; textbox . setAttribute ( "type" , "search" ) ; textbox . setAttribute ( "emptytext" , strings . getString ( "search_tabs" ) ) ; textbox . setAttribute ( "tooltiptext" , strings . getString ( "search_tabs" ) ) ; textbox . setAttribute ( "clickSelectsAll" , "true" ) ; textbox . setAttribute ( "newlines" , "replacewithspaces" ) ; textbox . addEventListener ( "command" , function ( ) { tk . filterTabs ( this . value ) ; } ) ; textbox . addEventListener ( "input" , function ( ) { const el_tabkit_filtertabs_includetext = document . getElementById ( "tabkit-filtertabs-includetext" ) ; if ( el_tabkit_filtertabs_includetext == null ) { return ; } ( el_tabkit_filtertabs_includetext as any ) . collapsed = ! this . value ; } ) ; textbox . addEventListener ( "blur" , function ( ) { const el_tabkit_filtertabs_includetext = document . getElementById ( "tabkit-filtertabs-includetext" ) ; if ( el_tabkit_filtertabs_includetext == null ) { return ; } ( el_tabkit_filtertabs_includetext as any ) . collapsed = ! this . value ; } ) ; vbox . appendChild ( textbox ) ; const checkbox = document . createElementNS ( XUL_NS , "checkbox" ) ; checkbox . setAttribute ( "id" , "tabkit-filtertabs-includetext" ) ; checkbox . setAttribute ( "label" , strings . getString ( "include_page_text" ) ) ; checkbox . addEventListener ( "command" , function ( ) { tk . filterTabs ( ( document . getElementById ( "tabkit-filtertabs-query" ) as any ) . value ) ; } ) ; checkbox . setAttribute ( "collapsed" , "true" ) ; vbox . appendChild ( checkbox ) ; tk . mapBoolPrefToAttribute ( "disableSearchBar" , document . getElementById ( "tabkit-filtertabs-box" ) , "hidden" ) ; tk . filterTabs ( " " ) ; } ; this . initListeners . push ( this . initSearchBar ) ; this . filterTabs = function filterTabs ( query ) { if ( query && typeof tk . _groupsToReExpandAfterSearch === "undefined" ) { tk . _groupsToReExpandAfterSearch = [ ] ; Object . values ( tk . getAllGroups ( ) ) . forEach ( function ( g ) { if ( g [ 0 ] . hasAttribute ( "groupcollapsed" ) ) { tk . toggleGroupCollapsed ( g [ 0 ] ) ; tk . _groupsToReExpandAfterSearch . push ( g [ 0 ] . getAttribute ( "groupid" ) ) ; } } ) ; } const terms = query . split ( /\s+/g ) ; const el_tabkit_filtertabs_includetext = document . getElementById ( "tabkit-filtertabs-includetext" ) ; if ( el_tabkit_filtertabs_includetext == null ) { return ; } const includePageText = ( el_tabkit_filtertabs_includetext as any ) . checked ; for ( let t = 0 ; t < _tabs . length ; t ++ ) { const tab = _tabs [ t ] ; const foundTerms = { } as { [ key ] : boolean ; } ; let uri = null ; uri = tab . linkedBrowser . currentURI . spec ; try { uri = decodeURI ( uri ) ; } catch ( ex ) { } const title = tab . label ; const details = ( title + " " + uri ) . toLowerCase ( ) ; for ( let i = 0 ; i < terms . length ; i ++ ) { if ( details . indexOf ( terms [ i ] ) > - 1 ) { foundTerms [ i ] = true ; } } let match = true ; for ( let i = 0 ; i < terms . length ; i ++ ) { if ( ! ( i in foundTerms ) ) { match = false ; break ; } } if ( ! match && includePageText ) { const frames = [ ] ; const frameQueue = [ tab . linkedBrowser . contentWindow ] ; while ( frameQueue . length > 0 ) { const f = frameQueue . pop ( ) ; for ( let i = 0 ; i < f . frames . length ; i ++ ) frameQueue . push ( f . frames [ i ] ) ; frames . push ( f ) ; } for ( let i = 0 ; i < frames . length ; i ++ ) { if ( ! frames [ i ] . document || ! frames [ i ] . document . body ) continue ; const body = frames [ i ] . document . body ; const count = body . childNodes . length ; const range = document . createRange ( ) ; range . setStart ( body , 0 ) ; range . setEnd ( body , count ) ; const start = document . createRange ( ) ; start . setStart ( body , 0 ) ; start . setEnd ( body , 0 ) ; const end = document . createRange ( ) ; end . setStart ( body , count ) ; end . setEnd ( body , count ) ; const finder = Cc [ "@mozilla.org/embedcomp/rangefind;1" ] . createInstance ( ) . QueryInterface ( Components . interfaces . nsIFind ) ; finder . caseSensitive = false ; for ( let j = 0 ; j < terms . length ; j ++ ) { if ( ! ( j in foundTerms ) ) { if ( finder . Find ( terms [ j ] , range , start , end ) != null ) foundTerms [ j ] = true ; } } } match = true ; for ( let i = 0 ; i < terms . length ; i ++ ) { if ( ! ( i in foundTerms ) ) { match = false ; break ; } } } tk . tabSetHidden ( tab , ! match ) ; } if ( ! query && typeof tk . _groupsToReExpandAfterSearch === "object" ) { tk . _groupsToReExpandAfterSearch . forEach ( function ( gid ) { const g = tk . getGroupById ( gid ) ; if ( ! g [ 0 ] . hasAttribute ( "groupcollapsed" ) ) { tk . toggleGroupCollapsed ( g [ 0 ] ) ; } } ) ; delete tk . _groupsToReExpandAfterSearch ; } } ; this . initMouseGestures = function initMouseGestures ( ) { gBrowser . tabContainer . addEventListener ( "wheel" , tk . onTabWheelGesture , true ) ; const tabContextMenu = gBrowser . tabContextMenu ; const closeAllTabsButButton = document . getElementById ( "context_closeOtherTabs" ) ; if ( closeAllTabsButButton ) { tabContextMenu . insertBefore ( document . getElementById ( "menu_tabkit-closeTabsToLeft" ) , closeAllTabsButButton ) ; tabContextMenu . insertBefore ( document . getElementById ( "menu_tabkit-closeTabsToRight" ) , closeAllTabsButButton ) ; tabContextMenu . insertBefore ( document . getElementById ( "menu_tabkit-closeTabsAbove" ) , closeAllTabsButButton ) ; tabContextMenu . insertBefore ( document . getElementById ( "menu_tabkit-closeTabsBelow" ) , closeAllTabsButButton ) ; tk . mapBoolPrefToAttribute ( "closeBeforeAfterNotOther" , document . getElementById ( "mainPopupSet" ) , "closebeforeafternotother" ) ; } else tk . dump ( "Could not find removeAllTabsBut" ) ; } ; this . initListeners . push ( this . initMouseGestures ) ; this . onTabWheelGesture = function onTabWheelGesture ( event ) { if ( ! event . isTrusted ) return ; let isUsingTabSheelSwitch = false ; try { isUsingTabSheelSwitch = gPrefService . getBoolPref ( "extensions.tabkit.mouse-gestures.tabWheelSwitchHover" ) ; } catch ( ex ) { } const name = event . originalTarget . localName ; if ( ! isUsingTabSheelSwitch || ( name === "scrollbar" || name === "scrollbarbutton" || name === "slider" || name === "thumb" ) ) { const scrollbar = tk . VerticalTabBarScrollbar . getElement ( ) ; if ( ! scrollbar ) { tk . dump ( "tabInnerBox.mVerticalScrollbar is null - so what scrollbar did we scroll over?!" ) ; return ; } let delta = 0 ; if ( tk . TabBar . Mode . getIsVerticalMode ( ) ) delta = ( Math . abs ( event . deltaY ) !== 1 ? event . deltaY : ( event . deltaY < 0 ? - 3 : 3 ) ) * 24 ; else if ( gBrowser . tabContainer . getAttribute ( "multirow" ) === "true" ) delta = event . deltaY < 0 ? - 48 : 48 ; else return ; let curpos = scrollbar . getAttribute ( "curpos" ) ; curpos = isNaN ( curpos ) ? 0 : Number ( curpos ) ; const maxpos = Number ( scrollbar . getAttribute ( "maxpos" ) ) ; const newpos = Math . min ( maxpos , Math . max ( 0 , curpos + delta ) ) ; scrollbar . setAttribute ( "curpos" , newpos ) ; event . preventDefault ( ) ; event . stopPropagation ( ) ; } } ; this . removeTabsBefore = function removeTabsBefore ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; for ( let i = contextTab . _tPos - 1 ; i >= 0 ; i -- ) gBrowser . removeTab ( _tabs [ i ] ) ; } ; this . removeTabsAfter = function removeTabsAfter ( contextTab ) { if ( ! contextTab ) contextTab = gBrowser . selectedTab ; for ( let i = _tabs . length - 1 ; i > contextTab . _tPos ; i -- ) gBrowser . removeTab ( _tabs [ i ] ) ; } ; let _allTabsInnerBox = null as null | Element ; this . initScrollbarsNotArrows = function initScrollbarsNotArrows ( ) { if ( _prefs . getBoolPref ( "scrollbarsNotArrows" ) ) ( document . documentElement as any ) . setAttribute ( "scrollbarsnotarrows" , "true" ) ; tk . addDelayedEventListener ( _tabContainer . mAllTabsPopup , "popupshowing" , tk . scrollAllTabsMenu ) ; } ; this . initListeners . push ( this . initScrollbarsNotArrows ) ; this . scrollAllTabsMenu = function scrollAllTabsMenu ( ) { if ( ! _allTabsInnerBox ) { const arrowScrollBox = _tabContainer . mAllTabsPopup . popupBoxObject . firstChild ; if ( ! arrowScrollBox ) { tk . dump ( "_tabContainer.mAllTabsPopup.popupBoxObject.firstChild is null" ) ; return ; } _allTabsInnerBox = ( document as mozDocument ) . getAnonymousElementByAttribute ( arrowScrollBox . _scrollbox , "class" , "box-inherit scrollbox-innerbox" ) ; } tk . scrollToElement ( _allTabsInnerBox , gBrowser . selectedTab . mCorrespondingMenuitem ) ; } ; this . initOpenSelectedLinks = function initOpenSelectedLinks ( ) { tk . addDelayedEventListener ( document . getElementById ( "contentAreaContextMenu" ) , "popupshowing" , tk . openSelectedLinks_onPopupShowing ) ; } ; this . initListeners . push ( this . initOpenSelectedLinks ) ; this . openSelectedLinks_onPopupShowing = function openSelectedLinks_onPopupShowing ( ) { const topMenuItem = ( document . getElementById ( "context_tabkit-opentopselectedlinks" ) as any ) ; const menuItem = ( document . getElementById ( "context_tabkit-openselectedlinks" ) as any ) ; const textMenuItem = ( document . getElementById ( "context_tabkit-openselectedtextlinks" ) as any ) ; if ( topMenuItem == null || menuItem == null || textMenuItem == null ) { return ; } topMenuItem . hidden = menuItem . hidden = textMenuItem . hidden = true ; topMenuItem . tabkit_selectedLinks = menuItem . tabkit_selectedLinks = textMenuItem . tabkit_selectedLinks = null ; if ( ! _prefs . getBoolPref ( "openSelectedLinks" ) ) return ; const oneItemOnly = ! _prefs . getBoolPref ( "openSelectedLinks.showAll" ) ; const [ uris , topUris ] = tk . openSelectedLinks_getURIs ( ) ; if ( topUris . length > 0 && topUris . length < uris . length ) { const s = topUris . length > 1 ? "s" : "" ; topMenuItem . setAttribute ( "label" , "Open Main " + topUris . length + " Link" + s + " in New Tab" + s ) ; topMenuItem . tabkit_selectedLinks = topUris ; topMenuItem . tabkit_linkSource = window . content . document . documentURI ; topMenuItem . hidden = false ; if ( oneItemOnly ) return ; } if ( uris . length > 0 ) { const s = uris . length > 1 ? "s" : "" ; const all = topUris . length < uris . length ? "All " : "" ; menuItem . setAttribute ( "label" , "Open " + all + uris . length + " Link" + s + " in New Tab" + s ) ; menuItem . tabkit_selectedLinks = uris ; menuItem . tabkit_linkSource = window . content . document . documentURI ; menuItem . hidden = false ; if ( oneItemOnly ) return ; } const textUris = tk . openSelectedLinks_getTextURIs ( ) ; if ( textUris . length > 0 ) { const s = textUris . length > 1 ? "s" : "" ; textMenuItem . setAttribute ( "label" , "Open " + textUris . length + " Text Link" + s + " in New Tab" + s ) ; textMenuItem . tabkit_selectedLinks = textUris ; textMenuItem . tabkit_linkSource = window . content . document . documentURI ; textMenuItem . hidden = false ; } } ; this . openSelectedLinks_getURIs = function openSelectedLinks_getURIs ( ) { let focusedWindow = ( document as any ) . commandDispatcher . focusedWindow ; if ( focusedWindow === window ) focusedWindow = window . content ; const selection = focusedWindow . getSelection ( ) ; const uris = [ ] , topUris = [ ] ; let largestSize = 0 ; for ( let i = 0 ; i < selection . rangeCount ; i ++ ) { const treeWalker = focusedWindow . document . createTreeWalker ( selection . getRangeAt ( i ) . cloneContents ( ) , NodeFilter . SHOW_ELEMENT , { acceptNode : function ( node ) { return node . localName . toLowerCase ( ) === "a" ? NodeFilter . FILTER_ACCEPT : NodeFilter . FILTER_SKIP ; } , } , true ) ; while ( treeWalker . nextNode ( ) ) { const node = treeWalker . currentNode ; if ( ! node . href || ( node . href . indexOf ( "http" ) !== 0 && node . href . indexOf ( "file" ) !== 0 && node . href . indexOf ( "ftp" ) !== 0 ) ) { continue ; } const uri = node . href ; if ( uris . indexOf ( uri ) === - 1 ) uris . push ( uri ) ; const size = parseInt ( focusedWindow . getComputedStyle ( node , null ) . getPropertyValue ( "font-size" ) ) ; if ( size > largestSize ) { largestSize = size ; topUris . length = 0 ; } else if ( size < largestSize ) continue ; if ( topUris . indexOf ( uri ) === - 1 ) topUris . push ( uri ) ; } } return [ uris , topUris ] ; } ; this . openSelectedLinks_getTextURIs = function openSelectedLinks_getTextURIs ( ) { let focusedWindow = ( document as any ) . commandDispatcher . focusedWindow ; if ( focusedWindow === window ) focusedWindow = window . content ; const selectedText = focusedWindow . getSelection ( ) . toString ( ) ; return tk . detectURIsFromText ( selectedText ) ; } ; this . openSelectedLinks = function openSelectedLinks ( menuItem ) { if ( ! menuItem . tabkit_selectedLinks ) return ; const uris = menuItem . tabkit_selectedLinks . filter ( function __uriSecurityCheck ( uri ) { try { const linkSourceURI = _ios . newURI ( menuItem . tabkit_linkSource , null , null ) ; const linkSourcePrinciple = _sm . getCodebasePrincipal ( linkSourceURI ) ; _sm . checkLoadURIStrWithPrincipal ( linkSourcePrinciple , uri , _sm . STANDARD ) ; return true ; } catch ( ex ) { return false ; } } ) ; const selected_tab_before_operation = gBrowser . selectedTab ; tk . addingTab ( { parent_tab : selected_tab_before_operation , added_tab_type : "related" , } ) ; const first_tab = gBrowser . addTab ( uris . shift ( ) ) ; tk . addingTabOver ( { added_tab : first_tab , added_tab_type : "related" , parent_tab : selected_tab_before_operation , should_keep_added_tab_position : false , } ) ; uris . forEach ( function ( uri ) { tk . addingTab ( { parent_tab : selected_tab_before_operation , added_tab_type : "related" , } ) ; const new_tab = gBrowser . addTab ( uri ) ; tk . addingTabOver ( { added_tab : new_tab , added_tab_type : "related" , parent_tab : selected_tab_before_operation , should_keep_added_tab_position : false , } ) ; } ) ; if ( ! gPrefService . getBoolPref ( "browser.tabs.loadInBackground" ) ) { gBrowser . selectedTab = first_tab ; } } ; this . openClipboardLinks = function openClipboardLinks ( ) { let unicodeString = "" ; try { const transferable = ( ( Transferable as any ) ( ) as any ) ; const clipboard = Components . classes [ "@mozilla.org/widget/clipboard;1" ] . createInstance ( Components . interfaces . nsIClipboard ) ; const unicodeStringObject = { } ; const unicodeStringLengthObject = { } ; transferable . addDataFlavor ( "text/unicode" ) ; clipboard . getData ( transferable , clipboard . kGlobalClipboard ) ; transferable . getTransferData ( "text/unicode" , unicodeStringObject , unicodeStringLengthObject ) ; if ( unicodeStringObject ) { unicodeString = ( unicodeStringObject as any ) . value . QueryInterface ( Components . interfaces . nsISupportsString ) . toString ( ) ; } } catch ( ex ) { return ; } const uris = tk . detectURIsFromText ( unicodeString ) ; tk . debug ( "uris: " ) ; tk . debug ( uris . toString ( ) ) ; if ( uris . length === 0 ) { return ; } const firstTab = gBrowser . addTab ( uris . shift ( ) ) ; let lastTab = firstTab ; uris . forEach ( function ( uri ) { lastTab = gBrowser . addTab ( uri ) ; } ) ; if ( ! gPrefService . getBoolPref ( "browser.tabs.loadInBackground" ) ) { gBrowser . selectedTab = firstTab ; } tk . makeGroupBetweenTwoTabs ( firstTab , lastTab ) ; } ; this . detectURIsFromText = function detectURIsFromText ( textToDetect ) { const uris = [ ] as string [ ] ; if ( textToDetect === "" ) return uris ; const matches = textToDetect . match ( /\b(?:(?:https?|ftp):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/ig ) ; if ( matches == null ) { return uris ; } for ( let i = 0 ; i < matches . length ; i ++ ) { const uri = ( gBrowser . mURIFixup . createFixupURI ( matches [ i ] , gBrowser . mURIFixup . FIXUP_FLAGS_MAKE_ALTERNATE_URI ) as mozURI | null ) ; if ( uri == null ) continue ; const uri_str = uri . spec ; if ( uris . indexOf ( uri_str ) === - 1 ) uris . push ( uri_str ) ; } return uris ; } ; this . postInitFx4Modifications = function postInitFx4Modifications ( ) { ( function ( ) { "use strict" ; if ( typeof gBrowser . pinTab !== "function" ) { tk . debug ( "gBrowser.pinTab doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . pinTab ; gBrowser . pinTab = function ( _aTab ) { "use strict" ; let result ; if ( tk . TabBar . Mode . getIsVerticalMode ( ) ) { alert ( "Sorry, Tab Kit 2 does not support App Tabs in Vertical mode" ) ; } else { result = old_func . apply ( this , arguments ) ; } return result ; } ; } ) ( ) ; ( function ( ) { "use strict" ; if ( typeof gBrowser . tabContainer . _lockTabSizing !== "function" ) { tk . debug ( "gBrowser.tabContainer._lockTabSizing doesn't exists, replacing function failed" ) ; return ; } const old_func = gBrowser . tabContainer . _lockTabSizing ; gBrowser . tabContainer . _lockTabSizing = function ( _aTab ) { "use strict" ; const result = old_func . apply ( this , arguments ) ; const numPinned = this . tabbrowser . _numPinnedTabs as number ; const tabs = this . tabbrowser . visibleTabs ; for ( let i = numPinned ; i < tabs . length ; i ++ ) { const tab = tabs [ i ] ; tab . style . setProperty ( "max-width" , "" ) ; } return result ; } ; } ) ( ) ; } ; this . postInitFx4TabEffects = function postInitFx4TabEffects ( ) { window . addEventListener ( "fullscreen" , tk . onFullScreenToggle , false ) ; } ; this . postInitListeners . push ( this . postInitFx4Modifications ) ; this . postInitListeners . push ( this . postInitFx4TabEffects ) ; this . onFullScreenToggle = function onFullScreenToggle ( ) { const tabsToolbar = document . getElementById ( "TabsToolbar" ) ; if ( tabsToolbar == null ) { return ; } let isFullScreenBeforeEvent = window . fullScreen ; if ( _prefs . getCharPref ( "tabbar_fullscreen_value_meaning_in_callback" ) === "value_after_change" ) { isFullScreenBeforeEvent = ! isFullScreenBeforeEvent ; } const willBeFullScreen = ! isFullScreenBeforeEvent ; const splitter = document . getElementById ( "tabkit-splitter" ) ; const appcontent = document . getElementById ( "appcontent" ) ; const full_screen_behaviour_preference_value = _prefs . getCharPref ( "tabbarFullscreenBehaviour" ) ; const should_hide_spitter_on_collapse = ( full_screen_behaviour_preference_value === "auto_collapse_with_spitter_hidden" ) ; if ( ! splitter ) { return ; } if ( full_screen_behaviour_preference_value === "do_nothing" ) { return ; } if ( willBeFullScreen ) { tk . debug ( "gonna set splitter collapsed" ) ; splitter . setAttribute ( "state" , "collapsed" ) ; tabsToolbar . setAttribute ( "collapsed" , "true" ) ; if ( should_hide_spitter_on_collapse ) { tk . debug ( "gonna set splitter hidden" ) ; splitter . setAttribute ( "hidden" , "true" ) ; } appcontent . setAttribute ( "collapsed" , "false" ) ; } else { tk . debug ( "gonna set splitter open" ) ; splitter . setAttribute ( "state" , "open" ) ; tabsToolbar . removeAttribute ( "collapsed" ) ; if ( should_hide_spitter_on_collapse ) { tk . debug ( "gonna set splitter visible" ) ; splitter . removeAttribute ( "hidden" ) ; } appcontent . setAttribute ( "collapsed" , "false" ) ; } } ; this . onPrefTabsonTopChanged = function onPrefTabsonTopChanged ( ) { if ( tk . localPrefService . getBoolPref ( "firefox.tabsontop.force_disable.enabled" ) === false ) { return ; } if ( gPrefService . getBoolPref ( "browser.tabs.onTop" ) === true ) { gPrefService . setBoolPref ( "browser.tabs.onTop" , false ) ; } } ; this . initOnPrefTabsonTopChanged = function initOnPrefTabsonTopChanged ( ) { tk . addGlobalPrefListener ( "browser.tabs.onTop" , tk . onPrefTabsonTopChanged ) ; tk . onPrefTabsonTopChanged ( ) ; } ; this . initListeners . push ( this . initOnPrefTabsonTopChanged ) ; this . Panorama = this . Panorama || { } ; this . Panorama . Initializers = this . Panorama . Initializers || { } ; this . Panorama . Initializers . addMethodHookOnPostInit = function addMethodHookOnPostInit ( ) { ( function ( ) { "use strict" ; if ( typeof TabView !== "object" || TabView == null || ( typeof TabView . toggle ) !== "function" ) { tk . debug ( "TabView.toggle doesn't exists, replacing function failed" ) ; return ; } const old_func = TabView . toggle ; TabView . toggle = function ( ) { "use strict" ; let result ; tk . debug ( ">>> TabView.toggle >>>" ) ; if ( tk . localPrefService . getBoolPref ( "panorama.enabled" ) === false ) { alert ( "Sorry, but Tabkit 2 does not support Panorama (They use the same API). Why use Panorama when you have Tabkit 2? :)" ) ; } else { result = old_func . apply ( this , arguments ) ; } tk . debug ( "<<< TabView.toggle <<<" ) ; return result ; } ; } ) ( ) ; } ; this . postInitListeners . push ( this . Panorama . Initializers . addMethodHookOnPostInit ) ; this . api = { getParentTab : function ( tab ) { if ( tk . getTabGroupId ( tab ) == null ) { return null ; } const tabs_in_group = tk . getGroupFromTab ( tab ) ; if ( tabs_in_group == null ) { return null ; } const possible_parent_tab_id = tab . getAttribute ( "possibleparent" ) ; return tabs_in_group . find ( ( tab_in_group ) => { if ( tab_in_group === tab ) { return false ; } return tk . getTabId ( tab_in_group ) === possible_parent_tab_id ; } ) ; } , getChildTabs : function ( tab ) { if ( tk . getTabGroupId ( tab ) == null ) { return null ; } const tabs_in_group = tk . getGroupFromTab ( tab ) ; if ( tabs_in_group == null ) { return null ; } const tab_id = tk . getTabId ( tab ) ; return tabs_in_group . filter ( ( tab_in_group ) => { if ( tab_in_group === tab ) { return false ; } return tab_in_group . getAttribute ( "possibleparent" ) === tab_id ; } ) ; } , addChildTabs : function ( tab , new_child_tabs ) { const tab_group_id = tk . getTabGroupId ( tab ) ; if ( tab_group_id == null ) { return ; } const tabs_in_group = tk . getGroupFromTab ( tab ) ; if ( tabs_in_group == null ) { return ; } const tab_id = tk . getTabId ( tab ) ; new_child_tabs . forEach ( ( new_child_tab ) => { new_child_tab . setAttribute ( "possibleparent" , tab_id ) ; } ) ; } , resetTab : function ( tab ) { tab . removeAttribute ( "groupid" ) ; tab . removeAttribute ( "tabid" ) ; tab . removeAttribute ( "possibleparent" ) ; } , } ; } ; } ) ( window ) ; window . addEventListener ( "DOMContentLoaded" , window . tabkit . onDOMContentLoaded , false ) ; window . addEventListener ( "load" , window . tabkit . onLoad , false ) ;