Last active
December 13, 2019 08:38
-
-
Save nolanlum/8acf20f06864f3bc8f6af6c2689cdcdb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name MultiRowTabLiteforFx.uc.js | |
| // @namespace http://space.geocities.yahoo.co.jp/gl/alice0775 | |
| // @description Multi-row tabs draggability fix, Experimental CSS version | |
| // @include main | |
| // @compatibility Firefox 69 | |
| // @author Alice0775, Endor8, TroudhuK, Izheil | |
| // @version 06/09/2019 23:37 Fixed issue with tabs when moving to another window | |
| // @version 05/09/2019 03:24 Fixed tab draggability to work with FF69 | |
| // @version 22/07/2019 19:21 Compatibility fix with Windows 7 | |
| // @version 23/03/2019 22:25 Comments on tab width | |
| // @version 09/03/2019 15:38 Fixed compatibility issue with Tab Session Manager addon | |
| // @version 18/02/2019 20:46 Tab line not being fully shown on maximized or fullscreen | |
| // @version 03/02/2019 15:15 Firefox 67 | |
| // @version 01/02/2019 23:48 Fixed empty pixel line below tabs | |
| // @version 31/01/2019 10:32 Fixed issue with fullscreen | |
| // @version 30/01/2019 02:05 Fixed issue with a pixel being above the tab bar | |
| // @version 23/11/2018 00:41 Firefox 65 | |
| // @version 19/10/2018 07:34 Firefox 62 | |
| // @version 11/05/2018 15:05 Firefox 60 | |
| // @version 08/05/2017 00:00 Firefox 48 | |
| // @version 05/01/2017 00:01 hide favicon if busy | |
| // @version 02/09/2016 00:01 Bug 1222490 - Actually remove panorama for Fx45+ | |
| // @version 02/09/2016 00:01 workaround css for lwt | |
| // ==/UserScript== | |
| zzzz_MultiRowTabLite(); | |
| function zzzz_MultiRowTabLite() { | |
| var css =` | |
| /* MULTIROW TABS CSS */ | |
| /* | |
| - For tab minimum width, you have to go to about:config and modify [browser.tabs.tabMinWidth] | |
| to the value you want. | |
| - For tab growth v | |
| Value of 1 -> Tab grows. Fixed max width of 226px. | |
| Value of 0 -> Tab doesn't grow. Uses tab min width as fixed width. */ | |
| :root { | |
| --tab-growth: 1} | |
| .tabbrowser-tab:not([pinned]) { | |
| flex-grow: var(--tab-growth)} | |
| .tabbrowser-tab::after {border: none !important} | |
| :root:-moz-lwtheme[lwtheme-image] {background-repeat: repeat-y !important} | |
| #tabbrowser-tabs .tab-background{ | |
| max-height: var(--tab-min-height) !important; | |
| min-height: var(--tab-min-height) !important} | |
| @media (-moz-os-version: windows-win10) { | |
| #tabbrowser-tabs .tab-background, #tabbrowser-tabs .tabbrowser-tab { | |
| min-height: calc(var(--tab-min-height) + 1px) !important} | |
| } | |
| /* This fix is intended for some updates when the tab line gets chopped on top of screen | |
| #main-window[sizemode="normal"] .tabbrowser-tab .tab-line, | |
| #main-window[sizemode="maximized"] .tabbrowser-tab .tab-line, | |
| #main-window[sizemode="fullscreen"] .tabbrowser-tab .tab-line {transform: translate(0,1px) !important} | |
| */ | |
| #tabbrowser-tabs {margin-top: 0px !important} | |
| .tab-stack {width: 100%} | |
| #tabbrowser-tabs .arrowscrollbox-scrollbox { | |
| display: flex; | |
| flex-wrap: wrap;} | |
| @-moz-document url(chrome://browser/content/browser.xhtml){ | |
| .scrollbutton-up[orient="horizontal"][part]~spacer, | |
| .scrollbutton-up[orient="horizontal"][part], | |
| .scrollbutton-down[orient="horizontal"][part]{ display: none } | |
| scrollbox[part][orient="horizontal"]{ | |
| display: flex; | |
| flex-wrap: wrap; | |
| overflow-y: auto; | |
| max-height: calc(var(--tab-min-height) * var(--multirow-n-rows)); | |
| scrollbar-color: currentColor transparent; | |
| scrollbar-width: thin; | |
| } | |
| } | |
| #tabbrowser-tabs .tabbrowser-arrowscrollbox { | |
| overflow: visible; | |
| display: block} | |
| :root[uidensity="touch"] .tabbrowser-tab, | |
| :root[uidensity="touch"] .tab-stack { | |
| min-height: calc(var(--tab-min-height) + 3px) !important; | |
| max-height: calc(var(--tab-min-height) + 3px) !important; | |
| margin-bottom: 0 !important} | |
| :root[uidensity="touch"] #tabbrowser-tabs .arrowscrollbox-scrollbox { | |
| min-height: var(--tab-min-height) !important;} | |
| .arrowscrollbox-overflow-start-indicator, | |
| .arrowscrollbox-overflow-end-indicator {position: fixed !important} | |
| @media (-moz-os-version: windows-win10) { | |
| .titlebar-buttonbox, #titlebar-buttonbox {display: block !important; height:var(--tab-min-height) !important}} | |
| #tabbrowser-tabs .scrollbutton-up, #tabbrowser-tabs .scrollbutton-down, #alltabs-button, | |
| :root:not([customizing]) #TabsToolbar #new-tab-button, .tabbrowser-tab::after | |
| {display: none} | |
| `; | |
| var sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService); | |
| var uri = makeURI('data:text/css;charset=UTF=8,' + encodeURIComponent(css)); | |
| sss.loadAndRegisterSheet(uri, sss.AGENT_SHEET); | |
| gBrowser.tabContainer._getDropIndex = function(event, isLink) { | |
| var tabs = document.getElementsByClassName("tabbrowser-tab") | |
| var tab = this._getDragTargetTab(event, isLink); | |
| if (window.getComputedStyle(this).direction == "ltr") { | |
| for (let i = tab ? tab._tPos : 0; i < tabs.length; i++) { | |
| let rect = tabs[i].getBoundingClientRect(); | |
| if (event.clientX < rect.x + rect.width / 2 | |
| && event.clientY < rect.y + rect.height) // multirow fix | |
| return i; | |
| } | |
| } else { | |
| for (let i = tab ? tab._tPos : 0; i < tabs.length; i++) { | |
| let rect = tabs[i].getBoundingClientRect(); | |
| if (event.clientX > rect.x + rect.width / 2 | |
| && event.clientY < rect.y + rect.height) // multirow fix | |
| return i; | |
| } | |
| } | |
| return tabs.length; | |
| }; | |
| // This scrolls down to the current tab when you open a new one, or restore a session. | |
| function scrollToView() { | |
| var selTab = document.querySelectorAll(".tabbrowser-tab[selected='true']")[0]; | |
| selTab.scrollIntoView({behavior: "smooth", block: "nearest", inline: "nearest"}); | |
| } | |
| gBrowser.tabContainer.addEventListener('TabOpen', scrollToView, false); | |
| gBrowser.tabContainer.addEventListener("TabSelect", scrollToView, false); | |
| document.addEventListener("SSTabRestoring", scrollToView, false); | |
| // This sets when to apply the fix (by default a new row starts after the 23th open tab, unless you changed the min-size of tabs) | |
| gBrowser.tabContainer.ondragstart = function(){if(gBrowser.tabContainer.clientHeight > document.getElementsByClassName("tabbrowser-tab")[0].clientHeight) { | |
| gBrowser.tabContainer._getDropEffectForTabDrag = function(event){return "";}; // multirow fix: to make the default "dragover" handler do nothing | |
| gBrowser.tabContainer._onDragOver = function(event) { | |
| event.preventDefault(); | |
| event.stopPropagation(); | |
| var ind = this._tabDropIndicator; | |
| var effects = orig_getDropEffectForTabDrag(event); | |
| if (effects == "link") { | |
| let tab = this._getDragTargetTab(event, true); | |
| if (tab) { | |
| if (!this._dragTime) | |
| this._dragTime = Date.now(); | |
| if (!tab.hasAttribute("pendingicon") && // annoying fix | |
| Date.now() >= this._dragTime + this._dragOverDelay) | |
| this.selectedItem = tab; | |
| ind.hidden = true; | |
| return; | |
| } | |
| } | |
| var newIndex = this._getDropIndex(event, effects == "link"); | |
| if (newIndex == null) | |
| return | |
| var tabs = document.getElementsByClassName("tabbrowser-tab"); | |
| var ltr = (window.getComputedStyle(this).direction == "ltr"); | |
| var rect = this.arrowScrollbox.getBoundingClientRect(); | |
| var newMarginX, newMarginY; | |
| if (newIndex == tabs.length) { | |
| let tabRect = tabs[newIndex - 1].getBoundingClientRect(); | |
| if (ltr) | |
| newMarginX = tabRect.right - rect.left; | |
| else | |
| newMarginX = rect.right - tabRect.left; | |
| newMarginY = tabRect.top + tabRect.height - rect.top - rect.height; // multirow fix | |
| } else { | |
| let tabRect = tabs[newIndex].getBoundingClientRect(); | |
| if (ltr) | |
| newMarginX = tabRect.left - rect.left; | |
| else | |
| newMarginX = rect.right - tabRect.right; | |
| newMarginY = tabRect.top + tabRect.height - rect.top - rect.height; // multirow fix | |
| } | |
| ind.hidden = false; | |
| newMarginX += ind.clientWidth / 2; | |
| if (!ltr) | |
| newMarginX *= -1; | |
| ind.style.transform = "translate(" + Math.round(newMarginX) + "px," + Math.round(newMarginY) + "px)"; // multirow fix | |
| ind.style.marginInlineStart = (-ind.clientWidth) + "px"; | |
| } | |
| } | |
| gBrowser.tabContainer.addEventListener("dragover", gBrowser.tabContainer._onDragOver, true); | |
| gBrowser.tabContainer.onDrop = function(event) { | |
| var newIndex; | |
| var dt = event.dataTransfer; | |
| var dropEffect = dt.dropEffect; | |
| var draggedTab; | |
| let movingTabs; | |
| if (dt.mozTypesAt(0)[0] == TAB_DROP_TYPE) { | |
| draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); | |
| if (!draggedTab) { | |
| return; | |
| } | |
| movingTabs = draggedTab._dragData.movingTabs; | |
| draggedTab.container._finishGroupSelectedTabs(draggedTab); | |
| } | |
| if (draggedTab && dropEffect == "copy") {} | |
| else if (draggedTab && draggedTab.container == this) { | |
| newIndex = this._getDropIndex(event, false); | |
| if (newIndex > draggedTab._tPos) | |
| newIndex--; | |
| gBrowser.moveTabTo(draggedTab, newIndex); | |
| } | |
| }; | |
| gBrowser.tabContainer.addEventListener("drop", function(event){this.onDrop(event);}, true); | |
| }}; | |
| // copy of the original and overrided _getDropEffectForTabDrag method | |
| function orig_getDropEffectForTabDrag(event) { | |
| var dt = event.dataTransfer; | |
| let isMovingTabs = dt.mozItemCount > 0; | |
| for (let i = 0; i < dt.mozItemCount; i++) { | |
| // tabs are always added as the first type | |
| let types = dt.mozTypesAt(0); | |
| if (types[0] != TAB_DROP_TYPE) { | |
| isMovingTabs = false; | |
| break; | |
| }} | |
| if (isMovingTabs) { | |
| let sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0); | |
| if ( | |
| sourceNode instanceof XULElement && | |
| sourceNode.localName == "tab" && | |
| sourceNode.ownerGlobal.isChromeWindow && | |
| sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == | |
| "navigator:browser" && | |
| sourceNode.ownerGlobal.gBrowser.tabContainer == sourceNode.container | |
| ) { | |
| // Do not allow transfering a private tab to a non-private window | |
| // and vice versa. | |
| if ( | |
| PrivateBrowsingUtils.isWindowPrivate(window) != | |
| PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerGlobal) | |
| ) { | |
| return "none";} | |
| if ( | |
| window.gMultiProcessBrowser != | |
| sourceNode.ownerGlobal.gMultiProcessBrowser | |
| ) { | |
| return "none";} | |
| return dt.dropEffect == "copy" ? "copy" : "move"; | |
| }} | |
| if (browserDragAndDrop.canDropLink(event)) { | |
| return "link";} | |
| return "none";} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* DO NOT DELETE THIS LINE */ | |
| @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); | |
| /* This enables the use of JS external files */ | |
| toolbarbutton#alltabs-button { | |
| -moz-binding: url("userChrome.xml#js") | |
| } | |
| /* Hide Tab Close buttons on unselected tabs unless hovered */ | |
| #tabbrowser-tabs .tabbrowser-tab:not([pinned="true"]) > .tab-stack > .tab-content > .tab-close-button { | |
| visibility: collapse !important; | |
| opacity: 0 !important; | |
| } | |
| #tabbrowser-tabs .tabbrowser-tab[selected="true"]:not([pinned="true"]) > .tab-stack > .tab-content > .tab-close-button { | |
| visibility: visible !important; | |
| } | |
| #tabbrowser-tabs .tabbrowser-tab:not([pinned="true"]):hover > .tab-stack > .tab-content > .tab-close-button { | |
| visibility: visible !important; | |
| opacity: 1 !important; | |
| transition: all 250ms ease-in-out !important; | |
| } | |
| #tabbrowser-tabs .tabbrowser-tab:not([pinned="true"]) > .tab-stack > .tab-content > .tab-close-button { | |
| display: -moz-box !important; | |
| } | |
| /* Style pending tabs with a background and italic text */ | |
| #tabbrowser-tabs .tabbrowser-tab[pending="true"] > .tab-stack > .tab-background { | |
| background-attachment: none !important; | |
| background-color: #ECE9D8 !important; | |
| background-image: none !important; | |
| } | |
| #tabbrowser-tabs .tabbrowser-tab[pending="true"] > .tab-stack > .tab-content > .tab-label-container > .tab-label { | |
| color: #C00 !important; | |
| font-style: italic !important; | |
| } | |
| /* Style unread tabs with reddish italic text */ | |
| /*#tabbrowser-tabs .tabbrowser-tab[unread="true"] > .tab-stack > .tab-content > .tab-label-container > .tab-label { | |
| color: #F66 !important; | |
| font-style: italic !important; | |
| }*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?xml version="1.0"?> | |
| <!-- Copyright (c) 2017 Haggai Nuchi | |
| Available for use under the MIT License: | |
| https://opensource.org/licenses/MIT | |
| --> | |
| <!-- Run userChrome.js/userChrome.xul and .uc.js/.uc.xul/.css files --> | |
| <bindings id="generalBindings" | |
| xmlns="http://www.mozilla.org/xbl" | |
| xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" | |
| xmlns:xbl="http://www.mozilla.org/xbl"> | |
| <binding id="js"> | |
| <implementation> | |
| <constructor><![CDATA[ | |
| if(window.userChromeJsMod) return; | |
| window.userChromeJsMod = true; | |
| var chromeFiles = FileUtils.getDir("UChrm", []).directoryEntries; | |
| var xulFiles = []; | |
| var sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService); | |
| while(chromeFiles.hasMoreElements()) { | |
| var file = chromeFiles.getNext().QueryInterface(Ci.nsIFile); | |
| var fileURI = Services.io.newFileURI(file); | |
| if(file.isFile()) { | |
| if(/(^userChrome|\.uc)\.js$/i.test(file.leafName)) { | |
| Services.scriptloader.loadSubScriptWithOptions(fileURI.spec, {target: window, ignoreCache: true}); | |
| } | |
| else if(/(^userChrome|\.uc)\.xul$/i.test(file.leafName)) { | |
| xulFiles.push(fileURI.spec); | |
| } | |
| else if(/\.as\.css$/i.test(file.leafName)) { | |
| if(!sss.sheetRegistered(fileURI, sss.AGENT_SHEET)) | |
| sss.loadAndRegisterSheet(fileURI, sss.AGENT_SHEET); | |
| } | |
| else if(/^(?!(userChrome|userContent)\.css$).+\.css$/i.test(file.leafName)) { | |
| if(!sss.sheetRegistered(fileURI, sss.USER_SHEET)) | |
| sss.loadAndRegisterSheet(fileURI, sss.USER_SHEET); | |
| } | |
| } | |
| } | |
| setTimeout(function loadXUL() { | |
| if(xulFiles.length > 0) { | |
| document.loadOverlay(xulFiles.shift(), null); | |
| setTimeout(loadXUL, 5); | |
| } | |
| }, 0); | |
| ]]></constructor> | |
| </implementation> | |
| </binding> | |
| </bindings> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment