462 lines
12 KiB
JavaScript
462 lines
12 KiB
JavaScript
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Tree Style Tab.
|
|
*
|
|
* The Initial Developer of the Original Code is YUKI "Piro" Hiroshi.
|
|
* Portions created by the Initial Developer are Copyright (C) 2010-2014
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s): YUKI "Piro" Hiroshi <piro.outsider.reflex@gmail.com>
|
|
* Tetsuharu OHZEKI <https://github.com/saneyuki>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ******/
|
|
|
|
var EXPORTED_SYMBOLS = ['GroupTab'];
|
|
|
|
const Cc = Components.classes;
|
|
const Ci = Components.interfaces;
|
|
|
|
Components.utils.import('resource://treestyletab-modules/lib/inherit.jsm');
|
|
Components.utils.import('resource://treestyletab-modules/base.js');
|
|
Components.utils.import('resource://treestyletab-modules/pseudoTreeBuilder.js');
|
|
Components.utils.import('resource://treestyletab-modules/tabAttributesObserver.js');
|
|
|
|
function GroupTab(aWindow)
|
|
{
|
|
this.window = aWindow;
|
|
this.init();
|
|
}
|
|
|
|
GroupTab.prototype = inherit(TreeStyleTabBase, {
|
|
|
|
initialized : false,
|
|
shouldUpdate : false,
|
|
|
|
window : null,
|
|
get document()
|
|
{
|
|
return this.window && this.window.document;
|
|
},
|
|
get location()
|
|
{
|
|
return this.window && this.window.location;
|
|
},
|
|
get locationSearch()
|
|
{
|
|
return this.location.href.replace(/^[^\?]+/, '');
|
|
},
|
|
|
|
get label()
|
|
{
|
|
return this.document.getElementById('label');
|
|
},
|
|
get editor()
|
|
{
|
|
return this.document.getElementById('editor');
|
|
},
|
|
get deck()
|
|
{
|
|
return this.document.getElementById('deck');
|
|
},
|
|
get temporaryCheck()
|
|
{
|
|
return this.document.getElementById('temporary');
|
|
},
|
|
|
|
get title()
|
|
{
|
|
if (this._title === null) {
|
|
let locationSearch = this.locationSearch;
|
|
let title = locationSearch.match(/(?:^|[\?&;])title=([^&;]*)/i);
|
|
if (title)
|
|
title = title[1];
|
|
// for old style URIs
|
|
if (!title && !/(?:^|[\?&;])temporary=/i.test(locationSearch))
|
|
title = locationSearch.replace(/^\?/, '');
|
|
this._title = (title) ?
|
|
this.trim(decodeURIComponent(title)) :
|
|
'' ;
|
|
}
|
|
return this._title;
|
|
},
|
|
set title(aValue) {
|
|
this._title = aValue;
|
|
this.document.title = this.label.value = aValue;
|
|
this.label.setAttribute('tooltiptext', aValue);
|
|
this.document.documentElement.setAttribute('title', aValue);
|
|
this._updateURI();
|
|
return aValue;
|
|
},
|
|
_title : null,
|
|
|
|
get temporary() {
|
|
return /(?:^|[\?&;])temporary=(?:1|yes|true)/i.test(this.locationSearch);
|
|
},
|
|
set temporary(aValue) {
|
|
aValue = !!aValue;
|
|
this._updateURI({ temporary: aValue });
|
|
this.temporaryCheck.checked = aValue;
|
|
return aValue;
|
|
},
|
|
|
|
_updateURI : function GT_updateURI(aOptions) {
|
|
aOptions = aOptions || {};
|
|
var temporary = this.temporary;
|
|
if ('temporary' in aOptions)
|
|
temporary = aOptions.temporary;
|
|
this.location.replace(
|
|
this.location.href.split('?')[0] + '?' +
|
|
'title=' + encodeURIComponent(this.title) + '&' +
|
|
'temporary=' + temporary
|
|
);
|
|
},
|
|
|
|
get browser()
|
|
{
|
|
var ownerWindow = this.window
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell)
|
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
|
.parent;
|
|
if (ownerWindow) {
|
|
ownerWindow = ownerWindow
|
|
.QueryInterface(Ci.nsIWebNavigation)
|
|
.document
|
|
.defaultView;
|
|
return ownerWindow.gBrowser;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
getOwnerTab : function GT_getOwnerTab(aWindow)
|
|
{
|
|
var b = this.browser;
|
|
return b &&
|
|
b.treeStyleTab &&
|
|
b.treeStyleTab.getTabFromFrame(aWindow || this.window);
|
|
},
|
|
|
|
|
|
init : function GT_init()
|
|
{
|
|
var title = this.title;
|
|
if (title) {
|
|
this.document.title = title;
|
|
this.document.documentElement.setAttribute('title', title);
|
|
this.label.value = title;
|
|
this.label.setAttribute('tooltiptext', title);
|
|
this.editor.value = title;
|
|
}
|
|
|
|
this.temporaryCheck.checked = this.temporary;
|
|
|
|
this.window.addEventListener('load', this, false);
|
|
|
|
this.window.groupTab = this;
|
|
},
|
|
|
|
|
|
trim : function GT_trim(aString)
|
|
{
|
|
return aString.replace(/^\s+|\s+$/g, '');
|
|
},
|
|
|
|
getTabById : function GT_getTabById(aId)
|
|
{
|
|
var b = this.browser;
|
|
if (aId && b)
|
|
return b.treeStyleTab.getTabById(aId);
|
|
return null;
|
|
},
|
|
|
|
|
|
enterEdit : function GT_enterEdit()
|
|
{
|
|
if (this.deck.selectedIndex == 1) return;
|
|
|
|
this.editor.value = this.label.value;
|
|
this.deck.selectedIndex = 1;
|
|
this.editor.focus();
|
|
this.editor.select();
|
|
},
|
|
|
|
exitEdit : function GT_exitEdit()
|
|
{
|
|
if (this.deck.selectedIndex == 0) return;
|
|
|
|
var old = this.trim(this.label.value);
|
|
var value = this.trim(this.editor.value);
|
|
|
|
if (value != old)
|
|
this.title = value;
|
|
|
|
this.editor.blur();
|
|
this.deck.selectedIndex = 0;
|
|
},
|
|
|
|
|
|
updateTree : function GT_updateTree()
|
|
{
|
|
if (!this.window || this.window.closed)
|
|
return;
|
|
|
|
var tree = this.document.getElementById('tree');
|
|
|
|
var range = this.document.createRange();
|
|
range.selectNodeContents(tree);
|
|
range.deleteContents();
|
|
range.detach();
|
|
|
|
var contents = PseudoTreeBuilder.build(this.getOwnerTab());
|
|
if (contents)
|
|
tree.appendChild(contents);
|
|
|
|
this.onResize();
|
|
},
|
|
|
|
checkUpdateTreeNow : function GT_checkUpdateTreeNow()
|
|
{
|
|
if (this.getOwnerTab().selected)
|
|
this.window.setTimeout(function(aSelf) {
|
|
if (aSelf.window && !aSelf.window.closed)
|
|
aSelf.onTabSelect();
|
|
}, 0, this);
|
|
},
|
|
|
|
|
|
handleEvent : function GT_handleEvent(aEvent)
|
|
{
|
|
switch (aEvent.type)
|
|
{
|
|
case 'load':
|
|
if (aEvent.currentTarget == this.window)
|
|
this.onLoad(aEvent);
|
|
return;
|
|
|
|
case 'unload':
|
|
case 'TabClose':
|
|
return this.onUnload(aEvent);
|
|
|
|
case 'click':
|
|
return this.onClick(aEvent);
|
|
case 'dblclick':
|
|
return this.onDblClick(aEvent);
|
|
case 'keypress':
|
|
return this.onKeyPress(aEvent);
|
|
|
|
case PseudoTreeBuilder.kTAB_LINK_CLICK:
|
|
return this.onItemClick(aEvent);
|
|
|
|
case 'TabSelect':
|
|
return this.onTabSelect(aEvent);
|
|
|
|
case 'resize':
|
|
return this.onResize();
|
|
|
|
case this.kEVENT_TYPE_ATTACHED:
|
|
return this.onTabAttached(aEvent);
|
|
case this.kEVENT_TYPE_DETACHED:
|
|
return this.onTabDetached(aEvent);
|
|
}
|
|
},
|
|
|
|
onLoad : function GT_onLoad(aEvent)
|
|
{
|
|
this.window.removeEventListener('load', this, false);
|
|
|
|
if (this.initialized)
|
|
return;
|
|
|
|
var tab = this.getOwnerTab();
|
|
|
|
this.updateTree();
|
|
|
|
this.window.addEventListener('unload', this, false);
|
|
this.window.addEventListener('click', this, false);
|
|
this.window.addEventListener('dblclick', this, false);
|
|
this.window.addEventListener('resize', this, false);
|
|
|
|
tab.addEventListener('TabSelect', this, false);
|
|
tab.addEventListener('TabClose', this, false);
|
|
tab.parentNode.addEventListener(this.kEVENT_TYPE_ATTACHED, this, false);
|
|
tab.parentNode.addEventListener(this.kEVENT_TYPE_DETACHED, this, false);
|
|
|
|
this.tabsObserver = new TabAttributesObserver({
|
|
container : this.browser.tabContainer,
|
|
attributes : 'label,visibleLabel,image',
|
|
callback : (function(aTab) {
|
|
this.onTabModified(aTab);
|
|
}).bind(this)
|
|
});
|
|
|
|
this.editor.addEventListener('keypress', this, false);
|
|
|
|
this.document.getElementById('tree')
|
|
.addEventListener(PseudoTreeBuilder.kTAB_LINK_CLICK, this, false);
|
|
|
|
this.initialized = true;
|
|
},
|
|
|
|
onUnload : function GT_onUnload(aEvent)
|
|
{
|
|
var tab = this.getOwnerTab();
|
|
if (!this.initialized || !tab)
|
|
return;
|
|
|
|
this.window.removeEventListener('unload', this, false);
|
|
this.window.removeEventListener('click', this, false);
|
|
this.window.removeEventListener('dblclick', this, false);
|
|
this.window.removeEventListener('resize', this, false);
|
|
|
|
tab.removeEventListener('TabSelect', this, false);
|
|
tab.removeEventListener('TabClose', this, false);
|
|
tab.parentNode.removeEventListener(this.kEVENT_TYPE_ATTACHED, this, false);
|
|
tab.parentNode.removeEventListener(this.kEVENT_TYPE_DETACHED, this, false);
|
|
|
|
this.tabsObserver.destroy();
|
|
delete this.tabsObserver;
|
|
|
|
this.editor.removeEventListener('keypress', this, false);
|
|
|
|
this.document.getElementById('tree')
|
|
.removeEventListener(PseudoTreeBuilder.kTAB_LINK_CLICK, this, false);
|
|
|
|
delete this.window.groupTab;
|
|
delete this.window;
|
|
},
|
|
|
|
onClick : function GT_onClick(aEvent)
|
|
{
|
|
if (aEvent.target == this.editor) return;
|
|
if (aEvent.target == this.label)
|
|
this.enterEdit();
|
|
else
|
|
this.exitEdit();
|
|
},
|
|
|
|
onDblClick : function GT_onDblClick(aEvent)
|
|
{
|
|
if (aEvent.target == this.editor) return;
|
|
if (this.deck.selectedIndex == 0)
|
|
this.enterEdit();
|
|
else
|
|
this.exitEdit();
|
|
},
|
|
|
|
onKeyPress : function GT_onKeyPress(aEvent)
|
|
{
|
|
if (aEvent.keyCode == aEvent.DOM_VK_ENTER ||
|
|
aEvent.keyCode == aEvent.DOM_VK_RETURN ||
|
|
aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
|
|
this.exitEdit();
|
|
},
|
|
|
|
onItemClick : function GT_onItemClick(aEvent)
|
|
{
|
|
var b = this.browser;
|
|
if (!b)
|
|
return;
|
|
|
|
var tab = this.getTabById(aEvent.detail.id);
|
|
if (!tab)
|
|
return;
|
|
|
|
var event = aEvent.detail.sourceEvent;
|
|
if (event.button == 1 ||
|
|
(event.button == 0 && this.isAccelKeyPressed(event)))
|
|
b.removeTab(tab);
|
|
else if (event.button != 2)
|
|
b.selectedTab = tab;
|
|
},
|
|
|
|
onTabSelect : function GT_onTabSelect(aEvent)
|
|
{
|
|
if (this.shouldUpdate)
|
|
this.updateTree();
|
|
|
|
this.shouldUpdate = false;
|
|
},
|
|
|
|
onResize : function GT_onResize()
|
|
{
|
|
var container = this.document.getElementById('tree');
|
|
var tree = container.firstChild;
|
|
|
|
var style = tree.style;
|
|
var height = tree.clientHeight * (tree.columnCount || 1);
|
|
if (height > container.boxObject.height) {
|
|
tree.columnCount = style.columnCount = style.MozColumnCount = 2;
|
|
var maxWidth = container.boxObject.width;
|
|
style.columnWidth = style.MozColumnWidth = Math.floor(maxWidth * 0.45)+'px';
|
|
style.columnGap = style.MozColumnGap = Math.floor(maxWidth * 0.05)+'px';
|
|
}
|
|
else {
|
|
tree.columnCount = 1;
|
|
style.columnCount = style.MozColumnCount =
|
|
style.columnWidth = style.MozColumnWidth =
|
|
style.columnGap = style.MozColumnGap = '';
|
|
}
|
|
|
|
var items = this.document.querySelectorAll('*|*.' + PseudoTreeBuilder.kTREEITEM);
|
|
Array.forEach(items, function(aItem) {
|
|
aItem.style.minHeight = (aItem.lastChild.boxObject.height + 1) + 'px';
|
|
}, this);
|
|
},
|
|
|
|
onTabAttached : function GT_onTabAttached(aEvent)
|
|
{
|
|
var tab = aEvent.detail.parentTab;
|
|
var id = tab.getAttribute(this.kID);
|
|
if (tab == this.getOwnerTab() ||
|
|
this.document.getElementsByAttribute('tab-id', id).length)
|
|
this.shouldUpdate = true;
|
|
|
|
this.checkUpdateTreeNow();
|
|
},
|
|
|
|
onTabDetached : function GT_onTabDetached(aEvent)
|
|
{
|
|
var tab = aEvent.originalTarget;
|
|
var id = tab.getAttribute(this.kID);
|
|
if (this.document.getElementsByAttribute('tab-id', id).length)
|
|
this.shouldUpdate = true;
|
|
|
|
this.checkUpdateTreeNow();
|
|
},
|
|
|
|
onTabModified : function GT_onTabModified(aTab)
|
|
{
|
|
if (aTab) {
|
|
let id = aTab.getAttribute(this.kID);
|
|
if (this.document.getElementsByAttribute('tab-id', id).length)
|
|
this.shouldUpdate = true;
|
|
}
|
|
|
|
this.checkUpdateTreeNow();
|
|
}
|
|
});
|