Created
June 23, 2011 08:16
-
-
Save lchr/1042141 to your computer and use it in GitHub Desktop.
Fantastic Pentadactyl tab plugin
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
| "use strict"; | |
| XML.ignoreWhitespace = false; | |
| XML.prettyPrinting = false; | |
| var INFO = | |
| <plugin name="buftabs" version="1.1.0" | |
| href="https://github.com/grassofhust/buftabs" | |
| summary="Buftabs: show the tabbar in the statusline" | |
| xmlns={NS}> | |
| <author email="lucas@glacicle.org" href="http://git.glacicle.org/vimperator-buftabs/">Lucas de Vries</author> | |
| <author email="frederick.zou@gmail.com">Yang Zou</author> | |
| <license href="http://sam.zoy.org/wtfpl/">WTFPL</license> | |
| <project name="Pentadactyl" minVersion="1.0"/> | |
| <p> | |
| When the script is loaded it hijacks the statusline to display a | |
| list of tabs, you can use the <o>buftabs</o> option to toggle it | |
| on or off. | |
| </p> | |
| <p> | |
| Use the BufTab, BufTabAlternate, BufTabSelected and BufTabs highlight groups to style the | |
| buftabs. Make sure you have called the "loadplugins" command | |
| before using the highlight groups in your vimperatorrc. | |
| </p> | |
| <p> | |
| Thanks Lucas de Vries and other folks on vimperator/dactyl. This plugins was initial developed by Lucas de Vries. | |
| </p> | |
| <item> | |
| <tags>'bt' 'buftabs'</tags> | |
| <spec>'buftabs' 'bt'</spec> | |
| <type>boolean</type> <default>true</default> | |
| <description> | |
| Toggle buftabs on or off. | |
| </description> | |
| </item> | |
| <item> | |
| <tags>'btr' 'buftabs-rnu'</tags> | |
| <spec>'buftabs-rnu' 'btr'</spec> | |
| <type>boolean</type> <default>false</default> | |
| <description>Relative tabnumber.</description> | |
| </item> | |
| <item> | |
| <tags>'btp' 'buftabs-progress'</tags> | |
| <spec>'buftabs-progress' 'btp'</spec> | |
| <type>boolean</type> <default>true</default> | |
| <description>Toggle progressbar on or off.</description> | |
| </item> | |
| <item> | |
| <tags>'bte' 'buftabs-elem'</tags> | |
| <spec>'buftabs-elem' 'bte'</spec> | |
| <type>charlist</type> <default>"nthb"</default> | |
| <description> | |
| <p>Define which sections are shown.</p> | |
| <p>Supported characters:</p> | |
| <dl dt="width: 6em;"> | |
| <dt>i</dt> <dd>Favicon</dd> | |
| <dt>n</dt> <dd>Tabnumber</dd> | |
| <dt>t</dt> <dd>Tab title</dd> | |
| <dt>h</dt> <dd>Forward/Backward indicator</dd> | |
| <dt>b</dt> <dd>Heart indicator</dd> | |
| </dl> | |
| </description> | |
| </item> | |
| <item> | |
| <tags>:bt :buftabs</tags> | |
| <strut/> | |
| <spec>:buftabs</spec> | |
| <description> | |
| <p>Toggle buftabs on or off.</p> | |
| </description> | |
| </item> | |
| <p>There are four highlight groups: BufTab, BufTabSelected, BufTabAlternate and BufTabs</p> | |
| <dl dt="width: 12em;"> | |
| <dt>BufTab</dt> <dd>Tabs</dd> | |
| <dt>BufTabSelected</dt> <dd>Selected Tab</dd> | |
| <dt>BufTabAlternate</dt> <dd>Previously selected tab</dd> | |
| <dt>BufTabs</dt> <dd>Buftabs itself</dd> | |
| </dl> | |
| <p>Recommend Settings:</p> | |
| <code> | |
| hi -a BufTabSelected background-repeat:no-repeat; background-size:contain, contain; background-position: 4px top; color:#FFF; background-color:#000; padding:0 4px; font-weight:normal;border-radius:2px; | |
| hi -a BufTabAlternate background-repeat:no-repeat; background-size:contain, contain; background-position: 4px top; padding:0 4px; cursor:pointer !important; | |
| hi -a BufTab background-repeat:no-repeat; background-size:contain, contain; background-position: 4px top; padding:0 4px; cursor:pointer !important; | |
| hi -a BufTab:hover color:#2e3330;background-color: #88b090; border-radius:2px; | |
| hi -a BufTabAlternate:hover color:#2e3330;background-color: #88b090; border-radius:2px; | |
| </code> | |
| <Warning>Buftabs does not support firefox 3.6.</Warning> | |
| </plugin>; | |
| let buftabs = { | |
| id : 'dactyl-statusline-field-buftabs', | |
| fullLoad: false, | |
| init : function () { | |
| if (buftabs.options['buftabs']) { | |
| if (document.getElementById(buftabs.id)) | |
| commandline.widgets.updateVisibility(); | |
| else { | |
| let widget = util.xmlToDom( | |
| <hbox xmlns={XUL} highlight="BufTabs" id={buftabs.id} flex="1" valign="middle"/>, | |
| document); | |
| statusline.widgets.url.parentNode.insertBefore(widget, statusline.widgets.url.nextSibling); | |
| commandline.widgets.addElement({ | |
| name: "buftabs", | |
| getGroup: function () this.statusbar, | |
| getValue: function () statusline.visible && buftabs.options['buftabs'], | |
| noValue: true | |
| }); | |
| commandline.widgets.statusbar.buftabs.addEventListener("DOMMouseScroll", function(event) { | |
| gBrowser.tabContainer.advanceSelectedTab(event.detail < 0 ? -1 : 1, true); | |
| event.stopPropagation(); | |
| }, true); | |
| buftabs.toggleProgressBar(); | |
| } | |
| registerMyListener(); | |
| buftabs.update(); | |
| } else | |
| buftabs.destory(); | |
| buftabs.fullLoad = true; | |
| }, | |
| setup: function() { | |
| buftabs.fullLoad = true; | |
| dactyl.execute('set buftabs!'); | |
| }, | |
| destory: function() { | |
| unregisterMyListener(); | |
| }, | |
| get options() buftabs._options || {'elem':'nthb', 'buftabs': true, 'rnu': false, 'progress': true}, | |
| set options(options) { | |
| buftabs._options = { | |
| 'elem' : 'elem' in options ? options['elem'] : buftabs.options['elem'], | |
| 'buftabs': 'buftabs' in options ? options['buftabs'] : buftabs.options['buftabs'], | |
| 'rnu': 'rnu' in options ? options['rnu'] : buftabs.options['rnu'], | |
| 'progress': 'progress' in options ? options['progress'] : buftabs.options['progress'] | |
| }; | |
| if (buftabs.fullLoad) // window has fully loaded | |
| buftabs.init(); | |
| }, | |
| get btabs() commandline.widgets.statusbar.buftabs, | |
| get blabels() buftabs.btabs.childNodes, | |
| get curLabelIndex() buftabs._curLabelIndex, | |
| set curLabelIndex(value) { | |
| buftabs._curLabelIndex = value; | |
| }, | |
| Otab : function (arg) { | |
| if (typeof arg == 'object') | |
| return arg; | |
| return gBrowser.tabs[arg]; | |
| }, | |
| mb_strlen: function (str) { | |
| let m=encodeURIComponent(str).match(/%[89ABab]/g); | |
| let extra_length = m ? m.length / 3 : 0; | |
| return str.length + extra_length; | |
| }, | |
| mb_substr: function(str, length) { | |
| let i = 0; | |
| let overflow = false; | |
| let plength = 0; | |
| while (!overflow) { | |
| plength = buftabs.mb_strlen(str.substr(0, i)); | |
| if (plength >= length) | |
| overflow = true; | |
| i = i + 1; | |
| } | |
| return str.substr(0, i).replace(/^\s+|\s+$/g, ""); | |
| }, | |
| Obrowser: function(arg) { | |
| if (typeof arg == 'object') // a label obj | |
| return arg; | |
| return gBrowser.getBrowserAtIndex(arg); | |
| }, | |
| Olabel: function (arg) { | |
| if (typeof arg == 'object') | |
| return arg; | |
| return buftabs.blabels[arg]; | |
| }, | |
| Oposition: function (arg) { | |
| let label = arg; | |
| if (typeof arg != 'object') | |
| label = buftabs.Olabel(arg); | |
| return label.getBoundingClientRect(); | |
| }, | |
| Onavigation : function (arg) { | |
| let tab = arg; | |
| if (typeof arg != 'object') | |
| tab = buftabs.Otab(arg); | |
| let browser = tab.linkedBrowser; | |
| // history | |
| if (browser.webNavigation) { | |
| let sh = browser.webNavigation.sessionHistory; | |
| if (sh && (sh.index > 0) && (sh.index < sh.count - 1)) | |
| return UTF8("↔"); | |
| if (sh && (sh.index < sh.count - 1) && (sh.index == 0)) | |
| return UTF8("→"); | |
| if (sh && (sh.index >= sh.count - 1) && (sh.index > 0)) | |
| return UTF8("←"); | |
| } | |
| return ""; | |
| }, | |
| setFavicon : function (aLabel, aTab) { | |
| let label = buftabs.Olabel(aLabel); | |
| let tab = buftabs.Otab(aTab); | |
| let image = tab.image; | |
| if (tab.linkedBrowser.webProgress.isLoadingDocument) | |
| image = "chrome://browser/skin/tabbrowser/connecting.png"; | |
| if (image == "") | |
| image = DEFAULT_FAVICON; | |
| label.style.paddingLeft="21px"; | |
| label.style.backgroundImage='url("'+image+'")'; | |
| }, | |
| removeFavicon : function (arg) { | |
| let label = buftabs.Olabel(arg); | |
| label.style.paddingLeft="4px"; | |
| label.style.backgroundImage='none'; | |
| }, | |
| showFavicons : function () { | |
| buftabs.blabels.foreach(function (label) buftabs.setFavicon(label)); | |
| buftabs.layout(); | |
| }, | |
| // layout | |
| layout: function () { | |
| // Scroll | |
| let position = buftabs.Oposition(buftabs.curLabelIndex); | |
| let first_position = buftabs.Oposition(0); | |
| let last_position = buftabs.Oposition(buftabs.blabels.length - 1); | |
| let btabs_position = buftabs.btabs.getBoundingClientRect(); | |
| let next_position = false; | |
| if (buftabs.curLabelIndex + 1 < buftabs.blabels.length) | |
| next_position = buftabs.Oposition(buftabs.curLabelIndex + 1); | |
| let prev_position = false; | |
| if (buftabs.curLabelIndex > 0) | |
| prev_position = buftabs.Oposition(buftabs.curLabelIndex - 1); | |
| if (next_position) { | |
| if (next_position['right'] >= btabs_position['right']) | |
| buftabs.btabs.scrollLeft = next_position['right'] + btabs_position['left'] - first_position['left'] - btabs_position['right']; | |
| } else { | |
| if (position['right'] >= btabs_position['right']) | |
| buftabs.btabs.scrollLeft = position['right'] + btabs_position['left'] - first_position['left'] - btabs_position['right']; | |
| } | |
| if (prev_position) { | |
| if (prev_position['left'] <= btabs_position['left']) | |
| buftabs.btabs.scrollLeft = prev_position['left'] - first_position['left']; | |
| } else { | |
| if (position['left'] <= btabs_position['left']) | |
| buftabs.btabs.scrollLeft = 0; | |
| } | |
| // Show the entire line if possible | |
| if (buftabs.btabs.scrollWidth <= buftabs.btabs.clientWidth) | |
| buftabs.btabs.scrollLeft = 0; | |
| else { | |
| // check last label position | |
| let sum = 0; | |
| let labels = Array.slice(buftabs.blabels); | |
| labels.forEach(function (label) { | |
| sum += label.scrollWidth; | |
| }); | |
| if (last_position['right'] < buftabs.btabs.clientWidth) | |
| // This doesn't work, why? | |
| // buftabs.btabs.scrollLeft = buftabs.btabs.scrollWidth - buftabs.btabs.clientWidth; | |
| buftabs.btabs.scrollLeft = sum - buftabs.btabs.clientWidth; | |
| } | |
| }, | |
| buildContainer: function () { | |
| let container = util.xmlToDom( | |
| <hbox xmlns={XUL} flex="0" tooltiptext=""> | |
| <xul:image flex="0" key="image" class="plain show buftabs-image" /> | |
| <xul:label flex="0" key="tabnumber" class="plain show buftabs-tabnumber" /> | |
| <xul:label flex="0" key="indexnumber" class="plain show buftabs-indexnumber" /> | |
| <xul:label flex="0" key="info" class="plain show buftabs-info" /> | |
| <xul:label flex="0" key="history" class="plain show buftabs-history" /> | |
| <xul:label flex="0" key="bookmark" class="plain show buftabs-bookmark" /> | |
| </hbox>, | |
| document); | |
| return container; | |
| }, | |
| obtainElements: function (container) { | |
| let nodes = container.childNodes; | |
| for each (let node in nodes) { | |
| object[node.key] = node; | |
| } | |
| }, | |
| toggleProgressBar: function () { | |
| commandline.widgets.addElement({ | |
| name: "progress", | |
| getGroup: function () this.statusbar, | |
| getValue: function () buftabs.options['progress'] || !buftabs.options['buftabs'], | |
| noValue: true | |
| }); | |
| }, | |
| buildLabels: function () { | |
| // Get buftabbar | |
| let btabs = buftabs.btabs; | |
| let visibleTabs_length = gBrowser.visibleTabs.length; | |
| // Make sure we have an appropriate amount of labels | |
| while (btabs.childNodes.length > visibleTabs_length) | |
| btabs.removeChild(btabs.lastChild); | |
| while (btabs.childNodes.length < visibleTabs_length) { | |
| let label = document.createElement("label"); | |
| label.setAttribute("crop", "end"); | |
| btabs.appendChild(label); | |
| label.addEventListener("mouseover", function(ev) { | |
| buftabs.updateLabelTooltip(this, this.tabindex); | |
| }, false); | |
| label.addEventListener("click", function (ev) { | |
| if (ev.button == 0) | |
| gBrowser.selectTabAtIndex(this.tabpos); | |
| else if (ev.button == 1) { | |
| if (gBrowser.visibleTabs[this.tabpos + 1]) | |
| gBrowser.tabContainer.selectedItem = gBrowser.visibleTabs[this.tabpos + 1]; // conflict with tab-option.js? | |
| gBrowser.removeTab(gBrowser.tabContainer.getItemAtIndex(this.tabindex)); | |
| } | |
| }, false); | |
| } | |
| }, | |
| // Update the tabs | |
| update: function () { | |
| if (!buftabs.options["buftabs"]) | |
| return; | |
| buftabs.buildLabels(); | |
| let visibleTabs = gBrowser.visibleTabs; | |
| // Create the new tabs | |
| for (let [i, tab] in iter(visibleTabs)) { | |
| // Create label | |
| let label = buftabs.Olabel(i); | |
| // Fill label | |
| label.tabpos = i; | |
| label.tabindex = gBrowser.tabContainer.getIndexOfItem(tab); | |
| buftabs.fillLabel(label, tab); | |
| } | |
| buftabs.curLabelIndex = tabs.index(null, true); | |
| buftabs.layout(); | |
| }, | |
| updateTabOpen: function (aEvent) { | |
| buftabs.update(); | |
| }, | |
| updateTabHide: function (aEvent) { | |
| buftabs.update(); | |
| }, | |
| updateTabMove: function (aEvent) { | |
| buftabs.update(); | |
| }, | |
| updateTabSelect: function (aEvent) { | |
| buftabs.update(); | |
| }, | |
| updateTabClose: function (aEvent) { | |
| if (!buftabs.options["buftabs"]) | |
| return; | |
| buftabs.buildLabels(); | |
| let visibleTabs = gBrowser.visibleTabs; | |
| let closed_labelIndex = gBrowser.tabContainer.getIndexOfItem(aEvent.target); | |
| // Create the new tabs | |
| for (let [i, tab] in iter(visibleTabs)) { | |
| // Create label | |
| let label = buftabs.Olabel(i); | |
| // Fill label | |
| label.tabpos = i; | |
| label.tabindex = gBrowser.tabContainer.getIndexOfItem(tab); | |
| if (label.tabindex > closed_labelIndex) // dirty hack, I don't know why | |
| label.tabindex = label.tabindex - 1; | |
| buftabs.fillLabel(label, tab); | |
| } | |
| buftabs.curLabelIndex = tabs.index(null, true); | |
| buftabs.layout(); | |
| }, | |
| updateTabAttrModified: function(aEvent) { | |
| if (!aEvent.target.hidden) { | |
| let tab = aEvent.target; | |
| let labelindex = tabs.index(tab, true); | |
| buftabs.fillLabel(labelindex, tab); | |
| dactyl.timeout(buftabs.layout, 400); | |
| } | |
| }, | |
| // fill a label | |
| fillLabel: function(arglabel, argtab) { | |
| let label = buftabs.Olabel(arglabel); | |
| let tab = buftabs.Otab(argtab); | |
| let browser = tab.linkedBrowser; | |
| let tabvalue = ""; | |
| // Get title | |
| if (buftabs.options['elem'].indexOf('t') >= 0) | |
| tabvalue += tab.label; | |
| let indicate = ""; | |
| // Get history | |
| if (buftabs.options['elem'].indexOf('h') >= 0) | |
| indicate = buftabs.Onavigation(tab); | |
| // tabvalue += buftabs.Onavigation(tab); // todo, use tab directly | |
| // Bookmark icon | |
| if (buftabs.options['elem'].indexOf('b') >= 0 && bookmarkcache.isBookmarked(browser.contentDocument.location.href)) | |
| indicate += UTF8("❤"); | |
| // Brackets and index | |
| if (buftabs.options['elem'].indexOf('n') >= 0) { | |
| if (buftabs.options['rnu']) { | |
| if (indicate.length > 0) | |
| indicate = "[" + (label.tabpos + 1) + " " + indicate + "]"; | |
| else | |
| indicate = "[" + (label.tabpos + 1) + "]"; | |
| } else { | |
| if (indicate.length > 0) | |
| indicate = "[" + (label.tabindex + 1) + " " + indicate + "]"; | |
| else | |
| indicate = "[" + (label.tabindex + 1) + "]"; | |
| } | |
| } | |
| tabvalue = indicate + tabvalue; | |
| label.setAttribute("value", tabvalue); | |
| // tabbrowser getIcon | |
| if (buftabs.options['elem'].indexOf('i') >= 0) | |
| buftabs.setFavicon(label, tab); | |
| else | |
| buftabs.removeFavicon(label); | |
| // Set the correct highlight group | |
| if (tabs.index(null, true) == label.tabpos) | |
| label.setAttributeNS(NS.uri, "highlight", "BufTabSelected"); | |
| else { | |
| if (tabs.index(tabs.alternate, true) == label.tabpos) | |
| label.setAttributeNS(NS.uri, "highlight", "BufTabAlternate"); | |
| else | |
| label.setAttributeNS(NS.uri, "highlight", "BufTab"); | |
| } | |
| }, | |
| updateLabelTooltip: function (aLabel, aTab) { | |
| let label = buftabs.Olabel(aLabel); | |
| let tab = buftabs.Otab(aTab); | |
| let browser = tab.linkedBrowser; | |
| label.setAttribute('tooltiptext', tab.label + "\n" + browser.currentURI.spec); | |
| }, | |
| }; | |
| function registerMyListener() { | |
| gBrowser.tabContainer.addEventListener("TabOpen", buftabs.updateTabOpen, false); | |
| gBrowser.tabContainer.addEventListener("TabHide", buftabs.updateTabHide, false); | |
| gBrowser.tabContainer.addEventListener("TabMove", buftabs.updateTabMove, false); | |
| gBrowser.tabContainer.addEventListener("TabClose", buftabs.updateTabClose, false); | |
| gBrowser.tabContainer.addEventListener("TabSelect", buftabs.updateTabSelect, false); | |
| gBrowser.tabContainer.addEventListener("TabAttrModified", buftabs.updateTabAttrModified, false); // updateed, use fillLabel | |
| window.addEventListener("fullscreen", buftabs.layout, false); | |
| } | |
| function unregisterMyListener() { | |
| gBrowser.tabContainer.removeEventListener("TabOpen", buftabs.updateTabOpen, false); | |
| gBrowser.tabContainer.removeEventListener("TabHide", buftabs.updateTabHide, false); | |
| gBrowser.tabContainer.removeEventListener("TabMove", buftabs.updateTabMove, false); | |
| gBrowser.tabContainer.removeEventListener("TabClose", buftabs.updateTabClose, false); | |
| gBrowser.tabContainer.removeEventListener("TabSelect", buftabs.updateTabSelect, false); | |
| gBrowser.tabContainer.removeEventListener("TabAttrModified", buftabs.updateTabAttrModified, false); | |
| window.removeEventListener("fullscreen", buftabs.layout, false); | |
| } | |
| buftabs.init(); | |
| window.addEventListener('unload', buftabs.destory, false); | |
| // Options | |
| options.add(["buftabs-progress", "btp"], | |
| "Show progress", | |
| "boolean", | |
| true, | |
| { | |
| setter: function (value) { | |
| buftabs.options = {'progress': value}; | |
| return value; | |
| } | |
| } | |
| ); | |
| options.add(["buftabs-rnu", "btr"], | |
| "Show Relative tabnumber", | |
| "boolean", | |
| false, | |
| { | |
| setter: function (value) { | |
| buftabs.options = {'rnu': value}; | |
| return value; | |
| } | |
| } | |
| ); | |
| options.add(["buftabs-elem", "bte"], | |
| "Show or hide certain elemments", | |
| "charlist", | |
| "nthb", | |
| { | |
| values: { | |
| 'i': 'Favicon', | |
| 'n': 'Tabnumber', | |
| 't': 'Tab title', | |
| 'h': 'History forward/backward indicate', | |
| 'b': 'Bookmark state' | |
| }, | |
| setter: function (value) { | |
| buftabs.options = {'elem': value}; | |
| return value; | |
| } | |
| }); | |
| options.add(["buftabs", "bt"], | |
| "Control whether to use buftabs in the statusline", | |
| "boolean", | |
| true, | |
| { | |
| setter: function (value) { | |
| buftabs.options = {'buftabs' : value}; | |
| return value; | |
| } | |
| } | |
| ); | |
| // Add custom commands | |
| group.commands.add(["buf[tabs]", "bt"], | |
| "Activate buftabs manual", | |
| buftabs.setup, | |
| { | |
| argCount: 0 | |
| } | |
| ); | |
| // Initialise highlight groups | |
| highlight.loadCSS(<![CDATA[ | |
| !BufTabs color: inherit; margin:0 !important; padding:0 !important; overflow:hidden; | |
| !BufTab margin:0; padding:0; max-width:120px; | |
| !BufTabAlternate margin:0; padding:0; max-width:120px; | |
| !BufTabSelected margin:0; padding:0; max-width:120px; | |
| ]]>); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment