2010-06-16 17:19:11 -07:00
|
|
|
/* ***** 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.
|
|
|
|
*
|
2010-08-06 15:46:55 -07:00
|
|
|
* The Original Code is groupItems.js.
|
2010-06-16 17:19:11 -07:00
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Ian Gilman <ian@iangilman.com>.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Aza Raskin <aza@mozilla.com>
|
|
|
|
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
|
|
|
* Ehsan Akhgari <ehsan@mozilla.com>
|
2010-07-14 19:40:46 -07:00
|
|
|
* Raymond Lee <raymond@appcoast.com>
|
2010-06-16 17:19:11 -07:00
|
|
|
*
|
|
|
|
* 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 ***** */
|
|
|
|
|
|
|
|
// **********
|
2010-08-06 15:46:55 -07:00
|
|
|
// Title: groupItems.js
|
2010-06-16 17:19:11 -07:00
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ##########
|
2010-08-06 15:46:55 -07:00
|
|
|
// Class: GroupItem
|
|
|
|
// A single groupItem in the TabView window. Descended from <Item>.
|
2010-05-26 11:29:31 -07:00
|
|
|
// Note that it implements the <Subscribable> interface.
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
2010-06-11 15:08:14 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Constructor: GroupItem
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
|
|
|
// Parameters:
|
2010-08-06 15:46:55 -07:00
|
|
|
// listOfEls - an array of DOM elements for tabs to be added to this groupItem
|
|
|
|
// options - various options for this groupItem (see below). In addition, gets passed
|
2010-07-18 08:58:10 -07:00
|
|
|
// to <add> along with the elements provided.
|
|
|
|
//
|
|
|
|
// Possible options:
|
2010-08-06 15:46:55 -07:00
|
|
|
// id - specifies the groupItem's id; otherwise automatically generated
|
2010-06-11 15:08:14 -07:00
|
|
|
// locked - see <Item.locked>; default is {}
|
|
|
|
// userSize - see <Item.userSize>; default is null
|
|
|
|
// bounds - a <Rect>; otherwise based on the locations of the provided elements
|
2010-08-06 15:46:55 -07:00
|
|
|
// container - a DOM element to use as the container for this groupItem; otherwise will create
|
|
|
|
// title - the title for the groupItem; otherwise blank
|
|
|
|
// dontPush - true if this groupItem shouldn't push away on creation; default is false
|
|
|
|
window.GroupItem = function GroupItem(listOfEls, options) {
|
2010-05-26 11:29:31 -07:00
|
|
|
try {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(options) == 'undefined')
|
2010-05-26 11:29:31 -07:00
|
|
|
options = {};
|
|
|
|
|
|
|
|
this._inited = false;
|
|
|
|
this._children = []; // an array of Items
|
|
|
|
this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
|
2010-08-06 15:46:55 -07:00
|
|
|
this.isAGroupItem = true;
|
|
|
|
this.id = options.id || GroupItems.getNextID();
|
2010-05-26 11:29:31 -07:00
|
|
|
this._isStacked = false;
|
|
|
|
this._stackAngles = [0];
|
|
|
|
this.expanded = null;
|
|
|
|
this.locked = (options.locked ? Utils.copy(options.locked) : {});
|
|
|
|
this.topChild = null;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-19 17:45:23 -07:00
|
|
|
this.keepProportional = false;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
// Variable: _activeTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// The <TabItem> for the groupItem's active tab.
|
2010-05-25 19:18:25 -07:00
|
|
|
this._activeTab = null;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-08 15:32:08 -07:00
|
|
|
// Variables: xDensity, yDensity
|
|
|
|
// "density" ranges from 0 to 1, with 0 being "not dense" = "squishable" and 1 being "dense"
|
|
|
|
// = "not squishable". For example, if there is extra space in the vertical direction,
|
|
|
|
// yDensity will be < 1. These are set by <GroupItem.arrange>, as it is dependent on the tab items
|
|
|
|
// inside the groupItem.
|
2010-07-06 11:42:23 -07:00
|
|
|
this.xDensity = 0;
|
|
|
|
this.yDensity = 0;
|
|
|
|
|
2010-07-22 12:46:51 -07:00
|
|
|
if (Utils.isPoint(options.userSize))
|
2010-05-26 11:29:31 -07:00
|
|
|
this.userSize = new Point(options.userSize);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
var rectToBe;
|
2010-07-17 22:03:31 -07:00
|
|
|
if (options.bounds) {
|
2010-07-22 12:46:51 -07:00
|
|
|
Utils.assert("options.bounds must be a Rect",Utils.isRect(options.bounds));
|
2010-05-26 11:29:31 -07:00
|
|
|
rectToBe = new Rect(options.bounds);
|
2010-07-17 22:03:31 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!rectToBe) {
|
2010-08-06 15:46:55 -07:00
|
|
|
rectToBe = GroupItems.getBoundingBox(listOfEls);
|
2010-07-30 02:54:30 -07:00
|
|
|
rectToBe.inset(-30, -30);
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
var $container = options.container;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!$container) {
|
2010-06-04 15:08:24 -07:00
|
|
|
$container = iQ('<div>')
|
2010-08-06 15:46:55 -07:00
|
|
|
.addClass('groupItem')
|
2010-05-26 11:29:31 -07:00
|
|
|
.css({position: 'absolute'})
|
|
|
|
.css(rectToBe);
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
this.bounds = $container.bounds();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-19 11:56:07 -07:00
|
|
|
this.isDragging = false;
|
2010-05-26 11:29:31 -07:00
|
|
|
$container
|
|
|
|
.css({zIndex: -100})
|
2010-06-04 15:08:24 -07:00
|
|
|
.appendTo("body");
|
|
|
|
/* .dequeue(); */
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ___ New Tab Button
|
2010-06-04 15:08:24 -07:00
|
|
|
this.$ntb = iQ("<div>")
|
2010-05-26 11:29:31 -07:00
|
|
|
.appendTo($container);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
this.$ntb
|
2010-08-04 22:43:05 -07:00
|
|
|
.addClass('newTabButton')
|
2010-07-30 02:54:30 -07:00
|
|
|
.click(function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.newTab();
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-28 22:02:26 -07:00
|
|
|
(this.$ntb)[0].title = 'New tab';
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Resizer
|
2010-06-04 15:08:24 -07:00
|
|
|
this.$resizer = iQ("<div>")
|
|
|
|
.addClass('resizer')
|
2010-05-26 11:29:31 -07:00
|
|
|
.css({
|
|
|
|
position: "absolute",
|
|
|
|
width: 16, height: 16,
|
|
|
|
bottom: 0, right: 0,
|
|
|
|
})
|
|
|
|
.appendTo($container)
|
|
|
|
.hide();
|
|
|
|
|
|
|
|
// ___ Titlebar
|
|
|
|
var html =
|
2010-06-04 15:08:24 -07:00
|
|
|
"<div class='title-container'>" +
|
2010-07-18 08:58:10 -07:00
|
|
|
"<input class='name' value='" + (options.title || "") + "'/>" +
|
|
|
|
"<div class='title-shield' />" +
|
2010-06-22 16:47:02 -07:00
|
|
|
"</div>";
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
this.$titlebar = iQ('<div>')
|
|
|
|
.addClass('titlebar')
|
2010-07-18 08:58:10 -07:00
|
|
|
.html(html)
|
2010-05-26 11:29:31 -07:00
|
|
|
.appendTo($container);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$titlebar.css({
|
|
|
|
position: "absolute",
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-22 16:47:02 -07:00
|
|
|
var $close = iQ('<div>')
|
|
|
|
.addClass('close')
|
|
|
|
.click(function() {
|
|
|
|
self.closeAll();
|
|
|
|
})
|
|
|
|
.appendTo($container);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ___ Title
|
2010-06-04 15:08:24 -07:00
|
|
|
this.$titleContainer = iQ('.title-container', this.$titlebar);
|
|
|
|
this.$title = iQ('.name', this.$titlebar);
|
|
|
|
this.$titleShield = iQ('.title-shield', this.$titlebar);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var titleUnfocus = function() {
|
|
|
|
self.$titleShield.show();
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!self.getTitle()) {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.$title
|
|
|
|
.addClass("defaultName")
|
|
|
|
.val(self.defaultName);
|
|
|
|
} else {
|
2010-06-07 16:16:55 -07:00
|
|
|
self.$title
|
|
|
|
.css({"background":"none"})
|
|
|
|
.animate({
|
2010-06-21 18:05:46 -07:00
|
|
|
"padding-left": "1px"
|
2010-06-07 16:16:55 -07:00
|
|
|
}, {
|
2010-06-21 17:34:34 -07:00
|
|
|
duration: 200,
|
2010-07-29 12:37:25 -07:00
|
|
|
easing: "tabviewBounce"
|
2010-06-07 16:16:55 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
var handleKeyPress = function(e) {
|
2010-08-01 23:34:52 -07:00
|
|
|
if (e.which == 13 || e.which == 27) { // return & escape
|
2010-07-28 22:02:26 -07:00
|
|
|
(self.$title)[0].blur();
|
2010-06-04 15:08:24 -07:00
|
|
|
self.$title
|
2010-05-26 11:29:31 -07:00
|
|
|
.addClass("transparentBorder")
|
2010-07-30 02:54:30 -07:00
|
|
|
.one("mouseout", function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.$title.removeClass("transparentBorder");
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
} else
|
2010-05-26 11:29:31 -07:00
|
|
|
self.adjustTitleSize();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
self.save();
|
2010-06-25 16:00:51 -07:00
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$title
|
|
|
|
.css({backgroundRepeat: 'no-repeat'})
|
|
|
|
.blur(titleUnfocus)
|
|
|
|
.focus(function() {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (self.locked.title) {
|
2010-07-28 22:02:26 -07:00
|
|
|
(self.$title)[0].blur();
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
}
|
2010-07-28 22:02:26 -07:00
|
|
|
(self.$title)[0].select();
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!self.getTitle()) {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.$title
|
|
|
|
.removeClass("defaultName")
|
|
|
|
.val('');
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.keyup(handleKeyPress);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
titleUnfocus();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (this.locked.title)
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$title.addClass('name-locked');
|
|
|
|
else {
|
|
|
|
this.$titleShield
|
|
|
|
.mousedown(function(e) {
|
|
|
|
self.lastMouseDownTarget = (Utils.isRightClick(e) ? null : e.target);
|
|
|
|
})
|
2010-07-18 08:58:10 -07:00
|
|
|
.mouseup(function(e) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var same = (e.target == self.lastMouseDownTarget);
|
|
|
|
self.lastMouseDownTarget = null;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!same)
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
if (!self.isDragging) {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.$titleShield.hide();
|
2010-07-28 22:02:26 -07:00
|
|
|
(self.$title)[0].focus();
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-19 18:46:39 -07:00
|
|
|
// ___ Stack Expander
|
|
|
|
this.$expander = iQ("<img/>")
|
2010-07-29 12:37:25 -07:00
|
|
|
.attr("src", "chrome://browser/skin/tabview/stack-expander.png")
|
2010-06-19 18:46:39 -07:00
|
|
|
.addClass("stackExpander")
|
|
|
|
.appendTo($container)
|
2010-07-18 08:58:10 -07:00
|
|
|
.hide();
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ locking
|
2010-07-11 17:54:42 -07:00
|
|
|
if (this.locked.bounds)
|
2010-07-18 08:58:10 -07:00
|
|
|
$container.css({cursor: 'default'});
|
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (this.locked.close)
|
2010-05-26 11:29:31 -07:00
|
|
|
$close.hide();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Superclass initialization
|
2010-07-28 22:02:26 -07:00
|
|
|
this._init($container[0]);
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
if (this.$debug)
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$debug.css({zIndex: -1000});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Children
|
2010-07-18 08:58:10 -07:00
|
|
|
Array.prototype.forEach.call(listOfEls, function(el) {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.add(el, null, options);
|
|
|
|
});
|
|
|
|
|
|
|
|
// ___ Finish Up
|
|
|
|
this._addHandlers($container);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!this.locked.bounds)
|
2010-05-26 11:29:31 -07:00
|
|
|
this.setResizable(true);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.register(this);
|
2010-07-06 11:42:23 -07:00
|
|
|
|
2010-07-11 16:48:05 -07:00
|
|
|
// ___ Position
|
2010-07-22 19:24:31 -07:00
|
|
|
var immediately = $container ? true : false;
|
|
|
|
this.setBounds(rectToBe, immediately);
|
2010-07-03 18:13:31 -07:00
|
|
|
this.snap();
|
2010-07-17 22:03:31 -07:00
|
|
|
if ($container)
|
2010-07-22 19:24:31 -07:00
|
|
|
this.setBounds(rectToBe, immediately);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Push other objects away
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!options.dontPush)
|
2010-07-18 08:58:10 -07:00
|
|
|
this.pushAway();
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
this._inited = true;
|
|
|
|
this.save();
|
2010-07-30 02:54:30 -07:00
|
|
|
} catch(e) {
|
2010-08-06 15:46:55 -07:00
|
|
|
Utils.log("Error in GroupItem()");
|
2010-08-04 22:43:05 -07:00
|
|
|
Utils.log(e.stack);
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
window.GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Variable: defaultName
|
|
|
|
// The prompt text for the title field.
|
2010-08-06 15:46:55 -07:00
|
|
|
defaultName: "name this groupItem...",
|
2010-05-25 19:18:25 -07:00
|
|
|
|
|
|
|
// -----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: setActiveTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// Sets the active <TabItem> for this groupItem
|
2010-07-30 02:54:30 -07:00
|
|
|
setActiveTab: function(tab) {
|
2010-06-14 15:43:02 -07:00
|
|
|
Utils.assert('tab must be a TabItem', tab && tab.isATabItem);
|
2010-05-25 19:18:25 -07:00
|
|
|
this._activeTab = tab;
|
|
|
|
},
|
|
|
|
|
|
|
|
// -----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: getActiveTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// Gets the active <TabItem> for this groupItem
|
2010-07-30 02:54:30 -07:00
|
|
|
getActiveTab: function() {
|
2010-05-25 19:18:25 -07:00
|
|
|
return this._activeTab;
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: getStorageData
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns all of the info worth storing about this groupItem.
|
2010-05-26 11:29:31 -07:00
|
|
|
getStorageData: function() {
|
|
|
|
var data = {
|
2010-07-18 08:58:10 -07:00
|
|
|
bounds: this.getBounds(),
|
2010-05-26 11:29:31 -07:00
|
|
|
userSize: null,
|
2010-07-18 08:58:10 -07:00
|
|
|
locked: Utils.copy(this.locked),
|
2010-05-26 11:29:31 -07:00
|
|
|
title: this.getTitle(),
|
|
|
|
id: this.id
|
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-22 12:46:51 -07:00
|
|
|
if (Utils.isPoint(this.userSize))
|
2010-05-26 11:29:31 -07:00
|
|
|
data.userSize = new Point(this.userSize);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
return data;
|
|
|
|
},
|
|
|
|
|
2010-07-03 14:55:19 -07:00
|
|
|
// ----------
|
|
|
|
// Function: isEmpty
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns true if the tab groupItem is empty and unnamed.
|
2010-07-11 16:48:05 -07:00
|
|
|
isEmpty: function() {
|
2010-07-13 16:38:51 -07:00
|
|
|
return !this._children.length && !this.getTitle();
|
2010-07-11 16:48:05 -07:00
|
|
|
},
|
2010-07-03 14:55:19 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: save
|
2010-08-06 15:46:55 -07:00
|
|
|
// Saves this groupItem to persistent storage.
|
2010-05-26 11:29:31 -07:00
|
|
|
save: function() {
|
|
|
|
if (!this._inited) // too soon to save now
|
|
|
|
return;
|
|
|
|
|
|
|
|
var data = this.getStorageData();
|
2010-08-06 15:46:55 -07:00
|
|
|
if (GroupItems.groupItemStorageSanity(data))
|
|
|
|
Storage.saveGroupItem(gWindow, data);
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 14:52:25 -07:00
|
|
|
// Function: getTitle
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns the title of this groupItem as a string.
|
2010-05-26 11:29:31 -07:00
|
|
|
getTitle: function() {
|
|
|
|
var value = (this.$title ? this.$title.val() : '');
|
|
|
|
return (value == this.defaultName ? '' : value);
|
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-06-17 14:52:25 -07:00
|
|
|
// Function: setTitle
|
2010-08-06 15:46:55 -07:00
|
|
|
// Sets the title of this groupItem with the given string
|
2010-05-26 11:29:31 -07:00
|
|
|
setTitle: function(value) {
|
2010-07-18 08:58:10 -07:00
|
|
|
this.$title.val(value);
|
2010-05-26 11:29:31 -07:00
|
|
|
this.save();
|
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-06-17 14:52:25 -07:00
|
|
|
// Function: adjustTitleSize
|
2010-08-06 15:46:55 -07:00
|
|
|
// Used to adjust the width of the title box depending on groupItem width and title size.
|
2010-05-26 11:29:31 -07:00
|
|
|
adjustTitleSize: function() {
|
|
|
|
Utils.assert('bounds needs to have been set', this.bounds);
|
|
|
|
var w = Math.min(this.bounds.width - 35, Math.max(150, this.getTitle().length * 6));
|
|
|
|
var css = {width: w};
|
|
|
|
this.$title.css(css);
|
|
|
|
this.$titleShield.css(css);
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 14:52:25 -07:00
|
|
|
// Function: getContentBounds
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns a <Rect> for the groupItem's content area (which doesn't include the title, etc).
|
2010-05-26 11:29:31 -07:00
|
|
|
getContentBounds: function() {
|
|
|
|
var box = this.getBounds();
|
|
|
|
var titleHeight = this.$titlebar.height();
|
|
|
|
box.top += titleHeight;
|
|
|
|
box.height -= titleHeight;
|
|
|
|
box.inset(6, 6);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-08 15:32:08 -07:00
|
|
|
box.height -= 33; // For new tab button
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
return box;
|
2010-03-17 01:07:00 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 14:52:25 -07:00
|
|
|
// Function: setBounds
|
2010-06-17 15:57:45 -07:00
|
|
|
// Sets the bounds with the given <Rect>, animating unless "immediately" is false.
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
|
|
|
// Parameters:
|
2010-07-17 14:10:29 -07:00
|
|
|
// rect - a <Rect> giving the new bounds
|
|
|
|
// immediately - true if it should not animate; default false
|
|
|
|
// options - an object with additional parameters, see below
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
|
|
|
// Possible options:
|
2010-07-17 14:10:29 -07:00
|
|
|
// force - true to always update the DOM even if the bounds haven't changed; default false
|
|
|
|
setBounds: function(rect, immediately, options) {
|
2010-07-22 12:46:51 -07:00
|
|
|
if (!Utils.isRect(rect)) {
|
2010-08-06 15:46:55 -07:00
|
|
|
Utils.trace('GroupItem.setBounds: rect is not a real rectangle!', rect);
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-17 14:10:29 -07:00
|
|
|
if (!options)
|
|
|
|
options = {};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
rect.width = Math.max(110, rect.width);
|
|
|
|
rect.height = Math.max(125, rect.height);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var titleHeight = this.$titlebar.height();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Determine what has changed
|
|
|
|
var css = {};
|
|
|
|
var titlebarCSS = {};
|
|
|
|
var contentCSS = {};
|
|
|
|
|
2010-07-17 14:10:29 -07:00
|
|
|
if (rect.left != this.bounds.left || options.force)
|
2010-05-26 11:29:31 -07:00
|
|
|
css.left = rect.left;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-17 14:10:29 -07:00
|
|
|
if (rect.top != this.bounds.top || options.force)
|
2010-05-26 11:29:31 -07:00
|
|
|
css.top = rect.top;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-17 14:10:29 -07:00
|
|
|
if (rect.width != this.bounds.width || options.force) {
|
2010-05-26 11:29:31 -07:00
|
|
|
css.width = rect.width;
|
|
|
|
titlebarCSS.width = rect.width;
|
|
|
|
contentCSS.width = rect.width;
|
|
|
|
}
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-07-17 14:10:29 -07:00
|
|
|
if (rect.height != this.bounds.height || options.force) {
|
|
|
|
css.height = rect.height;
|
|
|
|
contentCSS.height = rect.height - titleHeight;
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-23 14:33:02 -07:00
|
|
|
if (Utils.isEmptyObject(css))
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var offset = new Point(rect.left - this.bounds.left, rect.top - this.bounds.top);
|
|
|
|
this.bounds = new Rect(rect);
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Deal with children
|
2010-07-11 17:54:42 -07:00
|
|
|
if (css.width || css.height) {
|
2010-05-26 11:29:31 -07:00
|
|
|
this.arrange({animate: !immediately}); //(immediately ? 'sometimes' : true)});
|
2010-07-11 17:54:42 -07:00
|
|
|
} else if (css.left || css.top) {
|
2010-07-13 16:38:51 -07:00
|
|
|
this._children.forEach(function(child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var box = child.getBounds();
|
|
|
|
child.setPosition(box.left + offset.x, box.top + offset.y, immediately);
|
|
|
|
});
|
|
|
|
}
|
2010-07-17 14:10:29 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ Update our representation
|
2010-07-11 17:54:42 -07:00
|
|
|
if (immediately) {
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(this.container).css(css);
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$titlebar.css(titlebarCSS);
|
|
|
|
} else {
|
2010-07-27 20:02:51 -07:00
|
|
|
TabItems.pausePainting();
|
2010-06-07 16:16:55 -07:00
|
|
|
iQ(this.container).animate(css, {
|
2010-07-18 08:58:10 -07:00
|
|
|
duration: 350,
|
2010-07-29 12:37:25 -07:00
|
|
|
easing: "tabviewBounce",
|
2010-06-07 16:16:55 -07:00
|
|
|
complete: function() {
|
2010-07-27 20:02:51 -07:00
|
|
|
TabItems.resumePainting();
|
2010-06-07 16:16:55 -07:00
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-07 16:16:55 -07:00
|
|
|
this.$titlebar.animate(titlebarCSS, {
|
|
|
|
duration: 350
|
2010-07-17 14:10:29 -07:00
|
|
|
});
|
2010-04-22 17:19:42 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.adjustTitleSize();
|
|
|
|
|
|
|
|
this._updateDebugBounds();
|
2010-08-08 15:32:08 -07:00
|
|
|
this.setTrenches(rect);
|
2010-06-15 11:53:53 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.save();
|
2010-04-01 17:20:59 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: setZ
|
2010-08-06 15:46:55 -07:00
|
|
|
// Set the Z order for the groupItem's container, as well as its children.
|
2010-05-26 11:29:31 -07:00
|
|
|
setZ: function(value) {
|
2010-07-07 14:12:04 -07:00
|
|
|
this.zIndex = value;
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(this.container).css({zIndex: value});
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
if (this.$debug)
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$debug.css({zIndex: value + 1});
|
|
|
|
|
|
|
|
var count = this._children.length;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (count) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var topZIndex = value + count + 1;
|
|
|
|
var zIndex = topZIndex;
|
|
|
|
var self = this;
|
2010-07-13 16:38:51 -07:00
|
|
|
this._children.forEach(function(child) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (child == self.topChild)
|
2010-05-26 11:29:31 -07:00
|
|
|
child.setZ(topZIndex + 1);
|
|
|
|
else {
|
|
|
|
child.setZ(zIndex);
|
|
|
|
zIndex--;
|
2010-05-18 17:08:45 -07:00
|
|
|
}
|
2010-04-30 17:24:03 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: close
|
2010-08-06 15:46:55 -07:00
|
|
|
// Closes the groupItem, removing (but not closing) all of its children.
|
2010-05-26 11:29:31 -07:00
|
|
|
close: function() {
|
|
|
|
this.removeAll();
|
2010-07-14 16:59:37 -07:00
|
|
|
this._sendToSubscribers("close");
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.unregister(this);
|
2010-06-19 11:56:07 -07:00
|
|
|
this.removeTrenches();
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(this.container).fadeOut(function() {
|
|
|
|
iQ(this).remove();
|
2010-05-26 11:29:31 -07:00
|
|
|
Items.unsquish();
|
2010-05-25 17:04:59 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
Storage.deleteGroupItem(gWindow, this.id);
|
2010-03-29 16:08:48 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: closeAll
|
2010-08-06 15:46:55 -07:00
|
|
|
// Closes the groupItem and all of its children.
|
2010-05-26 11:29:31 -07:00
|
|
|
closeAll: function() {
|
2010-06-14 17:23:17 -07:00
|
|
|
var self = this;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (this._children.length) {
|
2010-07-28 20:33:43 -07:00
|
|
|
var toClose = this._children.concat();
|
2010-07-13 16:38:51 -07:00
|
|
|
toClose.forEach(function(child) {
|
2010-07-27 20:02:51 -07:00
|
|
|
child.removeSubscriber(self, "close");
|
2010-05-26 11:29:31 -07:00
|
|
|
child.close();
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
}
|
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!this.locked.close)
|
2010-05-26 11:29:31 -07:00
|
|
|
this.close();
|
2010-04-26 16:48:46 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
// Function: add
|
2010-08-06 15:46:55 -07:00
|
|
|
// Adds an item to the groupItem.
|
2010-07-18 08:58:10 -07:00
|
|
|
// Parameters:
|
|
|
|
//
|
2010-05-26 11:29:31 -07:00
|
|
|
// a - The item to add. Can be an <Item>, a DOM element or a jQuery object.
|
|
|
|
// The latter two must refer to the container of an <Item>.
|
|
|
|
// dropPos - An object with left and top properties referring to the location dropped at. Optional.
|
2010-07-18 08:58:10 -07:00
|
|
|
// options - An object with optional settings for this call. Currently the only one is dontArrange.
|
2010-05-26 11:29:31 -07:00
|
|
|
add: function(a, dropPos, options) {
|
|
|
|
try {
|
|
|
|
var item;
|
|
|
|
var $el;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (a.isAnItem) {
|
2010-05-26 11:29:31 -07:00
|
|
|
item = a;
|
2010-07-18 08:58:10 -07:00
|
|
|
$el = iQ(a.container);
|
2010-05-26 11:29:31 -07:00
|
|
|
} else {
|
2010-06-04 15:08:24 -07:00
|
|
|
$el = iQ(a);
|
2010-05-26 11:29:31 -07:00
|
|
|
item = Items.item($el);
|
2010-06-15 16:08:21 -07:00
|
|
|
}
|
2010-08-06 15:46:55 -07:00
|
|
|
Utils.assertThrow("shouldn't already be in another groupItem", !item.parent || item.parent == this);
|
2010-07-15 17:23:39 -07:00
|
|
|
|
2010-06-19 11:56:07 -07:00
|
|
|
item.removeTrenches();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
if (!dropPos)
|
2010-05-26 11:29:31 -07:00
|
|
|
dropPos = {top:window.innerWidth, left:window.innerHeight};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(options) == 'undefined')
|
2010-05-26 11:29:31 -07:00
|
|
|
options = {};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var self = this;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
var wasAlreadyInThisGroupItem = false;
|
2010-07-13 16:38:51 -07:00
|
|
|
var oldIndex = this._children.indexOf(item);
|
2010-07-11 17:54:42 -07:00
|
|
|
if (oldIndex != -1) {
|
2010-07-18 08:58:10 -07:00
|
|
|
this._children.splice(oldIndex, 1);
|
2010-08-06 15:46:55 -07:00
|
|
|
wasAlreadyInThisGroupItem = true;
|
2010-03-24 16:27:56 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// TODO: You should be allowed to drop in the white space at the bottom and have it go to the end
|
|
|
|
// (right now it can match the thumbnail above it and go there)
|
2010-07-30 02:54:30 -07:00
|
|
|
function findInsertionPoint(dropPos) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (self.shouldStack(self._children.length + 1))
|
2010-05-26 11:29:31 -07:00
|
|
|
return 0;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var best = {dist: Infinity, item: null};
|
|
|
|
var index = 0;
|
|
|
|
var box;
|
2010-07-18 08:58:10 -07:00
|
|
|
self._children.forEach(function(child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
box = child.getBounds();
|
2010-07-11 17:54:42 -07:00
|
|
|
if (box.bottom < dropPos.top || box.top > dropPos.top)
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
var dist = Math.sqrt(Math.pow((box.top+box.height/2)-dropPos.top,2)
|
|
|
|
+ Math.pow((box.left+box.width/2)-dropPos.left,2));
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (dist <= best.dist) {
|
2010-05-26 11:29:31 -07:00
|
|
|
best.item = child;
|
|
|
|
best.dist = dist;
|
|
|
|
best.index = index;
|
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (self._children.length) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (best.item) {
|
2010-05-26 11:29:31 -07:00
|
|
|
box = best.item.getBounds();
|
|
|
|
var insertLeft = dropPos.left <= box.left + box.width/2;
|
2010-07-30 02:54:30 -07:00
|
|
|
if (!insertLeft)
|
2010-05-26 11:29:31 -07:00
|
|
|
return best.index+1;
|
2010-07-11 16:48:05 -07:00
|
|
|
return best.index;
|
|
|
|
}
|
|
|
|
return self._children.length;
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
return 0;
|
2010-05-24 17:14:10 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// Insert the tab into the right position.
|
|
|
|
var index = findInsertionPoint(dropPos);
|
2010-07-30 02:54:30 -07:00
|
|
|
this._children.splice(index, 0, item);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
item.setZ(this.getZ() + 1);
|
2010-08-06 15:46:55 -07:00
|
|
|
$el.addClass("tabInGroupItem");
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
if (!wasAlreadyInThisGroupItem) {
|
2010-06-22 16:42:06 -07:00
|
|
|
item.droppable(false);
|
2010-08-06 15:46:55 -07:00
|
|
|
item.groupItemData = {};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
item.addSubscriber(this, "close", function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.remove($el);
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
item.setParent(this);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(item.setResizable) == 'function')
|
2010-05-26 11:29:31 -07:00
|
|
|
item.setResizable(false);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-08 17:27:47 -07:00
|
|
|
if (item.tab == gBrowser.selectedTab) {
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.setActiveGroupItem(this);
|
2010-08-08 17:27:47 -07:00
|
|
|
GroupItems.updateTabBarForActiveGroupItem();
|
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-25 20:53:21 -07:00
|
|
|
if (!options.dontArrange) {
|
2010-05-26 11:29:31 -07:00
|
|
|
this.arrange();
|
2010-07-25 20:53:21 -07:00
|
|
|
}
|
2010-07-26 18:30:56 -07:00
|
|
|
UI.setReorderTabsOnHide(this);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (this._nextNewTabCallback) {
|
2010-05-26 11:29:31 -07:00
|
|
|
this._nextNewTabCallback.apply(this, [item])
|
|
|
|
this._nextNewTabCallback = null;
|
|
|
|
}
|
|
|
|
} catch(e) {
|
2010-08-06 15:46:55 -07:00
|
|
|
Utils.log('GroupItem.add error', e);
|
2010-05-25 17:04:59 -07:00
|
|
|
}
|
2010-04-22 09:38:37 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
// Function: remove
|
2010-08-06 15:46:55 -07:00
|
|
|
// Removes an item from the groupItem.
|
2010-07-18 08:58:10 -07:00
|
|
|
// Parameters:
|
|
|
|
//
|
2010-05-26 11:29:31 -07:00
|
|
|
// a - The item to remove. Can be an <Item>, a DOM element or a jQuery object.
|
|
|
|
// The latter two must refer to the container of an <Item>.
|
2010-07-18 08:58:10 -07:00
|
|
|
// options - An object with optional settings for this call. Currently the only one is dontArrange.
|
2010-05-26 11:29:31 -07:00
|
|
|
remove: function(a, options) {
|
2010-06-16 16:30:48 -07:00
|
|
|
try {
|
2010-07-18 08:58:10 -07:00
|
|
|
var $el;
|
2010-06-16 16:30:48 -07:00
|
|
|
var item;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (a.isAnItem) {
|
2010-06-16 16:30:48 -07:00
|
|
|
item = a;
|
|
|
|
$el = iQ(item.container);
|
|
|
|
} else {
|
2010-07-18 08:58:10 -07:00
|
|
|
$el = iQ(a);
|
2010-06-16 16:30:48 -07:00
|
|
|
item = Items.item($el);
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(options) == 'undefined')
|
2010-06-16 16:30:48 -07:00
|
|
|
options = {};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-13 16:38:51 -07:00
|
|
|
var index = this._children.indexOf(item);
|
2010-07-11 17:54:42 -07:00
|
|
|
if (index != -1)
|
2010-07-18 08:58:10 -07:00
|
|
|
this._children.splice(index, 1);
|
|
|
|
|
2010-06-16 16:30:48 -07:00
|
|
|
item.setParent(null);
|
2010-08-06 15:46:55 -07:00
|
|
|
item.removeClass("tabInGroupItem");
|
2010-06-16 16:30:48 -07:00
|
|
|
item.removeClass("stacked");
|
|
|
|
item.removeClass("stack-trayed");
|
|
|
|
item.setRotation(0);
|
|
|
|
item.setSize(item.defaultSize.x, item.defaultSize.y);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
item.droppable(true);
|
2010-07-27 20:02:51 -07:00
|
|
|
item.removeSubscriber(this, "close");
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(item.setResizable) == 'function')
|
2010-06-16 16:30:48 -07:00
|
|
|
item.setResizable(true);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (!this._children.length && !this.locked.close && !this.getTitle() && !options.dontClose) {
|
2010-06-16 16:30:48 -07:00
|
|
|
this.close();
|
2010-07-11 17:54:42 -07:00
|
|
|
} else if (!options.dontArrange) {
|
2010-06-16 16:30:48 -07:00
|
|
|
this.arrange();
|
|
|
|
}
|
|
|
|
} catch(e) {
|
|
|
|
Utils.log(e);
|
2010-04-22 09:38:37 -07:00
|
|
|
}
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-04-22 17:19:42 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: removeAll
|
2010-08-06 15:46:55 -07:00
|
|
|
// Removes all of the groupItem's children.
|
2010-04-22 17:19:42 -07:00
|
|
|
removeAll: function() {
|
2010-04-22 09:38:37 -07:00
|
|
|
var self = this;
|
2010-07-28 20:33:43 -07:00
|
|
|
var toRemove = this._children.concat();
|
2010-07-13 16:38:51 -07:00
|
|
|
toRemove.forEach(function(child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.remove(child, {dontArrange: true});
|
2010-04-22 09:38:37 -07:00
|
|
|
});
|
2010-04-22 12:51:48 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: setNewTabButtonBounds
|
2010-08-06 15:46:55 -07:00
|
|
|
// Used for positioning the "new tab" button in the "new tabs" groupItem.
|
2010-05-26 11:29:31 -07:00
|
|
|
setNewTabButtonBounds: function(box, immediately) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!immediately)
|
2010-07-17 14:10:29 -07:00
|
|
|
this.$ntb.animate(box.css(), {
|
2010-06-07 16:16:55 -07:00
|
|
|
duration: 320,
|
2010-07-29 12:37:25 -07:00
|
|
|
easing: "tabviewBounce"
|
2010-06-07 16:16:55 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
else
|
2010-07-17 14:10:29 -07:00
|
|
|
this.$ntb.css(box.css());
|
2010-05-04 16:44:03 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-19 18:46:39 -07:00
|
|
|
// ----------
|
2010-06-19 18:49:08 -07:00
|
|
|
// Function: hideExpandControl
|
2010-08-06 15:46:55 -07:00
|
|
|
// Hide the control which expands a stacked groupItem into a quick-look view.
|
2010-07-30 02:54:30 -07:00
|
|
|
hideExpandControl: function() {
|
2010-06-19 18:46:39 -07:00
|
|
|
this.$expander.hide();
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-19 18:49:08 -07:00
|
|
|
// Function: showExpandControl
|
2010-08-06 15:46:55 -07:00
|
|
|
// Show the control which expands a stacked groupItem into a quick-look view.
|
2010-07-30 02:54:30 -07:00
|
|
|
showExpandControl: function() {
|
2010-06-19 18:46:39 -07:00
|
|
|
var childBB = this.getChild(0).getBounds();
|
|
|
|
var dT = childBB.top - this.getBounds().top;
|
|
|
|
var dL = childBB.left - this.getBounds().left;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-19 18:46:39 -07:00
|
|
|
this.$expander
|
|
|
|
.show()
|
|
|
|
.css({
|
2010-06-21 14:49:38 -07:00
|
|
|
opacity: .2,
|
2010-06-19 18:46:39 -07:00
|
|
|
top: dT + childBB.height + Math.min(7, (this.getBounds().bottom-childBB.bottom)/2),
|
|
|
|
// TODO: Why the magic -6? because the childBB.width seems to be over-sizing itself.
|
|
|
|
// But who can blame an object for being a bit optimistic when self-reporting size.
|
|
|
|
// It has to impress the ladies somehow.
|
|
|
|
left: dL + childBB.width/2 - this.$expander.width()/2 - 6,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: shouldStack
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns true if the groupItem, given "count", should stack (instead of grid).
|
2010-05-26 11:29:31 -07:00
|
|
|
shouldStack: function(count) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (count <= 1)
|
2010-05-26 11:29:31 -07:00
|
|
|
return false;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var bb = this.getContentBounds();
|
|
|
|
var options = {
|
|
|
|
pretend: true,
|
2010-08-04 22:43:05 -07:00
|
|
|
count: count
|
2010-05-26 11:29:31 -07:00
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var rects = Items.arrange(null, bb, options);
|
2010-07-30 02:54:30 -07:00
|
|
|
return (rects[0].width < TabItems.minTabWidth * 1.35);
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-04-27 16:11:49 -07:00
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: arrange
|
2010-07-18 08:58:10 -07:00
|
|
|
// Lays out all of the children.
|
|
|
|
//
|
|
|
|
// Parameters:
|
2010-06-17 15:57:45 -07:00
|
|
|
// options - passed to <Items.arrange> or <_stackArrange>
|
2010-05-26 11:29:31 -07:00
|
|
|
arrange: function(options) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (this.expanded) {
|
2010-05-26 11:29:31 -07:00
|
|
|
this.topChild = null;
|
|
|
|
var box = new Rect(this.expanded.bounds);
|
|
|
|
box.inset(8, 8);
|
2010-07-23 14:33:02 -07:00
|
|
|
Items.arrange(this._children, box, Utils.extend({}, options, {padding: 8, z: 99999}));
|
2010-05-26 11:29:31 -07:00
|
|
|
} else {
|
|
|
|
var bb = this.getContentBounds();
|
|
|
|
var count = this._children.length;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!this.shouldStack(count)) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var animate;
|
2010-07-18 08:58:10 -07:00
|
|
|
if (!options || typeof(options.animate) == 'undefined')
|
2010-05-26 11:29:31 -07:00
|
|
|
animate = true;
|
2010-07-18 08:58:10 -07:00
|
|
|
else
|
2010-05-26 11:29:31 -07:00
|
|
|
animate = options.animate;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(options) == 'undefined')
|
2010-05-26 11:29:31 -07:00
|
|
|
options = {};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
this._children.forEach(function(child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
child.removeClass("stacked")
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.topChild = null;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var arrangeOptions = Utils.copy(options);
|
2010-07-23 14:33:02 -07:00
|
|
|
Utils.extend(arrangeOptions, {
|
2010-05-26 11:29:31 -07:00
|
|
|
pretend: true,
|
|
|
|
count: count
|
|
|
|
});
|
|
|
|
|
2010-08-08 15:32:08 -07:00
|
|
|
if (!count) {
|
2010-07-11 16:48:05 -07:00
|
|
|
this.xDensity = 0;
|
|
|
|
this.yDensity = 0;
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
2010-07-06 11:42:23 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var rects = Items.arrange(this._children, bb, arrangeOptions);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// yDensity = (the distance of the bottom of the last tab to the top of the content area)
|
2010-07-11 16:48:05 -07:00
|
|
|
// / (the total available content height)
|
|
|
|
this.yDensity = (rects[rects.length - 1].bottom - bb.top) / (bb.height);
|
|
|
|
|
|
|
|
// xDensity = (the distance from the left of the content area to the right of the rightmost
|
|
|
|
// tab) / (the total available content width)
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 16:48:05 -07:00
|
|
|
// first, find the right of the rightmost tab! luckily, they're in order.
|
|
|
|
// TODO: does this change for rtl?
|
|
|
|
var rightMostRight = 0;
|
2010-07-15 01:41:08 -07:00
|
|
|
for each (var rect in rects) {
|
2010-07-11 16:48:05 -07:00
|
|
|
if (rect.right > rightMostRight)
|
|
|
|
rightMostRight = rect.right;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
this.xDensity = (rightMostRight - bb.left) / (bb.width);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-13 16:38:51 -07:00
|
|
|
this._children.forEach(function(child, index) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!child.locked.bounds) {
|
2010-05-26 11:29:31 -07:00
|
|
|
child.setBounds(rects[index], !animate);
|
|
|
|
child.setRotation(0);
|
2010-07-11 17:54:42 -07:00
|
|
|
if (options.z)
|
2010-05-26 11:29:31 -07:00
|
|
|
child.setZ(options.z);
|
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this._isStacked = false;
|
|
|
|
} else
|
|
|
|
this._stackArrange(bb, options);
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (this._isStacked && !this.expanded) this.showExpandControl();
|
2010-06-19 18:46:39 -07:00
|
|
|
else this.hideExpandControl();
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: _stackArrange
|
2010-07-18 08:58:10 -07:00
|
|
|
// Arranges the children in a stack.
|
|
|
|
//
|
|
|
|
// Parameters:
|
2010-06-17 15:57:45 -07:00
|
|
|
// bb - <Rect> to arrange within
|
|
|
|
// options - see below
|
|
|
|
//
|
2010-07-18 08:58:10 -07:00
|
|
|
// Possible "options" properties:
|
2010-06-17 15:57:45 -07:00
|
|
|
// animate - whether to animate; default: true.
|
2010-07-18 08:58:10 -07:00
|
|
|
_stackArrange: function(bb, options) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var animate;
|
2010-07-18 08:58:10 -07:00
|
|
|
if (!options || typeof(options.animate) == 'undefined')
|
2010-05-26 11:29:31 -07:00
|
|
|
animate = true;
|
2010-07-18 08:58:10 -07:00
|
|
|
else
|
2010-05-26 11:29:31 -07:00
|
|
|
animate = options.animate;
|
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (typeof(options) == 'undefined')
|
2010-05-26 11:29:31 -07:00
|
|
|
options = {};
|
|
|
|
|
2010-05-25 17:04:59 -07:00
|
|
|
var count = this._children.length;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!count)
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var zIndex = this.getZ() + count + 1;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-06 11:42:23 -07:00
|
|
|
var Pi = Math.acos(-1);
|
|
|
|
var maxRotation = 35; // degress
|
2010-05-26 11:29:31 -07:00
|
|
|
var scale = 0.8;
|
|
|
|
var newTabsPad = 10;
|
|
|
|
var w;
|
2010-07-18 08:58:10 -07:00
|
|
|
var h;
|
2010-05-26 11:29:31 -07:00
|
|
|
var itemAspect = TabItems.tabHeight / TabItems.tabWidth;
|
|
|
|
var bbAspect = bb.height / bb.width;
|
2010-07-06 11:42:23 -07:00
|
|
|
|
|
|
|
// compute h and w. h and w are the dimensions of each of the tabs... in other words, the
|
|
|
|
// height and width of the entire stack, modulo rotation.
|
2010-08-06 15:46:55 -07:00
|
|
|
if (bbAspect > itemAspect) { // Tall, thin groupItem
|
2010-05-26 11:29:31 -07:00
|
|
|
w = bb.width * scale;
|
|
|
|
h = w * itemAspect;
|
2010-07-11 16:48:05 -07:00
|
|
|
// let's say one, because, even though there's more space, we're enforcing that with scale.
|
|
|
|
this.xDensity = 1;
|
|
|
|
this.yDensity = h / (bb.height * scale);
|
2010-08-06 15:46:55 -07:00
|
|
|
} else { // Short, wide groupItem
|
2010-05-26 11:29:31 -07:00
|
|
|
h = bb.height * scale;
|
|
|
|
w = h * (1 / itemAspect);
|
2010-07-11 16:48:05 -07:00
|
|
|
this.yDensity = 1;
|
|
|
|
this.xDensity = h / (bb.width * scale);
|
2010-05-23 23:42:18 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-06 11:42:23 -07:00
|
|
|
// x is the left margin that the stack will have, within the content area (bb)
|
|
|
|
// y is the vertical margin
|
2010-05-26 11:29:31 -07:00
|
|
|
var x = (bb.width - w) / 2;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var y = Math.min(x, (bb.height - h) / 2);
|
|
|
|
var box = new Rect(bb.left + x, bb.top + y, w, h);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var self = this;
|
|
|
|
var children = [];
|
2010-07-13 16:38:51 -07:00
|
|
|
this._children.forEach(function(child) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (child == self.topChild)
|
2010-05-26 11:29:31 -07:00
|
|
|
children.unshift(child);
|
|
|
|
else
|
|
|
|
children.push(child);
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-13 16:38:51 -07:00
|
|
|
children.forEach(function(child, index) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!child.locked.bounds) {
|
2010-05-26 11:29:31 -07:00
|
|
|
child.setZ(zIndex);
|
|
|
|
zIndex--;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
child.addClass("stacked");
|
|
|
|
child.setBounds(box, !animate);
|
2010-07-06 11:42:23 -07:00
|
|
|
child.setRotation(self._randRotate(maxRotation, index));
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
self._isStacked = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: _randRotate
|
|
|
|
// Random rotation generator for <_stackArrange>
|
2010-07-30 02:54:30 -07:00
|
|
|
_randRotate: function(spread, index) {
|
|
|
|
if (index >= this._stackAngles.length) {
|
|
|
|
var randAngle = 5*index + parseInt((Math.random()-.5)*1);
|
2010-05-26 11:29:31 -07:00
|
|
|
this._stackAngles.push(randAngle);
|
2010-07-18 08:58:10 -07:00
|
|
|
return randAngle;
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (index > 5) index = 5;
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
return this._stackAngles[index];
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: childHit
|
2010-08-06 15:46:55 -07:00
|
|
|
// Called by one of the groupItem's children when the child is clicked on.
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
2010-06-17 15:57:45 -07:00
|
|
|
// Returns an object:
|
2010-05-26 11:29:31 -07:00
|
|
|
// shouldZoom - true if the browser should launch into the tab represented by the child
|
|
|
|
// callback - called after the zoom animation is complete
|
|
|
|
childHit: function(child) {
|
|
|
|
var self = this;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ normal click
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!this._isStacked || this.expanded) {
|
2010-05-26 11:29:31 -07:00
|
|
|
return {
|
|
|
|
shouldZoom: true,
|
|
|
|
callback: function() {
|
|
|
|
self.collapse();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// ___ we're stacked, but command isn't held down
|
2010-07-11 18:08:33 -07:00
|
|
|
/*if (!Keys.meta) {
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.setActiveGroupItem(self);
|
2010-07-18 08:58:10 -07:00
|
|
|
return { shouldZoom: true };
|
2010-06-19 18:46:39 -07:00
|
|
|
}*/
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.setActiveGroupItem(self);
|
2010-08-08 17:27:47 -07:00
|
|
|
GroupItems.updateTabBarForActiveGroupItem();
|
2010-07-11 18:08:33 -07:00
|
|
|
return { shouldZoom: true };
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-19 18:46:39 -07:00
|
|
|
/*this.expand();
|
|
|
|
return {};*/
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
expand: function() {
|
2010-06-19 18:46:39 -07:00
|
|
|
var self = this;
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ we're stacked, and command is held down so expand
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.setActiveGroupItem(self);
|
2010-08-08 17:27:47 -07:00
|
|
|
GroupItems.updateTabBarForActiveGroupItem();
|
2010-06-19 18:46:39 -07:00
|
|
|
var startBounds = this.getChild(0).getBounds();
|
2010-06-04 15:08:24 -07:00
|
|
|
var $tray = iQ("<div>").css({
|
2010-05-26 11:29:31 -07:00
|
|
|
top: startBounds.top,
|
|
|
|
left: startBounds.left,
|
|
|
|
width: startBounds.width,
|
|
|
|
height: startBounds.height,
|
|
|
|
position: "absolute",
|
|
|
|
zIndex: 99998
|
|
|
|
}).appendTo("body");
|
|
|
|
|
2010-06-19 18:46:39 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var w = 180;
|
|
|
|
var h = w * (TabItems.tabHeight / TabItems.tabWidth) * 1.1;
|
|
|
|
var padding = 20;
|
|
|
|
var col = Math.ceil(Math.sqrt(this._children.length));
|
|
|
|
var row = Math.ceil(this._children.length/col);
|
|
|
|
|
|
|
|
var overlayWidth = Math.min(window.innerWidth - (padding * 2), w*col + padding*(col+1));
|
|
|
|
var overlayHeight = Math.min(window.innerHeight - (padding * 2), h*row + padding*(row+1));
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var pos = {left: startBounds.left, top: startBounds.top};
|
|
|
|
pos.left -= overlayWidth/3;
|
2010-07-18 08:58:10 -07:00
|
|
|
pos.top -= overlayHeight/3;
|
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
if (pos.top < 0) pos.top = 20;
|
|
|
|
if (pos.left < 0) pos.left = 20;
|
|
|
|
if (pos.top+overlayHeight > window.innerHeight) pos.top = window.innerHeight-overlayHeight-20;
|
|
|
|
if (pos.left+overlayWidth > window.innerWidth) pos.left = window.innerWidth-overlayWidth-20;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-07 16:16:55 -07:00
|
|
|
$tray
|
|
|
|
.animate({
|
|
|
|
width: overlayWidth,
|
|
|
|
height: overlayHeight,
|
|
|
|
top: pos.top,
|
|
|
|
left: pos.left
|
|
|
|
}, {
|
2010-07-18 08:58:10 -07:00
|
|
|
duration: 200,
|
2010-07-29 12:37:25 -07:00
|
|
|
easing: "tabviewBounce"
|
2010-06-07 16:16:55 -07:00
|
|
|
})
|
|
|
|
.addClass("overlay");
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
this._children.forEach(function(child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
child.addClass("stack-trayed");
|
|
|
|
});
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
var $shield = iQ('<div>')
|
2010-05-26 11:29:31 -07:00
|
|
|
.css({
|
|
|
|
left: 0,
|
|
|
|
top: 0,
|
|
|
|
width: window.innerWidth,
|
|
|
|
height: window.innerHeight,
|
|
|
|
position: 'absolute',
|
|
|
|
zIndex: 99997
|
|
|
|
})
|
|
|
|
.appendTo('body')
|
|
|
|
.click(function() { // just in case
|
|
|
|
self.collapse();
|
|
|
|
});
|
2010-06-19 18:46:39 -07:00
|
|
|
|
|
|
|
// There is a race-condition here. If there is
|
|
|
|
// a mouse-move while the shield is coming up
|
|
|
|
// it will collapse, which we don't want. Thus,
|
|
|
|
// we wait a little bit before adding this event
|
|
|
|
// handler.
|
2010-07-30 02:54:30 -07:00
|
|
|
setTimeout(function() {
|
2010-06-19 18:46:39 -07:00
|
|
|
$shield.mouseover(function() {
|
|
|
|
self.collapse();
|
|
|
|
});
|
2010-06-21 14:49:38 -07:00
|
|
|
}, 200);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.expanded = {
|
|
|
|
$tray: $tray,
|
|
|
|
$shield: $shield,
|
|
|
|
bounds: new Rect(pos.left, pos.top, overlayWidth, overlayHeight)
|
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
this.arrange();
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: collapse
|
2010-08-06 15:46:55 -07:00
|
|
|
// Collapses the groupItem from the expanded "tray" mode.
|
2010-05-26 11:29:31 -07:00
|
|
|
collapse: function() {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (this.expanded) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var z = this.getZ();
|
|
|
|
var box = this.getBounds();
|
|
|
|
this.expanded.$tray
|
|
|
|
.css({
|
|
|
|
zIndex: z + 1
|
|
|
|
})
|
|
|
|
.animate({
|
|
|
|
width: box.width,
|
|
|
|
height: box.height,
|
|
|
|
top: box.top,
|
|
|
|
left: box.left,
|
|
|
|
opacity: 0
|
2010-06-07 16:16:55 -07:00
|
|
|
}, {
|
|
|
|
duration: 350,
|
2010-07-29 12:37:25 -07:00
|
|
|
easing: "tabviewBounce",
|
2010-06-07 16:16:55 -07:00
|
|
|
complete: function() {
|
2010-07-18 08:58:10 -07:00
|
|
|
iQ(this).remove();
|
2010-06-07 16:16:55 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.expanded.$shield.remove();
|
|
|
|
this.expanded = null;
|
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
this._children.forEach(function(child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
child.removeClass("stack-trayed");
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.arrange({z: z + 2});
|
|
|
|
}
|
2010-03-17 01:07:00 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: _addHandlers
|
2010-07-18 08:58:10 -07:00
|
|
|
// Helper routine for the constructor; adds various event handlers to the container.
|
2010-05-26 11:29:31 -07:00
|
|
|
_addHandlers: function(container) {
|
2010-03-17 01:07:00 -07:00
|
|
|
var self = this;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
this.dropOptions.over = function() {
|
2010-08-08 15:32:08 -07:00
|
|
|
iQ(this.container).addClass("acceptsDrop");
|
2010-06-19 11:56:07 -07:00
|
|
|
};
|
2010-07-30 02:54:30 -07:00
|
|
|
this.dropOptions.drop = function(event) {
|
2010-06-22 16:42:06 -07:00
|
|
|
iQ(this.container).removeClass("acceptsDrop");
|
2010-07-30 02:54:30 -07:00
|
|
|
this.add(drag.info.$el, {left:event.pageX, top:event.pageY});
|
2010-08-08 17:27:47 -07:00
|
|
|
GroupItems.setActiveGroupItem(this);
|
2010-06-19 11:56:07 -07:00
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!this.locked.bounds)
|
2010-06-22 16:42:06 -07:00
|
|
|
this.draggable();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(container)
|
2010-07-30 02:54:30 -07:00
|
|
|
.mousedown(function(e) {
|
2010-06-14 16:56:27 -07:00
|
|
|
self._mouseDown = {
|
|
|
|
location: new Point(e.clientX, e.clientY),
|
|
|
|
className: e.target.className
|
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
})
|
2010-07-30 02:54:30 -07:00
|
|
|
.mouseup(function(e) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!self._mouseDown || !self._mouseDown.location || !self._mouseDown.className)
|
2010-06-14 16:56:27 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-14 16:56:27 -07:00
|
|
|
// Don't zoom in on clicks inside of the controls.
|
|
|
|
var className = self._mouseDown.className;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (className.indexOf('title-shield') != -1
|
2010-06-14 16:56:27 -07:00
|
|
|
|| className.indexOf('name') != -1
|
|
|
|
|| className.indexOf('close') != -1
|
2010-06-19 18:46:39 -07:00
|
|
|
|| className.indexOf('newTabButton') != -1
|
2010-07-30 02:54:30 -07:00
|
|
|
|| className.indexOf('stackExpander') != -1) {
|
2010-06-14 16:56:27 -07:00
|
|
|
return;
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-02 12:29:13 -07:00
|
|
|
var location = new Point(e.clientX, e.clientY);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
if (location.distance(self._mouseDown.location) > 1.0)
|
2010-06-09 14:45:48 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
// Zoom into the last-active tab when the groupItem
|
|
|
|
// is clicked, but only for non-stacked groupItems.
|
2010-07-16 00:17:19 -07:00
|
|
|
var activeTab = self.getActiveTab();
|
2010-07-30 02:54:30 -07:00
|
|
|
if (!self._isStacked) {
|
|
|
|
if (activeTab)
|
2010-07-16 00:17:19 -07:00
|
|
|
activeTab.zoomIn();
|
|
|
|
else if (self.getChild(0))
|
2010-07-18 08:58:10 -07:00
|
|
|
self.getChild(0).zoomIn();
|
2010-07-16 00:17:19 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-14 16:56:27 -07:00
|
|
|
self._mouseDown = null;
|
2010-05-25 19:18:25 -07:00
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-22 16:42:06 -07:00
|
|
|
this.droppable(true);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-30 02:54:30 -07:00
|
|
|
this.$expander.click(function() {
|
2010-06-19 18:46:39 -07:00
|
|
|
self.expand();
|
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: setResizable
|
2010-08-06 15:46:55 -07:00
|
|
|
// Sets whether the groupItem is resizable and updates the UI accordingly.
|
2010-07-30 02:54:30 -07:00
|
|
|
setResizable: function(value) {
|
2010-06-19 17:45:23 -07:00
|
|
|
this.resizeOptions.minWidth = 90;
|
|
|
|
this.resizeOptions.minHeight = 90;
|
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (value) {
|
2010-05-26 11:29:31 -07:00
|
|
|
this.$resizer.fadeIn();
|
2010-06-22 16:42:06 -07:00
|
|
|
this.resizable(true);
|
2010-05-26 11:29:31 -07:00
|
|
|
} else {
|
|
|
|
this.$resizer.fadeOut();
|
2010-06-22 16:42:06 -07:00
|
|
|
this.resizable(false);
|
2010-05-17 16:04:36 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: newTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// Creates a new tab within this groupItem.
|
2010-06-29 14:39:37 -07:00
|
|
|
newTab: function(url) {
|
2010-08-06 15:46:55 -07:00
|
|
|
GroupItems.setActiveGroupItem(this);
|
2010-08-08 17:27:47 -07:00
|
|
|
GroupItems.updateTabBarForActiveGroupItem();
|
2010-07-22 12:34:23 -07:00
|
|
|
let newTab = gBrowser.loadOneTab(url || "about:blank", {inBackground: true});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-04 17:12:08 -07:00
|
|
|
/* ToDo: why we need this here?
|
2010-05-26 11:29:31 -07:00
|
|
|
// Because opening a new tab happens in a different thread(?)
|
2010-07-29 12:37:25 -07:00
|
|
|
// calling UI.showTabView() inline won't do anything. Instead
|
2010-05-26 11:29:31 -07:00
|
|
|
// we have to marshal it. A value of 0 wait time doesn't seem
|
|
|
|
// to work. Instead, we use a value of 1 which seems to be the
|
|
|
|
// minimum amount of time required.
|
2010-07-30 02:54:30 -07:00
|
|
|
Utils.timeout(function() {
|
2010-07-29 12:37:25 -07:00
|
|
|
UI.showTabView()
|
2010-05-26 11:29:31 -07:00
|
|
|
}, 1);
|
2010-08-04 17:12:08 -07:00
|
|
|
*/
|
2010-07-12 21:04:04 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var self = this;
|
2010-07-30 02:54:30 -07:00
|
|
|
var doNextTab = function(tab) {
|
2010-08-06 15:46:55 -07:00
|
|
|
var groupItem = GroupItems.getActiveGroupItem();
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(tab.container).css({opacity: 0});
|
2010-06-07 17:20:15 -07:00
|
|
|
var $anim = iQ("<div>")
|
2010-06-04 15:08:24 -07:00
|
|
|
.addClass('newTabAnimatee')
|
|
|
|
.css({
|
|
|
|
top: tab.bounds.top+5,
|
|
|
|
left: tab.bounds.left+5,
|
|
|
|
width: tab.bounds.width-10,
|
|
|
|
height: tab.bounds.height-10,
|
|
|
|
zIndex: 999,
|
|
|
|
opacity: 0
|
2010-07-18 08:58:10 -07:00
|
|
|
})
|
2010-06-04 15:08:24 -07:00
|
|
|
.appendTo("body")
|
|
|
|
.animate({
|
|
|
|
opacity: 1.0
|
2010-06-07 16:16:55 -07:00
|
|
|
}, {
|
2010-07-18 08:58:10 -07:00
|
|
|
duration: 500,
|
2010-06-07 17:20:15 -07:00
|
|
|
complete: function() {
|
|
|
|
$anim.animate({
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
width: window.innerWidth,
|
|
|
|
height: window.innerHeight
|
|
|
|
}, {
|
2010-07-18 08:58:10 -07:00
|
|
|
duration: 270,
|
2010-07-30 02:54:30 -07:00
|
|
|
complete: function() {
|
2010-06-07 17:20:15 -07:00
|
|
|
iQ(tab.container).css({opacity: 1});
|
2010-07-22 12:34:23 -07:00
|
|
|
gBrowser.selectedTab = newTab;
|
2010-08-04 17:12:08 -07:00
|
|
|
/*
|
|
|
|
ToDo: do we need this? onTabFocus already handles right?
|
2010-07-29 12:37:25 -07:00
|
|
|
UI.hideTabView()
|
2010-08-04 17:12:08 -07:00
|
|
|
*/
|
2010-07-22 12:34:13 -07:00
|
|
|
gWindow.gURLBar.focus();
|
2010-06-07 17:20:15 -07:00
|
|
|
$anim.remove();
|
|
|
|
// We need a timeout here so that there is a chance for the
|
|
|
|
// new tab to get made! Otherwise it won't appear in the list
|
2010-08-06 15:46:55 -07:00
|
|
|
// of the groupItem's tab.
|
2010-06-07 17:20:15 -07:00
|
|
|
// TODO: This is probably a terrible hack that sets up a race
|
|
|
|
// condition. We need a better solution.
|
2010-08-04 17:12:08 -07:00
|
|
|
Utils.timeout(function() {
|
2010-08-06 15:46:55 -07:00
|
|
|
self._sendToSubscribers("tabAdded", { groupItemId: self.id });
|
|
|
|
GroupItems.updateTabBarForActiveGroupItem();
|
2010-08-04 17:12:08 -07:00
|
|
|
}, 1);
|
2010-06-07 17:20:15 -07:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
});
|
2010-06-07 16:16:55 -07:00
|
|
|
}
|
2010-06-07 17:20:15 -07:00
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
}
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// TODO: Because this happens as a callback, there is
|
|
|
|
// sometimes a long delay before the animation occurs.
|
|
|
|
// We need to fix this--immediate response to a users
|
|
|
|
// actions is necessary for a good user experience.
|
2010-07-18 08:58:10 -07:00
|
|
|
self.onNextNewTab(doNextTab);
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-07-25 20:53:21 -07:00
|
|
|
// Function: reorderTabItemsBasedOnTabOrder
|
2010-08-06 15:46:55 -07:00
|
|
|
// Reorders the tabs in a groupItem based on the arrangment of the tabs
|
2010-06-17 16:38:07 -07:00
|
|
|
// shown in the tab bar. It does it by sorting the children
|
2010-08-06 15:46:55 -07:00
|
|
|
// of the groupItem by the positions of their respective tabs in the
|
2010-05-26 11:29:31 -07:00
|
|
|
// tab bar.
|
2010-07-25 20:53:21 -07:00
|
|
|
reorderTabItemsBasedOnTabOrder: function() {
|
2010-07-22 15:09:36 -07:00
|
|
|
this._children.sort(function(a,b) a.tab._tPos - b.tab._tPos);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.arrange({animate: false});
|
|
|
|
// this.arrange calls this.save for us
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-25 20:53:21 -07:00
|
|
|
// Function: reorderTabsBasedOnTabItemOrder
|
|
|
|
// Reorders the tabs in the tab bar based on the arrangment of the tabs
|
2010-08-06 15:46:55 -07:00
|
|
|
// shown in the groupItem.
|
2010-07-25 20:53:21 -07:00
|
|
|
reorderTabsBasedOnTabItemOrder: function() {
|
|
|
|
var tabBarTabs = Array.slice(gBrowser.tabs);
|
|
|
|
var currentIndex;
|
|
|
|
|
|
|
|
// ToDo: optimisation is needed to further reduce the tab move.
|
|
|
|
this._children.forEach(function(tabItem) {
|
|
|
|
tabBarTabs.some(function(tab, i) {
|
|
|
|
if (tabItem.tab == tab) {
|
|
|
|
if (!currentIndex)
|
|
|
|
currentIndex = i;
|
|
|
|
else if (tab.pinned)
|
|
|
|
currentIndex++;
|
|
|
|
else {
|
|
|
|
var removed;
|
|
|
|
if (currentIndex < i)
|
|
|
|
currentIndex = i;
|
|
|
|
else if (currentIndex > i) {
|
|
|
|
removed = tabBarTabs.splice(i, 1);
|
|
|
|
tabBarTabs.splice(currentIndex, 0, removed);
|
|
|
|
gBrowser.moveTabTo(tabItem.tab, currentIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2010-06-17 16:38:07 -07:00
|
|
|
// ----------
|
|
|
|
// Function: setTopChild
|
|
|
|
// Sets the <Item> that should be displayed on top when in stack mode.
|
2010-07-30 02:54:30 -07:00
|
|
|
setTopChild: function(topChild) {
|
2010-06-17 16:38:07 -07:00
|
|
|
this.topChild = topChild;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-17 16:38:07 -07:00
|
|
|
this.arrange({animate: false});
|
|
|
|
// this.arrange calls this.save for us
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
|
|
|
// Function: getChild
|
|
|
|
// Returns the nth child tab or null if index is out of range.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// index - the index of the child tab to return, use negative
|
|
|
|
// numbers to index from the end (-1 is the last child)
|
2010-07-30 02:54:30 -07:00
|
|
|
getChild: function(index) {
|
|
|
|
if (index < 0)
|
2010-07-17 22:03:31 -07:00
|
|
|
index = this._children.length + index;
|
2010-07-30 02:54:30 -07:00
|
|
|
if (index >= this._children.length || index < 0)
|
2010-07-17 22:03:31 -07:00
|
|
|
return null;
|
2010-05-26 11:29:31 -07:00
|
|
|
return this._children[index];
|
|
|
|
},
|
2010-07-16 00:00:20 -07:00
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: getChildren
|
|
|
|
// Returns all children.
|
2010-07-30 02:54:30 -07:00
|
|
|
getChildren: function() {
|
2010-07-16 00:00:20 -07:00
|
|
|
return this._children;
|
|
|
|
},
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ---------
|
|
|
|
// Function: onNextNewTab
|
|
|
|
// Sets up a one-time handler that gets called the next time a
|
2010-08-06 15:46:55 -07:00
|
|
|
// tab is added to the groupItem.
|
2010-05-26 11:29:31 -07:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// callback - the one-time callback that is fired when the next
|
2010-08-06 15:46:55 -07:00
|
|
|
// time a tab is added to a groupItem; it gets passed the
|
2010-05-26 11:29:31 -07:00
|
|
|
// new tab
|
2010-07-30 02:54:30 -07:00
|
|
|
onNextNewTab: function(callback) {
|
2010-05-26 11:29:31 -07:00
|
|
|
this._nextNewTabCallback = callback;
|
|
|
|
}
|
|
|
|
});
|
2010-04-01 17:20:59 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ##########
|
2010-08-06 15:46:55 -07:00
|
|
|
// Class: GroupItems
|
|
|
|
// Singelton for managing all <GroupItem>s.
|
|
|
|
window.GroupItems = {
|
|
|
|
groupItems: [],
|
2010-07-26 17:15:19 -07:00
|
|
|
nextID: 1,
|
|
|
|
_inited: false,
|
2010-08-08 17:27:47 -07:00
|
|
|
_activeGroupItem: null,
|
2010-08-08 15:32:08 -07:00
|
|
|
_activeOrphanTab: null,
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 17:02:21 -07:00
|
|
|
// ----------
|
|
|
|
// Function: init
|
|
|
|
init: function() {
|
|
|
|
/*
|
|
|
|
Utils.log("hello");
|
|
|
|
Utils.log("Groups", Groups);
|
|
|
|
*/
|
|
|
|
},
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: getNextID
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns the next unused groupItem ID.
|
2010-05-26 11:29:31 -07:00
|
|
|
getNextID: function() {
|
|
|
|
var result = this.nextID;
|
|
|
|
this.nextID++;
|
|
|
|
this.save();
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: getStorageData
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns an object for saving GroupItems state to persistent storage.
|
2010-05-26 11:29:31 -07:00
|
|
|
getStorageData: function() {
|
2010-08-06 15:46:55 -07:00
|
|
|
var data = {nextID: this.nextID, groupItems: []};
|
|
|
|
this.groupItems.forEach(function(groupItem) {
|
|
|
|
data.groupItems.push(groupItem.getStorageData());
|
2010-05-26 11:29:31 -07:00
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
return data;
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: saveAll
|
2010-08-06 15:46:55 -07:00
|
|
|
// Saves GroupItems state, as well as the state of all of the groupItems.
|
2010-05-26 11:29:31 -07:00
|
|
|
saveAll: function() {
|
|
|
|
this.save();
|
2010-08-06 15:46:55 -07:00
|
|
|
this.groupItems.forEach(function(groupItem) {
|
|
|
|
groupItem.save();
|
2010-05-26 11:29:31 -07:00
|
|
|
});
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: save
|
2010-08-06 15:46:55 -07:00
|
|
|
// Saves GroupItems state.
|
2010-05-26 11:29:31 -07:00
|
|
|
save: function() {
|
|
|
|
if (!this._inited) // too soon to save now
|
|
|
|
return;
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
Storage.saveGroupItemsData(gWindow, {nextID:this.nextID});
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-07-03 18:13:31 -07:00
|
|
|
// Function: getBoundingBox
|
|
|
|
// Given an array of DOM elements, returns a <Rect> with (roughly) the union of their locations.
|
2010-08-06 15:46:55 -07:00
|
|
|
getBoundingBox: function GroupItems_getBoundingBox(els) {
|
2010-07-03 18:13:31 -07:00
|
|
|
var el, b;
|
|
|
|
var bounds = [iQ(el).bounds() for each (el in els)];
|
2010-07-31 12:24:29 -07:00
|
|
|
var left = Math.min.apply({},[ b.left for each (b in bounds) ]);
|
|
|
|
var top = Math.min.apply({},[ b.top for each (b in bounds) ]);
|
|
|
|
var right = Math.max.apply({},[ b.right for each (b in bounds) ]);
|
|
|
|
var bottom = Math.max.apply({},[ b.bottom for each (b in bounds) ]);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-03 18:13:31 -07:00
|
|
|
return new Rect(left, top, right-left, bottom-top);
|
|
|
|
},
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: reconstitute
|
2010-08-06 15:46:55 -07:00
|
|
|
// Restores to stored state, creating groupItems as needed.
|
|
|
|
// If no data, sets up blank slate (including "new tabs" groupItem).
|
|
|
|
reconstitute: function(groupItemsData, groupItemData) {
|
2010-05-26 11:29:31 -07:00
|
|
|
try {
|
2010-08-06 15:46:55 -07:00
|
|
|
if (groupItemsData && groupItemsData.nextID)
|
|
|
|
this.nextID = groupItemsData.nextID;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
if (groupItemData) {
|
|
|
|
for (var id in groupItemData) {
|
|
|
|
var groupItem = groupItemData[id];
|
|
|
|
if (this.groupItemStorageSanity(groupItem)) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var options = {
|
|
|
|
dontPush: true
|
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
new GroupItem([], Utils.extend({}, groupItem, options));
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this._inited = true;
|
|
|
|
this.save(); // for nextID
|
2010-07-30 02:54:30 -07:00
|
|
|
} catch(e) {
|
2010-05-26 11:29:31 -07:00
|
|
|
Utils.log("error in recons: "+e);
|
2010-03-17 01:07:00 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: groupItemStorageSanity
|
|
|
|
// Given persistent storage data for a groupItem, returns true if it appears to not be damaged.
|
|
|
|
groupItemStorageSanity: function(groupItemData) {
|
2010-07-18 08:58:10 -07:00
|
|
|
// TODO: check everything
|
2010-05-26 11:29:31 -07:00
|
|
|
var sane = true;
|
2010-08-06 15:46:55 -07:00
|
|
|
if (!Utils.isRect(groupItemData.bounds)) {
|
|
|
|
Utils.log('GroupItems.groupItemStorageSanity: bad bounds', groupItemData.bounds);
|
2010-05-26 11:29:31 -07:00
|
|
|
sane = false;
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
return sane;
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: getGroupItemWithTitle
|
|
|
|
// Returns the <GroupItem> that has the given title, or null if none found.
|
|
|
|
// TODO: what if there are multiple groupItems with the same title??
|
2010-07-13 16:38:51 -07:00
|
|
|
// Right now, looks like it'll return the last one.
|
2010-08-06 15:46:55 -07:00
|
|
|
getGroupItemWithTitle: function(title) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var result = null;
|
2010-08-06 15:46:55 -07:00
|
|
|
this.groupItems.forEach(function(groupItem) {
|
|
|
|
if (groupItem.getTitle() == title) {
|
|
|
|
result = groupItem;
|
2010-05-26 11:29:31 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
return result;
|
2010-07-18 08:58:10 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: register
|
2010-08-06 15:46:55 -07:00
|
|
|
// Adds the given <GroupItem> to the list of groupItems we're tracking.
|
|
|
|
register: function(groupItem) {
|
|
|
|
Utils.assert('groupItem', groupItem);
|
|
|
|
Utils.assert('only register once per groupItem', this.groupItems.indexOf(groupItem) == -1);
|
|
|
|
this.groupItems.push(groupItem);
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: unregister
|
2010-08-06 15:46:55 -07:00
|
|
|
// Removes the given <GroupItem> from the list of groupItems we're tracking.
|
|
|
|
unregister: function(groupItem) {
|
|
|
|
var index = this.groupItems.indexOf(groupItem);
|
2010-07-11 17:54:42 -07:00
|
|
|
if (index != -1)
|
2010-08-06 15:46:55 -07:00
|
|
|
this.groupItems.splice(index, 1);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
if (groupItem == this._activeGroupItem)
|
|
|
|
this._activeGroupItem = null;
|
2010-08-08 17:27:47 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: groupItem
|
|
|
|
// Given some sort of identifier, returns the appropriate groupItem.
|
|
|
|
// Currently only supports groupItem ids.
|
|
|
|
groupItem: function(a) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var result = null;
|
2010-08-06 15:46:55 -07:00
|
|
|
this.groupItems.forEach(function(candidate) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (candidate.id == a) {
|
2010-05-26 11:29:31 -07:00
|
|
|
result = candidate;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
return result;
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: arrange
|
2010-08-06 15:46:55 -07:00
|
|
|
// Arranges all of the groupItems into a grid.
|
2010-03-17 17:32:49 -07:00
|
|
|
arrange: function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
var bounds = Items.getPageBounds();
|
2010-08-06 15:46:55 -07:00
|
|
|
bounds.bottom -= 20; // for the dev menu
|
|
|
|
|
|
|
|
var count = this.groupItems.length - 1;
|
2010-03-17 17:32:49 -07:00
|
|
|
var columns = Math.ceil(Math.sqrt(count));
|
2010-07-18 08:58:10 -07:00
|
|
|
var rows = ((columns * columns) - count >= columns ? columns - 1 : columns);
|
2010-03-17 17:32:49 -07:00
|
|
|
var padding = 12;
|
2010-05-26 11:29:31 -07:00
|
|
|
var startX = bounds.left + padding;
|
|
|
|
var startY = bounds.top + padding;
|
|
|
|
var totalWidth = bounds.width - padding;
|
|
|
|
var totalHeight = bounds.height - padding;
|
2010-07-18 08:58:10 -07:00
|
|
|
var box = new Rect(startX, startY,
|
2010-05-26 11:29:31 -07:00
|
|
|
(totalWidth / columns) - padding,
|
|
|
|
(totalHeight / rows) - padding);
|
2010-08-06 15:46:55 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var i = 0;
|
2010-08-06 15:46:55 -07:00
|
|
|
this.groupItems.forEach(function(groupItem) {
|
|
|
|
if (groupItem.locked.bounds)
|
2010-07-18 08:58:10 -07:00
|
|
|
return;
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
groupItem.setBounds(box, true);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
box.left += box.width + padding;
|
|
|
|
i++;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (i % columns == 0) {
|
2010-05-26 11:29:31 -07:00
|
|
|
box.left = startX;
|
|
|
|
box.top += box.height + padding;
|
2010-03-17 17:32:49 -07:00
|
|
|
}
|
|
|
|
});
|
2010-03-26 11:34:09 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-03-26 11:34:09 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: removeAll
|
2010-08-06 15:46:55 -07:00
|
|
|
// Removes all tabs from all groupItems (which automatically closes all unnamed groupItems).
|
2010-03-26 11:34:09 -07:00
|
|
|
removeAll: function() {
|
2010-08-06 15:46:55 -07:00
|
|
|
var toRemove = this.groupItems.concat();
|
|
|
|
toRemove.forEach(function(groupItem) {
|
|
|
|
groupItem.removeAll();
|
2010-03-30 11:05:53 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-17 15:57:45 -07:00
|
|
|
// Function: newTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// Given a <TabItem>, files it in the appropriate groupItem.
|
2010-05-26 11:29:31 -07:00
|
|
|
newTab: function(tabItem) {
|
2010-08-08 17:27:47 -07:00
|
|
|
let activeGroupItem = this.getActiveGroupItem();
|
2010-08-04 22:43:05 -07:00
|
|
|
let orphanTab = this.getActiveOrphanTab();
|
2010-08-08 17:27:47 -07:00
|
|
|
// Utils.log('newTab', activeGroupItem, orphanTab);
|
|
|
|
if (activeGroupItem) {
|
|
|
|
activeGroupItem.add(tabItem);
|
|
|
|
} else if (orphanTab) {
|
2010-08-08 15:32:08 -07:00
|
|
|
let newGroupItemBounds = orphanTab.getBoundsWithTitle();
|
|
|
|
newGroupItemBounds.inset(-40,-40);
|
|
|
|
let newGroupItem = new GroupItem([orphanTab, tabItem], {bounds: newGroupItemBounds});
|
|
|
|
newGroupItem.snap();
|
|
|
|
this.setActiveGroupItem(newGroupItem);
|
2010-08-08 17:27:47 -07:00
|
|
|
this.updateTabBarForActiveGroupItem();
|
2010-08-04 22:43:05 -07:00
|
|
|
} else {
|
2010-08-06 14:22:56 -07:00
|
|
|
this.positionNewTabAtBottom(tabItem);
|
2010-08-08 15:32:08 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-08 15:32:08 -07:00
|
|
|
// ----------
|
|
|
|
// Function: positionNewTabAtBottom
|
|
|
|
// Does what it says on the tin.
|
|
|
|
// TODO: Make more robust and improve documentation,
|
|
|
|
// Also, this probably belongs in tabitems.js
|
|
|
|
positionNewTabAtBottom: function(tabItem) {
|
|
|
|
let windowBounds = Items.getSafeWindowBounds();
|
|
|
|
|
|
|
|
let itemBounds = new Rect(
|
|
|
|
windowBounds.right - TabItems.tabWidth,
|
|
|
|
windowBounds.bottom - TabItems.tabHeight,
|
|
|
|
TabItems.tabWidth,
|
|
|
|
TabItems.tabHeight
|
|
|
|
);
|
|
|
|
|
2010-08-06 14:22:56 -07:00
|
|
|
tabItem.setBounds(itemBounds);
|
2010-08-08 15:32:08 -07:00
|
|
|
},
|
2010-08-04 22:43:05 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: getActiveGroupItem
|
2010-08-08 17:27:47 -07:00
|
|
|
// Returns the active groupItem. Active means its tabs are
|
2010-07-29 12:37:25 -07:00
|
|
|
// shown in the tab bar when not in the TabView interface.
|
2010-08-06 15:46:55 -07:00
|
|
|
getActiveGroupItem: function() {
|
|
|
|
return this._activeGroupItem;
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: setActiveGroupItem
|
2010-08-08 17:27:47 -07:00
|
|
|
// Sets the active groupItem, thereby showing only the relevent tabs, and
|
|
|
|
// setting the groupItem which will receive new tabs.
|
2010-05-26 11:29:31 -07:00
|
|
|
//
|
2010-06-17 15:57:45 -07:00
|
|
|
// Paramaters:
|
2010-08-06 15:46:55 -07:00
|
|
|
// groupItem - the active <GroupItem> or <null> if no groupItem is active
|
2010-05-26 11:29:31 -07:00
|
|
|
// (which means we have an orphaned tab selected)
|
2010-08-06 15:46:55 -07:00
|
|
|
setActiveGroupItem: function(groupItem) {
|
2010-08-08 17:27:47 -07:00
|
|
|
|
|
|
|
if (this._activeGroupItem)
|
|
|
|
iQ(this._activeGroupItem.container).removeClass('activeGroupItem');
|
|
|
|
|
|
|
|
if (groupItem !== null) {
|
|
|
|
if (groupItem)
|
|
|
|
iQ(groupItem.container).addClass('activeGroupItem');
|
|
|
|
// if a groupItem is active, we surely are not in an orphaned tab.
|
|
|
|
this.setActiveOrphanTab(null);
|
|
|
|
}
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
this._activeGroupItem = groupItem;
|
2010-08-04 22:43:05 -07:00
|
|
|
},
|
2010-08-08 17:27:47 -07:00
|
|
|
|
2010-08-04 22:43:05 -07:00
|
|
|
// ----------
|
|
|
|
// Function: getActiveOrphanTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns the active orphan tab, in cases when there is no active groupItem.
|
2010-08-04 22:43:05 -07:00
|
|
|
getActiveOrphanTab: function() {
|
|
|
|
return this._activeOrphanTab;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: setActiveOrphanTab
|
2010-08-06 15:46:55 -07:00
|
|
|
// In cases where an orphan tab (not in a groupItem) is active by itself,
|
2010-08-04 22:43:05 -07:00
|
|
|
// this function is called and the "active orphan tab" is set.
|
|
|
|
//
|
|
|
|
// Paramaters:
|
2010-08-06 15:46:55 -07:00
|
|
|
// groupItem - the active <TabItem> or <null>
|
2010-08-04 22:43:05 -07:00
|
|
|
setActiveOrphanTab: function(tabItem) {
|
|
|
|
this._activeOrphanTab = tabItem;
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: updateTabBarForActiveGroupItem
|
|
|
|
// Hides and shows tabs in the tab bar based on the active groupItem.
|
|
|
|
updateTabBarForActiveGroupItem: function() {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!window.UI)
|
2010-05-26 11:29:31 -07:00
|
|
|
return; // called too soon
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
let tabItems = this._activeGroupItem == null ? this.getOrphanedTabs() :
|
|
|
|
this._activeGroupItem._children;
|
2010-07-25 22:01:11 -07:00
|
|
|
gBrowser.showOnlyTheseTabs(tabItems.map(function(item) item.tab));
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
|
|
|
// Function: getOrphanedTabs
|
2010-08-06 15:46:55 -07:00
|
|
|
// Returns an array of all tabs that aren't in a groupItem.
|
2010-07-30 02:54:30 -07:00
|
|
|
getOrphanedTabs: function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
var tabs = TabItems.getItems();
|
2010-07-30 02:54:30 -07:00
|
|
|
tabs = tabs.filter(function(tab) {
|
2010-05-26 11:29:31 -07:00
|
|
|
return tab.parent == null;
|
2010-05-18 17:08:45 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
return tabs;
|
2010-07-21 20:29:58 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: getNextGroupItemTab
|
2010-07-21 20:29:58 -07:00
|
|
|
// Paramaters:
|
2010-08-06 15:46:55 -07:00
|
|
|
// reverse - the boolean indicates the direction to look for the next groupItem.
|
2010-07-21 20:29:58 -07:00
|
|
|
// Returns the <tabItem>. If nothing is found, return null.
|
2010-08-06 15:46:55 -07:00
|
|
|
getNextGroupItemTab: function(reverse) {
|
|
|
|
var groupItems = GroupItems.groupItems.map(function(groupItem) groupItem);
|
|
|
|
var activeGroupItem = GroupItems.getActiveGroupItem();
|
2010-07-21 20:29:58 -07:00
|
|
|
var tabItem = null;
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
if (!activeGroupItem) {
|
|
|
|
if (groupItems.length > 0) {
|
2010-07-21 20:29:58 -07:00
|
|
|
if (reverse)
|
2010-08-06 15:46:55 -07:00
|
|
|
groupItems = groupItems.reverse();
|
2010-07-21 20:29:58 -07:00
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
groupItems.some(function(groupItem) {
|
|
|
|
var child = groupItem.getChild(0);
|
2010-07-21 20:29:58 -07:00
|
|
|
if (child) {
|
|
|
|
tabItem = child;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (reverse)
|
2010-08-06 15:46:55 -07:00
|
|
|
groupItems = groupItems.reverse();
|
2010-07-21 20:29:58 -07:00
|
|
|
|
|
|
|
var currentIndex;
|
2010-08-06 15:46:55 -07:00
|
|
|
groupItems.some(function(groupItem, index) {
|
|
|
|
if (groupItem == activeGroupItem) {
|
2010-07-21 20:29:58 -07:00
|
|
|
currentIndex = index;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2010-08-06 15:46:55 -07:00
|
|
|
var firstGroupItems = groupItems.slice(currentIndex + 1);
|
|
|
|
firstGroupItems.some(function(groupItem) {
|
|
|
|
var child = groupItem.getChild(0);
|
2010-07-21 20:29:58 -07:00
|
|
|
if (child) {
|
|
|
|
tabItem = child;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!tabItem) {
|
2010-08-06 15:46:55 -07:00
|
|
|
var orphanedTabs = GroupItems.getOrphanedTabs();
|
2010-07-21 20:29:58 -07:00
|
|
|
if (orphanedTabs.length > 0)
|
|
|
|
tabItem = orphanedTabs[0];
|
|
|
|
}
|
|
|
|
if (!tabItem) {
|
2010-08-06 15:46:55 -07:00
|
|
|
var secondGroupItems = groupItems.slice(0, currentIndex);
|
|
|
|
secondGroupItems.some(function(groupItem) {
|
|
|
|
var child = groupItem.getChild(0);
|
2010-07-21 20:29:58 -07:00
|
|
|
if (child) {
|
|
|
|
tabItem = child;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tabItem;
|
2010-08-06 07:17:01 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-08-06 15:46:55 -07:00
|
|
|
// Function: moveTabToGroupItem
|
2010-08-06 07:17:01 -07:00
|
|
|
// Paramaters:
|
|
|
|
// tab - the <xul:tab>.
|
2010-08-06 15:46:55 -07:00
|
|
|
// groupItemId - the <groupItem>'s id. If nothing, create a new <groupItem>.
|
|
|
|
moveTabToGroupItem : function(tab, groupItemId) {
|
2010-08-06 07:17:01 -07:00
|
|
|
Utils.log("move to tab")
|
|
|
|
let shouldUpdateTabBar = false;
|
2010-08-06 15:46:55 -07:00
|
|
|
let groupItem;
|
2010-08-06 07:17:01 -07:00
|
|
|
|
|
|
|
// switch to the appropriate tab first.
|
|
|
|
if (gBrowser.selectedTab == tab) {
|
|
|
|
let list = gBrowser.visibleTabs;
|
|
|
|
let listLength = list.length;
|
|
|
|
|
|
|
|
if (listLength > 1) {
|
|
|
|
let index = list.indexOf(tab);
|
|
|
|
if (index == 0 || (index + 1) < listLength)
|
|
|
|
gBrowser.selectTabAtIndex(index + 1);
|
|
|
|
else
|
|
|
|
gBrowser.selectTabAtIndex(index - 1);
|
|
|
|
shouldUpdateTabBar = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
shouldUpdateTabBar = true
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
// remove tab item from a groupItem
|
2010-08-06 07:17:01 -07:00
|
|
|
if (tab.tabItem.parent)
|
|
|
|
tab.tabItem.parent.remove(tab.tabItem);
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
// add tab item to a groupItem
|
|
|
|
if (groupItemId) {
|
|
|
|
groupItem = GroupItems.groupItem(groupItemId);
|
|
|
|
groupItem.add(tab.tabItem);
|
|
|
|
UI.setReorderTabItemsOnShow(groupItem);
|
2010-08-06 07:17:01 -07:00
|
|
|
} else {
|
|
|
|
let pageBounds = Items.getPageBounds();
|
|
|
|
pageBounds.inset(20, 20);
|
|
|
|
|
|
|
|
let box = new Rect(pageBounds);
|
|
|
|
box.width = 250;
|
|
|
|
box.height = 200;
|
|
|
|
|
2010-08-06 15:46:55 -07:00
|
|
|
new GroupItem([ tab.tabItem ], { bounds: box });
|
2010-08-06 07:17:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldUpdateTabBar)
|
2010-08-06 15:46:55 -07:00
|
|
|
this.updateTabBarForActiveGroupItem();
|
2010-08-06 07:17:01 -07:00
|
|
|
else {
|
|
|
|
tab.tabItem.setZoomPrep(false);
|
|
|
|
UI.showTabView();
|
|
|
|
}
|
2010-08-08 15:47:06 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: killNewTabGroup
|
|
|
|
// Removes the New Tab Group, which is now defunct. See bug 575851 and comments therein.
|
|
|
|
killNewTabGroup: function() {
|
|
|
|
this.groupItems.forEach(function(groupItem) {
|
|
|
|
if (groupItem.getTitle() == 'New Tabs') {
|
|
|
|
groupItem.removeAll();
|
|
|
|
groupItem.close();
|
|
|
|
}
|
|
|
|
});
|
2010-03-26 11:34:09 -07:00
|
|
|
}
|
|
|
|
};
|