+ teramako
+ MPL 1.1/GPL 2.0/LGPL 2.1
+
+
+ For adding Panorama supports.
+ This plugin makes the default feature to not switch to the other group suddenly.
+ And add some mappings and commands for Parnorama.
+
+
+ Use at your OWN RISK.
+ This pluin overwrite many mappings, some commands, and some completions.
+
+ New Mappings and Commands
+ -
+ g@
+ countg@
+
+
Switch to AppTab.
+ If the current tab is already AppTab, switch to the next AppTab.
+
+
+ -
+ ]]>
+ count<C-S-n>
+
+
Switch to next group.
+ Caution: cannot switch to empty group.
+
+
+ -
+ ]]>
+ count<C-S-p>
+
+
Switch to previous group.
+
+
+ -
+ :mkgroup :mkg
+ :mkgroup! GroupName
+
+
Create new tab group named GroupName.
+ If specified !, move the current tab to the group.
+
+
+ -
+ :stash :stashtogroup
+ :stashtogroup! GroupName
+
+
Stash the current tab to GroupName.
+ Caution: connnot stash AppTab (pinned tab)
+
+
+ -
+ :switchgroup :swg
+ :switchgroup GroupName
+ :swg GroupName
+ :countswitchgroup
+ :countswg
+
+
Switch group to GroupName
+
+
+
+>;
+
+/**
+ * @method selectVisible {{{
+ * 現在表示されているタブでの絶対/相対位置によるタブ選択を行う
+ * (tabs.select() だと全タブが対象となる)
+ * @param {String} spec
+ * @param {Boolean} wrap
+ */
+function selectVisible (spec, wrap) {
+ if (spec === void(0) || spec === "")
+ return;
+
+ let tabs = gBrowser.visibleTabs;
+ let index;
+ if (typeof spec === "number" || /^\d+$/.test(spec)) {
+ index = parseInt(spec, 10);
+ } else if (spec === "$") {
+ index = tabs.length - 1;
+ } else if (/^[+-]\d+$/.test(spec)) {
+ index = tabs.indexOf(gBrowser.mCurrentTab) + parseInt(spec, 10);
+ } else {
+ return;
+ }
+ let length = tabs.length;
+ if (index > length - 1)
+ index = wrap ? index % length : length - 1;
+ else if (index < 0)
+ index = wrap ? index % length + length : 0;
+
+ gBrowser.mTabContainer.selectedItem = tabs[index];
+} // }}}
+
+/**
+ * @method switchTo {{{
+ * tabs.switchTo 相当の関数
+ * @param {String} buffer
+ * @param {Boolean} allowNonUnique
+ * @param {Number} count
+ * @param {Boolean} reverse
+ */
+function switchTo (buffer, allowNonUnique, count, reverse) {
+ if (buffer == "")
+ return null;
+ if (buffer != null) {
+ tabs._lastBufferSwitchArgs = buffer;
+ tabs._lastBufferSwitchSpecial = allowNonUnique;
+ } else {
+ buffer = this._lastBufferSwitchArgs;
+ if (allowNonUnique === void(0) || allowNonUnique === null)
+ allowNonUnique = tabs._lastBufferSwitchSpecial;
+ }
+
+ if (buffer == "#") {
+ tabs.selectAlternateTab();
+ return;
+ }
+
+ let tab = searchTab(buffer);
+ if (tab) {
+ tabs.select(tab._tPos, false);
+ return;
+ }
+
+ if (!count || count < 1)
+ count = 1;
+ reverse = !!reverse;
+
+ m = [];
+ let lowerBuffer = buffer.toLowerCase();
+ let first = tabs.index() + (reverse ? 0 : 1);
+ let length = config.tabbrowser.browsers.length;
+ for (let [i, ] in tabs.browsers) {
+ let index = (i + first) % length;
+ let browser = config.tabbrowser.browsers[index];
+ let url, title;
+ if ("__SS_restoreState" in browser) {
+ let entry = browser.__SS_data.entries[0];
+ url = entry.url;
+ title = entry.title;
+ } else {
+ url = browser.contentDocument.location.href;
+ title = browser.contentDocument.title;
+ }
+ title = title.toLowerCase();
+ if (url == buffer) {
+ tabs.select(index, false);
+ return;
+ }
+ if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0)
+ m.push(index);
+ }
+ if (m.length == 0)
+ liberator.echoerr("E94: No matching buffer for " + buffer);
+ else if (m.length > 1 && !allowNonUnique)
+ liberator.echoerr("E93: More than one match for " + buffer);
+ else {
+ if (reverse) {
+ index = m.length - count;
+ while (index < 0)
+ index + m.length;
+ } else {
+ index = (count - 1) % m.length;
+ }
+ tabs.select(m[index], false);
+ }
+} // }}}
+
+
+/**
+ * @method searchTab {{{
+ * @param {String} buffer
+ * - "{Number}:"
+ * - "{GroupName} {Number}:"
+ * @return {Element|null}
+ */
+function searchTab (buffer) {
+ if (buffer == "#") {
+ if (tabs.alternate != null && tabs.getTab() != tabs.alternate)
+ return tabs.alternate;
+ return null;
+ }
+ let m = buffer.match(/^(\d+):?/);
+ if (m)
+ return tabs.getTab(parseInt(m[1], 10) -1);
+ m = buffer.match(/^(.+?)\s+(\d+):?/);
+ if (m) {
+ let [, groupName, tabNum] = m;
+ tabNum = parseInt(tabNum, 10);
+ let group = getGroupByName(groupName)[0];
+ if (!group)
+ return null;
+ let tabItem = group.getChild(tabNum -1);
+ if (!tabItem)
+ return null;
+ return tabItem.tab;
+ }
+ return null;
+} // }}}
+
+let TV = window.TabView;
+/**
+ * @type {Window} TabView._window {{{
+ */
+this.__defineGetter__("tabView", function() {
+ if (TV && TV._window && TV._window.GroupItems) {
+ delete this.tabView;
+ this.tabView = TV._window;
+ return TV._window;
+ } else {
+ let wating = true;
+ TV._initFrame(function(){ wating = false; })
+ while (wating)
+ liberator.threadYield(false, true);
+ return this.tabView;
+ }
+}); // }}}
+
+/**
+ * @type {Array} Array of AppTabs
+ */
+this.__defineGetter__("appTabs", function() gBrowser.visibleTabs.filter(function(t) t.pinned));
+
+/**
+ * @method createGroup {{{
+ * @param {String} name GroupName
+ * @return {GroupItem}
+ */
+function createGroup (name) {
+ let pageBounds = tabView.Items.getPageBounds();
+ pageBounds.inset(20, 20);
+ let box = new tabView.Rect(pageBounds);
+ box.width = 50;
+ box.height = 50;
+ let group = new tabView.GroupItem([], { bounds: box, title: name });
+ if (name && group.$title.hasClass("defaultName"))
+ group.$title.removeClass("defaultName");
+ return group;
+} // }}}
+
+/**
+ * @param {String|Number} name GroupName or GroupId
+ * @return {GroupItem[]}
+ */
+function getGroupByName (name) {
+ if (typeof name === "number")
+ return tabView.GroupItems.groupItems.filter(function(g) g.id == name);
+ return tabView.GroupItems.groupItems.filter(function(g) g.getTitle() == name);
+}
+/**
+ * @param {Element} tab
+ * @param {GroupItem|Number} group GroupItem object or group id
+ */
+function tabMoveToGroup (tab, group) {
+ let id = (typeof group == "object") ? group.id : group;
+ tabView.GroupItems.moveTabToGroupItem(tab, id);
+}
+
+/**
+ * @method switchToGroup {{{
+ * @param {String|Number} spec
+ * @param {Boolean} wrap
+ */
+function switchToGroup (spec, wrap) {
+ const GI = tabView.GroupItems
+ let current = GI.getActiveGroupItem() || GI.getActiveOrphanTab();
+ let groupsAndOrphans = GI.groupItems.concat(GI.getOrphanedTabs());
+ let offset = 1, relative = false, index;
+ if (typeof spec === "number")
+ index = parseInt(spec, 10);
+ else if (/^[+-]\d+$/.test(spec)) {
+ let buf = parseInt(spec, 10);
+ index = groupsAndOrphans.indexOf(current) + buf;
+ offset = buf >= 0 ? 1 : -1;
+ relative = true;
+ } else if (spec != "") {
+ if (/^\d+$/.test(spec))
+ spec = parseInt(spec, 10);
+ let targetGroup = getGroupByName(spec)[0];
+ if (targetGroup)
+ index = groupsAndOrphans.indexOf(targetGroup);
+ else {
+ liberator.echoerr("No such group: " + spec);
+ return;
+ }
+ } else {
+ return;
+ }
+ let length = groupsAndOrphans.length;
+ let apps = appTabs;
+ function groupSwitch (index, wrap) {
+ if (index > length - 1)
+ index = wrap ? index % length : length - 1;
+ else if (index < 0)
+ index = wrap ? index % length + length : 0;
+
+ let target = groupsAndOrphans[index],
+ group = null;
+ if (target instanceof tabView.GroupItem) {
+ group = target;
+ target = target.getActiveTab() || target.getChild(0);
+ }
+
+ if (target) {
+ gBrowser.mTabContainer.selectedItem = target.tab;
+ } else if (group && apps.length != 0) {
+ GI.setActiveGroupItem(group);
+ tabView.UI.goToTab(tabs.getTab(0));
+ } else if (relative) {
+ groupSwitch(index + offset, true);
+ } else {
+ liberator.echoerr("Cannot switch to " + spec);
+ return;
+ }
+ }
+ groupSwitch(index, wrap);
+} // }}}
+
+// ============================================================================
+// Mappings {{{
+// ============================================================================
+
+/**
+ * {count}g@ select {count} of AppTab
+ * g@ select AppTab,
+ * if already selected, select the next AppTab
+ */
+mappings.add([modes.NORMAL], ["g@"],
+ "Go to AppTab",
+ function (count) {
+ let apps = appTabs;
+ let i = 0;
+ if (count != null)
+ i = count;
+ else {
+ let currentTab = tabs.getTab();
+ if (currentTab.pinned)
+ i = apps.indexOf(currentTab) + 1;
+ i %= apps.length;
+ }
+ if (apps[i])
+ selectVisible(i);
+ }, { count: true });
+
+/**
+ * Switch to next group
+ */
+mappings.add([modes.NORMAL], ["