2010-05-26 11:29:31 -07:00
|
|
|
// Title: groups.js (revision-a)
|
2010-03-17 01:07:00 -07:00
|
|
|
(function(){
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: numCmp
|
|
|
|
// Numeric compare for sorting.
|
|
|
|
// Private to this file.
|
2010-03-17 01:07:00 -07:00
|
|
|
var numCmp = function(a,b){ return a-b; }
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: min
|
|
|
|
// Given a list of numbers, returns the smallest.
|
|
|
|
// Private to this file.
|
2010-03-17 01:07:00 -07:00
|
|
|
function min(list){ return list.slice().sort(numCmp)[0]; }
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: max
|
|
|
|
// Given a list of numbers, returns the largest.
|
|
|
|
// Private to this file.
|
2010-03-17 01:07:00 -07:00
|
|
|
function max(list){ return list.slice().sort(numCmp).reverse()[0]; }
|
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ##########
|
2010-05-26 11:29:31 -07:00
|
|
|
// Class: Group
|
|
|
|
// A single group in the tab candy window. Descended from <Item>.
|
|
|
|
// Note that it implements the <Subscribable> interface.
|
2010-06-11 15:08:14 -07:00
|
|
|
//
|
|
|
|
// ----------
|
|
|
|
// Constructor: Group
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// listOfEls - an array of DOM elements for tabs to be added to this group
|
|
|
|
// options - various options for this group (see below). In addition, gets passed
|
|
|
|
// to <add> along with the elements provided.
|
|
|
|
//
|
|
|
|
// Possible options:
|
|
|
|
// id - specifies the group's id; otherwise automatically generated
|
|
|
|
// 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
|
|
|
|
// container - a DOM element to use as the container for this group; otherwise will create
|
|
|
|
// title - the title for the group; otherwise blank
|
|
|
|
// dontPush - true if this group shouldn't push away on creation; default is false
|
2010-05-26 11:29:31 -07:00
|
|
|
window.Group = function(listOfEls, options) {
|
|
|
|
try {
|
|
|
|
if(typeof(options) == 'undefined')
|
|
|
|
options = {};
|
|
|
|
|
|
|
|
this._inited = false;
|
|
|
|
this._children = []; // an array of Items
|
|
|
|
this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
|
|
|
|
this.isAGroup = true;
|
|
|
|
this.id = options.id || Groups.getNextID();
|
|
|
|
this._isStacked = false;
|
|
|
|
this._stackAngles = [0];
|
|
|
|
this.expanded = null;
|
|
|
|
this.locked = (options.locked ? Utils.copy(options.locked) : {});
|
|
|
|
this.topChild = null;
|
2010-06-14 15:43:02 -07:00
|
|
|
|
|
|
|
// Variable: _activeTab
|
|
|
|
// The <TabItem> for the group's active tab.
|
2010-05-25 19:18:25 -07:00
|
|
|
this._activeTab = null;
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
if(isPoint(options.userSize))
|
|
|
|
this.userSize = new Point(options.userSize);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
var rectToBe;
|
|
|
|
if(options.bounds)
|
|
|
|
rectToBe = new Rect(options.bounds);
|
|
|
|
|
|
|
|
if(!rectToBe) {
|
|
|
|
var boundingBox = this._getBoundingBox(listOfEls);
|
|
|
|
var padding = 30;
|
|
|
|
rectToBe = new Rect(
|
|
|
|
boundingBox.left-padding,
|
|
|
|
boundingBox.top-padding,
|
|
|
|
boundingBox.width+padding*2,
|
|
|
|
boundingBox.height+padding*2
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
var $container = options.container;
|
|
|
|
if(!$container) {
|
2010-06-04 15:08:24 -07:00
|
|
|
$container = iQ('<div>')
|
|
|
|
.addClass('group')
|
2010-05-26 11:29:31 -07:00
|
|
|
.css({position: 'absolute'})
|
|
|
|
.css(rectToBe);
|
|
|
|
|
|
|
|
if( this.isNewTabsGroup() ) $container.addClass("newTabGroup");
|
|
|
|
}
|
|
|
|
|
|
|
|
$container
|
|
|
|
.css({zIndex: -100})
|
|
|
|
.data('isDragging', false)
|
2010-06-04 15:08:24 -07:00
|
|
|
.appendTo("body");
|
|
|
|
/* .dequeue(); */
|
2010-05-26 11:29:31 -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);
|
|
|
|
|
|
|
|
this.$ntb
|
|
|
|
.addClass(this.isNewTabsGroup() ? 'newTabButtonAlt' : 'newTabButton')
|
|
|
|
.click(function(){
|
|
|
|
self.newTab();
|
|
|
|
});
|
|
|
|
|
|
|
|
if( this.isNewTabsGroup() ) this.$ntb.html("<span>+</span>");
|
|
|
|
|
|
|
|
// ___ 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'>" +
|
|
|
|
"<input class='name' value='" + (options.title || "") + "'/>" +
|
|
|
|
"<div class='title-shield' />" +
|
|
|
|
"</div>" +
|
|
|
|
"<div class='close' />";
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
this.$titlebar = iQ('<div>')
|
|
|
|
.addClass('titlebar')
|
|
|
|
.html(html)
|
2010-05-26 11:29:31 -07:00
|
|
|
.appendTo($container);
|
|
|
|
|
|
|
|
this.$titlebar.css({
|
|
|
|
position: "absolute",
|
|
|
|
});
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
var $close = iQ('.close', this.$titlebar).click(function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.closeAll();
|
|
|
|
});
|
|
|
|
|
|
|
|
// ___ 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-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
var titleUnfocus = function() {
|
|
|
|
self.$titleShield.show();
|
|
|
|
if(!self.getTitle()) {
|
|
|
|
self.$title
|
|
|
|
.addClass("defaultName")
|
|
|
|
.val(self.defaultName);
|
|
|
|
} else {
|
2010-06-07 16:16:55 -07:00
|
|
|
self.$title
|
|
|
|
.css({"background":"none"})
|
|
|
|
.animate({
|
|
|
|
"paddingLeft": 1
|
|
|
|
}, {
|
|
|
|
duration: 350,
|
|
|
|
easing: 'tabcandyBounce'
|
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var handleKeyPress = function(e){
|
|
|
|
if( e.which == 13 ) { // return
|
2010-06-04 15:08:24 -07:00
|
|
|
self.$title.get(0).blur();
|
|
|
|
self.$title
|
2010-05-26 11:29:31 -07:00
|
|
|
.addClass("transparentBorder")
|
|
|
|
.one("mouseout", function(){
|
|
|
|
self.$title.removeClass("transparentBorder");
|
|
|
|
});
|
|
|
|
} else
|
|
|
|
self.adjustTitleSize();
|
|
|
|
|
|
|
|
self.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$title
|
|
|
|
.css({backgroundRepeat: 'no-repeat'})
|
|
|
|
.blur(titleUnfocus)
|
|
|
|
.focus(function() {
|
|
|
|
if(self.locked.title) {
|
2010-06-04 15:08:24 -07:00
|
|
|
self.$title.get(0).blur();
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
|
|
|
}
|
2010-06-04 15:08:24 -07:00
|
|
|
self.$title.get(0).select();
|
2010-05-26 11:29:31 -07:00
|
|
|
if(!self.getTitle()) {
|
|
|
|
self.$title
|
|
|
|
.removeClass("defaultName")
|
|
|
|
.val('');
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.keyup(handleKeyPress);
|
|
|
|
|
|
|
|
titleUnfocus();
|
|
|
|
|
|
|
|
if(this.locked.title)
|
|
|
|
this.$title.addClass('name-locked');
|
|
|
|
else {
|
|
|
|
this.$titleShield
|
|
|
|
.mousedown(function(e) {
|
|
|
|
self.lastMouseDownTarget = (Utils.isRightClick(e) ? null : e.target);
|
|
|
|
})
|
|
|
|
.mouseup(function(e) {
|
|
|
|
var same = (e.target == self.lastMouseDownTarget);
|
|
|
|
self.lastMouseDownTarget = null;
|
|
|
|
if(!same)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(!$container.data('isDragging')) {
|
|
|
|
self.$titleShield.hide();
|
|
|
|
self.$title.get(0).focus();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// ___ Content
|
2010-06-04 15:08:24 -07:00
|
|
|
// TODO: I don't think we need this any more
|
|
|
|
this.$content = iQ('<div>')
|
|
|
|
.addClass('group-content')
|
2010-05-26 11:29:31 -07:00
|
|
|
.css({
|
|
|
|
left: 0,
|
|
|
|
top: this.$titlebar.height(),
|
|
|
|
position: 'absolute'
|
|
|
|
})
|
|
|
|
.appendTo($container);
|
|
|
|
|
|
|
|
// ___ locking
|
|
|
|
if(this.locked.bounds)
|
|
|
|
$container.css({cursor: 'default'});
|
|
|
|
|
|
|
|
if(this.locked.close)
|
|
|
|
$close.hide();
|
|
|
|
|
|
|
|
// ___ Superclass initialization
|
|
|
|
this._init($container.get(0));
|
|
|
|
|
|
|
|
if(this.$debug)
|
|
|
|
this.$debug.css({zIndex: -1000});
|
|
|
|
|
|
|
|
// ___ Children
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(listOfEls, function(index, el) {
|
2010-05-26 11:29:31 -07:00
|
|
|
self.add(el, null, options);
|
|
|
|
});
|
|
|
|
|
|
|
|
// ___ Finish Up
|
|
|
|
this._addHandlers($container);
|
|
|
|
|
|
|
|
if(!this.locked.bounds)
|
|
|
|
this.setResizable(true);
|
|
|
|
|
|
|
|
Groups.register(this);
|
|
|
|
|
|
|
|
this.setBounds(rectToBe);
|
|
|
|
|
|
|
|
// ___ Push other objects away
|
|
|
|
if(!options.dontPush)
|
|
|
|
this.pushAway();
|
|
|
|
|
|
|
|
this._inited = true;
|
|
|
|
this.save();
|
|
|
|
} catch(e){
|
|
|
|
Utils.log("Error in Group(): " + e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ----------
|
2010-06-04 15:08:24 -07:00
|
|
|
window.Group.prototype = iQ.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-05-26 11:29:31 -07:00
|
|
|
defaultName: "name this group...",
|
2010-05-25 19:18:25 -07:00
|
|
|
|
|
|
|
// -----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: setActiveTab
|
2010-06-14 15:43:02 -07:00
|
|
|
// Sets the active <TabItem> for this group
|
2010-05-25 19:18:25 -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-06-14 15:43:02 -07:00
|
|
|
// Gets the active <TabItem> for this group
|
|
|
|
getActiveTab: function(){
|
2010-05-25 19:18:25 -07:00
|
|
|
return this._activeTab;
|
|
|
|
},
|
2010-04-22 17:19:42 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: getStorageData
|
|
|
|
// Returns all of the info worth storing about this group.
|
2010-05-26 11:29:31 -07:00
|
|
|
getStorageData: function() {
|
|
|
|
var data = {
|
|
|
|
bounds: this.getBounds(),
|
|
|
|
userSize: null,
|
|
|
|
locked: Utils.copy(this.locked),
|
|
|
|
title: this.getTitle(),
|
|
|
|
id: this.id
|
|
|
|
};
|
|
|
|
|
|
|
|
if(isPoint(this.userSize))
|
|
|
|
data.userSize = new Point(this.userSize);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: save
|
|
|
|
// Saves this group to persistant storage.
|
2010-05-26 11:29:31 -07:00
|
|
|
save: function() {
|
|
|
|
if (!this._inited) // too soon to save now
|
|
|
|
return;
|
|
|
|
|
|
|
|
var data = this.getStorageData();
|
|
|
|
if(Groups.groupStorageSanity(data))
|
|
|
|
Storage.saveGroup(Utils.getCurrentWindow(), data);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-11 15:08:14 -07:00
|
|
|
// Function: isNewTabsGroup
|
|
|
|
// Returns true if the callee is the "New Tabs" group.
|
|
|
|
// TODO: more robust
|
2010-05-26 11:29:31 -07:00
|
|
|
isNewTabsGroup: function() {
|
|
|
|
return (this.locked.bounds && this.locked.title && this.locked.close);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
getTitle: function() {
|
|
|
|
var value = (this.$title ? this.$title.val() : '');
|
|
|
|
return (value == this.defaultName ? '' : value);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
setTitle: function(value) {
|
|
|
|
this.$title.val(value);
|
|
|
|
this.save();
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
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);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
_getBoundingBox: function(els) {
|
2010-03-17 01:07:00 -07:00
|
|
|
var el;
|
|
|
|
var boundingBox = {
|
2010-06-04 15:08:24 -07:00
|
|
|
top: min( [iQ(el).position().top for([,el] in Iterator(els))] ),
|
|
|
|
left: min( [iQ(el).position().left for([,el] in Iterator(els))] ),
|
|
|
|
bottom: max( [iQ(el).position().top for([,el] in Iterator(els))] ) + iQ(els[0]).height(),
|
|
|
|
right: max( [iQ(el).position().left for([,el] in Iterator(els))] ) + iQ(els[0]).width(),
|
2010-03-17 01:07:00 -07:00
|
|
|
};
|
|
|
|
boundingBox.height = boundingBox.bottom - boundingBox.top;
|
|
|
|
boundingBox.width = boundingBox.right - boundingBox.left;
|
|
|
|
return boundingBox;
|
|
|
|
},
|
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ----------
|
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);
|
|
|
|
|
|
|
|
if(this.isNewTabsGroup())
|
|
|
|
box.height -= 12; // Hack for tab titles
|
|
|
|
else
|
|
|
|
box.height -= 33; // For new tab button
|
|
|
|
|
|
|
|
return box;
|
2010-03-17 01:07:00 -07:00
|
|
|
},
|
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
reloadBounds: function() {
|
|
|
|
var bb = Utils.getBounds(this.container);
|
2010-05-11 12:03:31 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(!this.bounds)
|
|
|
|
this.bounds = new Rect(0, 0, 0, 0);
|
2010-04-22 09:38:37 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.setBounds(bb, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
setBounds: function(rect, immediately) {
|
|
|
|
if(!isRect(rect)) {
|
|
|
|
Utils.trace('Group.setBounds: rect is not a real rectangle!', rect);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var titleHeight = this.$titlebar.height();
|
|
|
|
|
|
|
|
// ___ Determine what has changed
|
|
|
|
var css = {};
|
|
|
|
var titlebarCSS = {};
|
|
|
|
var contentCSS = {};
|
|
|
|
var force = false;
|
|
|
|
|
|
|
|
if(force || rect.left != this.bounds.left)
|
|
|
|
css.left = rect.left;
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(force || rect.top != this.bounds.top)
|
|
|
|
css.top = rect.top;
|
|
|
|
|
|
|
|
if(force || rect.width != this.bounds.width) {
|
|
|
|
css.width = rect.width;
|
|
|
|
titlebarCSS.width = rect.width;
|
|
|
|
contentCSS.width = rect.width;
|
|
|
|
}
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(force || rect.height != this.bounds.height) {
|
|
|
|
css.height = rect.height;
|
|
|
|
contentCSS.height = rect.height - titleHeight;
|
|
|
|
}
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
if(iQ.isEmptyObject(css))
|
2010-05-26 11:29:31 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
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
|
|
|
|
if(css.width || css.height) {
|
|
|
|
this.arrange({animate: !immediately}); //(immediately ? 'sometimes' : true)});
|
|
|
|
} else if(css.left || css.top) {
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this._children, function(index, child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
var box = child.getBounds();
|
|
|
|
child.setPosition(box.left + offset.x, box.top + offset.y, immediately);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// ___ Update our representation
|
|
|
|
if(immediately) {
|
2010-06-04 15:08:24 -07:00
|
|
|
/*
|
2010-05-26 11:29:31 -07:00
|
|
|
$(this.container).stop(true, true);
|
|
|
|
this.$titlebar.stop(true, true);
|
|
|
|
this.$content.stop(true, true);
|
2010-06-04 15:08:24 -07:00
|
|
|
*/
|
2010-05-14 15:58:55 -07:00
|
|
|
|
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);
|
|
|
|
this.$content.css(contentCSS);
|
|
|
|
} else {
|
|
|
|
TabMirror.pausePainting();
|
2010-06-07 16:16:55 -07:00
|
|
|
iQ(this.container).animate(css, {
|
|
|
|
duration: 350,
|
|
|
|
easing: 'tabcandyBounce',
|
|
|
|
complete: function() {
|
|
|
|
TabMirror.resumePainting();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/* }).dequeue(); */
|
|
|
|
|
|
|
|
this.$titlebar.animate(titlebarCSS, {
|
|
|
|
duration: 350
|
|
|
|
});//.dequeue();
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-06-07 16:16:55 -07:00
|
|
|
this.$content.animate(contentCSS, {
|
|
|
|
duration: 350
|
|
|
|
}); //.dequeue();
|
2010-04-22 17:19:42 -07:00
|
|
|
}
|
2010-05-06 17:01:53 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.adjustTitleSize();
|
|
|
|
|
|
|
|
this._updateDebugBounds();
|
|
|
|
|
|
|
|
if(!isRect(this.bounds))
|
|
|
|
Utils.trace('Group.setBounds: this.bounds is not a real rectangle!', this.bounds);
|
|
|
|
|
2010-06-15 11:53:53 -07:00
|
|
|
this.setTrenches(rect);
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.save();
|
2010-04-01 17:20:59 -07:00
|
|
|
},
|
|
|
|
|
2010-06-15 11:53:53 -07:00
|
|
|
setTrenches: function(rect) {
|
|
|
|
|
|
|
|
var container = this.container;
|
2010-06-15 12:26:35 -07:00
|
|
|
|
2010-06-15 11:53:53 -07:00
|
|
|
if (!this.borderTrenches) {
|
|
|
|
var bT = this.borderTrenches = {};
|
|
|
|
bT.left = Trenches.register(container,"x","border","left");
|
|
|
|
bT.right = Trenches.register(container,"x","border","right");
|
|
|
|
bT.top = Trenches.register(container,"y","border","top");
|
|
|
|
bT.bottom = Trenches.register(container,"y","border","bottom");
|
|
|
|
}
|
|
|
|
var bT = this.borderTrenches;
|
|
|
|
bT.left.setWithRect(rect);
|
|
|
|
bT.right.setWithRect(rect);
|
|
|
|
bT.top.setWithRect(rect);
|
|
|
|
bT.bottom.setWithRect(rect);
|
|
|
|
|
|
|
|
if (!this.guideTrenches) {
|
|
|
|
var gT = this.guideTrenches = {};
|
|
|
|
gT.left = Trenches.register(container,"x","guide","left");
|
|
|
|
gT.right = Trenches.register(container,"x","guide","right");
|
|
|
|
gT.top = Trenches.register(container,"y","guide","top");
|
|
|
|
gT.bottom = Trenches.register(container,"y","guide","bottom");
|
|
|
|
}
|
|
|
|
var gT = this.guideTrenches;
|
|
|
|
gT.left.setWithRect(rect);
|
|
|
|
gT.right.setWithRect(rect);
|
|
|
|
gT.top.setWithRect(rect);
|
|
|
|
gT.bottom.setWithRect(rect);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
|
|
|
setZ: function(value) {
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(this.container).css({zIndex: value});
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(this.$debug)
|
|
|
|
this.$debug.css({zIndex: value + 1});
|
|
|
|
|
|
|
|
var count = this._children.length;
|
|
|
|
if(count) {
|
|
|
|
var topZIndex = value + count + 1;
|
|
|
|
var zIndex = topZIndex;
|
|
|
|
var self = this;
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this._children, function(index, child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
if(child == self.topChild)
|
|
|
|
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-04-22 09:38:37 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
|
|
|
close: function() {
|
|
|
|
this.removeAll();
|
|
|
|
this._sendOnClose();
|
|
|
|
Groups.unregister(this);
|
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
|
|
|
|
|
|
|
Storage.deleteGroup(Utils.getCurrentWindow(), this.id);
|
2010-03-29 16:08:48 -07:00
|
|
|
},
|
2010-04-26 16:48:46 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
closeAll: function() {
|
2010-06-14 17:23:17 -07:00
|
|
|
var self = this;
|
2010-05-26 11:29:31 -07:00
|
|
|
if(this._children.length) {
|
2010-06-04 15:08:24 -07:00
|
|
|
var toClose = iQ.merge([], this._children);
|
|
|
|
iQ.each(toClose, function(index, child) {
|
2010-06-14 17:23:17 -07:00
|
|
|
child.removeOnClose(self);
|
2010-05-26 11:29:31 -07:00
|
|
|
child.close();
|
|
|
|
});
|
2010-06-14 17:23:17 -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-05-25 17:04:59 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
// Function: add
|
|
|
|
// Adds an item to the group.
|
|
|
|
// Parameters:
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
// options - An object with optional settings for this call. Currently the only one is dontArrange.
|
|
|
|
add: function(a, dropPos, options) {
|
|
|
|
try {
|
|
|
|
var item;
|
|
|
|
var $el;
|
|
|
|
if(a.isAnItem) {
|
|
|
|
item = a;
|
2010-06-04 15:08:24 -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);
|
|
|
|
}
|
|
|
|
|
|
|
|
Utils.assert('shouldn\'t already be in another group', !item.parent || item.parent == this);
|
|
|
|
|
|
|
|
if(!dropPos)
|
|
|
|
dropPos = {top:window.innerWidth, left:window.innerHeight};
|
|
|
|
|
|
|
|
if(typeof(options) == 'undefined')
|
|
|
|
options = {};
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
var wasAlreadyInThisGroup = false;
|
2010-06-04 15:08:24 -07:00
|
|
|
var oldIndex = iQ.inArray(item, this._children);
|
2010-05-26 11:29:31 -07:00
|
|
|
if(oldIndex != -1) {
|
|
|
|
this._children.splice(oldIndex, 1);
|
|
|
|
wasAlreadyInThisGroup = true;
|
2010-03-24 16:27:56 -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)
|
|
|
|
function findInsertionPoint(dropPos){
|
|
|
|
if(self.shouldStack(self._children.length + 1))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
var best = {dist: Infinity, item: null};
|
|
|
|
var index = 0;
|
|
|
|
var box;
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(self._children, function(index, child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
box = child.getBounds();
|
|
|
|
if(box.bottom < dropPos.top || box.top > dropPos.top)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var dist = Math.sqrt( Math.pow((box.top+box.height/2)-dropPos.top,2)
|
|
|
|
+ Math.pow((box.left+box.width/2)-dropPos.left,2) );
|
|
|
|
|
|
|
|
if( dist <= best.dist ){
|
|
|
|
best.item = child;
|
|
|
|
best.dist = dist;
|
|
|
|
best.index = index;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if( self._children.length > 0 ){
|
|
|
|
if(best.item) {
|
|
|
|
box = best.item.getBounds();
|
|
|
|
var insertLeft = dropPos.left <= box.left + box.width/2;
|
|
|
|
if( !insertLeft )
|
|
|
|
return best.index+1;
|
|
|
|
else
|
|
|
|
return best.index;
|
|
|
|
} else
|
|
|
|
return self._children.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-05-24 17:14:10 -07:00
|
|
|
}
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// Insert the tab into the right position.
|
|
|
|
var index = findInsertionPoint(dropPos);
|
|
|
|
this._children.splice( index, 0, item );
|
|
|
|
|
|
|
|
item.setZ(this.getZ() + 1);
|
|
|
|
$el.addClass("tabInGroup");
|
|
|
|
if( this.isNewTabsGroup() ) $el.addClass("inNewTabGroup")
|
|
|
|
|
|
|
|
if(!wasAlreadyInThisGroup) {
|
2010-06-08 17:13:19 -07:00
|
|
|
iQ($el.get(0)).droppable("disable");
|
2010-05-26 11:29:31 -07:00
|
|
|
item.groupData = {};
|
2010-05-25 17:04:59 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
item.addOnClose(this, function() {
|
|
|
|
self.remove($el);
|
|
|
|
});
|
|
|
|
|
|
|
|
item.setParent(this);
|
|
|
|
|
|
|
|
if(typeof(item.setResizable) == 'function')
|
|
|
|
item.setResizable(false);
|
|
|
|
|
|
|
|
if(item.tab == Utils.activeTab)
|
|
|
|
Groups.setActiveGroup(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!options.dontArrange)
|
|
|
|
this.arrange();
|
|
|
|
|
|
|
|
if( this._nextNewTabCallback ){
|
|
|
|
this._nextNewTabCallback.apply(this, [item])
|
|
|
|
this._nextNewTabCallback = null;
|
|
|
|
}
|
|
|
|
} catch(e) {
|
|
|
|
Utils.log('Group.add error', e);
|
2010-05-25 17:04:59 -07:00
|
|
|
}
|
2010-04-22 09:38:37 -07:00
|
|
|
},
|
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
// Function: remove
|
|
|
|
// Removes an item from the group.
|
|
|
|
// Parameters:
|
|
|
|
//
|
|
|
|
// 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>.
|
|
|
|
// options - An object with optional settings for this call. Currently the only one is dontArrange.
|
|
|
|
remove: function(a, options) {
|
|
|
|
var $el;
|
|
|
|
var item;
|
|
|
|
|
|
|
|
if(a.isAnItem) {
|
|
|
|
item = a;
|
2010-06-04 15:08:24 -07:00
|
|
|
$el = iQ(item.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-04-26 13:37:39 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(typeof(options) == 'undefined')
|
|
|
|
options = {};
|
2010-04-22 17:19:42 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
var index = iQ.inArray(item, this._children);
|
2010-05-26 11:29:31 -07:00
|
|
|
if(index != -1)
|
|
|
|
this._children.splice(index, 1);
|
|
|
|
|
|
|
|
item.setParent(null);
|
|
|
|
item.removeClass("tabInGroup");
|
|
|
|
item.removeClass("inNewTabGroup")
|
|
|
|
item.removeClass("stacked");
|
|
|
|
item.removeClass("stack-trayed");
|
|
|
|
item.setRotation(0);
|
|
|
|
item.setSize(item.defaultSize.x, item.defaultSize.y);
|
|
|
|
|
2010-06-08 17:13:19 -07:00
|
|
|
iQ($el.get(0)).droppable("enable");
|
2010-05-26 11:29:31 -07:00
|
|
|
item.removeOnClose(this);
|
|
|
|
|
|
|
|
if(typeof(item.setResizable) == 'function')
|
|
|
|
item.setResizable(true);
|
|
|
|
|
|
|
|
if(this._children.length == 0 && !this.locked.close && !this.getTitle() && !options.dontClose){
|
|
|
|
this.close();
|
|
|
|
} else if(!options.dontArrange) {
|
2010-04-22 17:19:42 -07:00
|
|
|
this.arrange();
|
2010-04-22 09:38:37 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-04-22 17:19:42 -07:00
|
|
|
// ----------
|
|
|
|
removeAll: function() {
|
2010-04-22 09:38:37 -07:00
|
|
|
var self = this;
|
2010-06-04 15:08:24 -07:00
|
|
|
var toRemove = iQ.merge([], this._children);
|
|
|
|
iQ.each(toRemove, function(index, 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-05-26 11:29:31 -07:00
|
|
|
|
2010-05-04 16:44:03 -07:00
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
setNewTabButtonBounds: function(box, immediately) {
|
|
|
|
var css = {
|
|
|
|
left: box.left,
|
|
|
|
top: box.top,
|
|
|
|
width: box.width,
|
|
|
|
height: box.height
|
|
|
|
};
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
/* this.$ntb.stop(true, true); */
|
2010-05-26 11:29:31 -07:00
|
|
|
if(!immediately)
|
2010-06-07 16:16:55 -07:00
|
|
|
this.$ntb.animate(css, {
|
|
|
|
duration: 320,
|
|
|
|
easing: 'tabcandyBounce'
|
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
else
|
|
|
|
this.$ntb.css(css);
|
2010-05-04 16:44:03 -07:00
|
|
|
},
|
2010-05-11 17:22:06 -07:00
|
|
|
|
2010-05-25 17:04:59 -07:00
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
shouldStack: function(count) {
|
|
|
|
if(count <= 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var bb = this.getContentBounds();
|
|
|
|
var options = {
|
|
|
|
pretend: true,
|
|
|
|
count: (this.isNewTabsGroup() ? count + 1 : count)
|
|
|
|
};
|
2010-05-24 17:14:10 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var rects = Items.arrange(null, bb, options);
|
2010-06-02 12:29:13 -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-05-26 11:29:31 -07:00
|
|
|
// ----------
|
|
|
|
arrange: function(options) {
|
|
|
|
if(this.expanded) {
|
|
|
|
this.topChild = null;
|
|
|
|
var box = new Rect(this.expanded.bounds);
|
|
|
|
box.inset(8, 8);
|
2010-06-04 15:08:24 -07:00
|
|
|
Items.arrange(this._children, box, iQ.extend({}, options, {padding: 8, z: 99999}));
|
2010-05-26 11:29:31 -07:00
|
|
|
} else {
|
|
|
|
var bb = this.getContentBounds();
|
|
|
|
var count = this._children.length;
|
|
|
|
if(!this.shouldStack(count)) {
|
|
|
|
var animate;
|
|
|
|
if(!options || typeof(options.animate) == 'undefined')
|
|
|
|
animate = true;
|
|
|
|
else
|
|
|
|
animate = options.animate;
|
2010-04-27 16:11:49 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(typeof(options) == 'undefined')
|
|
|
|
options = {};
|
|
|
|
|
|
|
|
this._children.forEach(function(child){
|
|
|
|
child.removeClass("stacked")
|
|
|
|
});
|
|
|
|
|
|
|
|
this.topChild = null;
|
|
|
|
|
|
|
|
var arrangeOptions = Utils.copy(options);
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.extend(arrangeOptions, {
|
2010-05-26 11:29:31 -07:00
|
|
|
pretend: true,
|
|
|
|
count: count
|
|
|
|
});
|
|
|
|
|
|
|
|
if(this.isNewTabsGroup()) {
|
|
|
|
arrangeOptions.count++;
|
|
|
|
} else if(!count)
|
|
|
|
return;
|
2010-05-18 17:08:45 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var rects = Items.arrange(this._children, bb, arrangeOptions);
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this._children, function(index, child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
if(!child.locked.bounds) {
|
|
|
|
child.setBounds(rects[index], !animate);
|
|
|
|
child.setRotation(0);
|
|
|
|
if(options.z)
|
|
|
|
child.setZ(options.z);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if(this.isNewTabsGroup()) {
|
|
|
|
var box = rects[rects.length - 1];
|
|
|
|
box.left -= this.bounds.left;
|
|
|
|
box.top -= this.bounds.top;
|
|
|
|
this.setNewTabButtonBounds(box, !animate);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._isStacked = false;
|
|
|
|
} else
|
|
|
|
this._stackArrange(bb, options);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
_stackArrange: function(bb, options) {
|
|
|
|
var animate;
|
|
|
|
if(!options || typeof(options.animate) == 'undefined')
|
|
|
|
animate = true;
|
|
|
|
else
|
|
|
|
animate = options.animate;
|
|
|
|
|
|
|
|
if(typeof(options) == 'undefined')
|
|
|
|
options = {};
|
|
|
|
|
2010-05-25 17:04:59 -07:00
|
|
|
var count = this._children.length;
|
2010-05-26 11:29:31 -07:00
|
|
|
if(!count)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var zIndex = this.getZ() + count + 1;
|
|
|
|
|
|
|
|
var scale = 0.8;
|
|
|
|
var newTabsPad = 10;
|
|
|
|
var w;
|
|
|
|
var h;
|
|
|
|
var itemAspect = TabItems.tabHeight / TabItems.tabWidth;
|
|
|
|
var bbAspect = bb.height / bb.width;
|
|
|
|
if(bbAspect > itemAspect) { // Tall, thin group
|
|
|
|
w = bb.width * scale;
|
|
|
|
h = w * itemAspect;
|
|
|
|
} else { // Short, wide group
|
|
|
|
h = bb.height * scale;
|
|
|
|
w = h * (1 / itemAspect);
|
2010-05-23 23:42:18 -07:00
|
|
|
}
|
2010-04-27 16:11:49 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
var x = (bb.width - w) / 2;
|
|
|
|
if(this.isNewTabsGroup())
|
|
|
|
x -= (w + newTabsPad) / 2;
|
|
|
|
|
|
|
|
var y = Math.min(x, (bb.height - h) / 2);
|
|
|
|
var box = new Rect(bb.left + x, bb.top + y, w, h);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
var children = [];
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this._children, function(index, child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
if(child == self.topChild)
|
|
|
|
children.unshift(child);
|
|
|
|
else
|
|
|
|
children.push(child);
|
|
|
|
});
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(children, function(index, child) {
|
2010-05-26 11:29:31 -07:00
|
|
|
if(!child.locked.bounds) {
|
|
|
|
child.setZ(zIndex);
|
|
|
|
zIndex--;
|
|
|
|
|
|
|
|
child.addClass("stacked");
|
|
|
|
child.setBounds(box, !animate);
|
|
|
|
child.setRotation(self._randRotate(35, index));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if(this.isNewTabsGroup()) {
|
|
|
|
box.left += box.width + newTabsPad;
|
|
|
|
box.left -= this.bounds.left;
|
|
|
|
box.top -= this.bounds.top;
|
|
|
|
this.setNewTabButtonBounds(box, !animate);
|
|
|
|
}
|
|
|
|
|
|
|
|
self._isStacked = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
_randRotate: function(spread, index){
|
|
|
|
if( index >= this._stackAngles.length ){
|
2010-05-27 19:17:01 -07:00
|
|
|
var randAngle = 5*index + parseInt( (Math.random()-.5)*1 );
|
2010-05-26 11:29:31 -07:00
|
|
|
this._stackAngles.push(randAngle);
|
|
|
|
return randAngle;
|
|
|
|
}
|
2010-05-27 19:17:01 -07:00
|
|
|
|
|
|
|
if( index > 5 ) index = 5;
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
return this._stackAngles[index];
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: childHit
|
|
|
|
// Called by one of the group's children when the child is clicked on. Returns an object:
|
|
|
|
// 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-04-28 17:18:02 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ normal click
|
|
|
|
if(!this._isStacked || this.expanded) {
|
|
|
|
return {
|
|
|
|
shouldZoom: true,
|
|
|
|
callback: function() {
|
|
|
|
self.collapse();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// ___ we're stacked, but command isn't held down
|
|
|
|
if( Keys.meta == false ){
|
|
|
|
Groups.setActiveGroup(self);
|
|
|
|
return { shouldZoom: true };
|
|
|
|
}
|
2010-04-28 17:18:02 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ___ we're stacked, and command is held down so expand
|
|
|
|
Groups.setActiveGroup(self);
|
|
|
|
var startBounds = child.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");
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
var pos = {left: startBounds.left, top: startBounds.top};
|
|
|
|
pos.left -= overlayWidth/3;
|
|
|
|
pos.top -= overlayHeight/3;
|
|
|
|
|
|
|
|
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-06-07 16:16:55 -07:00
|
|
|
$tray
|
|
|
|
.animate({
|
|
|
|
width: overlayWidth,
|
|
|
|
height: overlayHeight,
|
|
|
|
top: pos.top,
|
|
|
|
left: pos.left
|
|
|
|
}, {
|
|
|
|
duration: 350,
|
|
|
|
easing: 'tabcandyBounce'
|
|
|
|
})
|
|
|
|
.addClass("overlay");
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
this._children.forEach(function(child){
|
|
|
|
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')
|
|
|
|
.mouseover(function() {
|
|
|
|
self.collapse();
|
|
|
|
})
|
|
|
|
.click(function() { // just in case
|
|
|
|
self.collapse();
|
|
|
|
});
|
2010-05-25 17:04:59 -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-05-25 17:04:59 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
this.arrange();
|
|
|
|
|
|
|
|
return {};
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
collapse: function() {
|
|
|
|
if(this.expanded) {
|
|
|
|
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,
|
|
|
|
easing: 'tabcandyBounce',
|
|
|
|
complete: function() {
|
|
|
|
iQ(this).remove();
|
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
this.expanded.$shield.remove();
|
|
|
|
this.expanded = null;
|
|
|
|
|
|
|
|
this._children.forEach(function(child){
|
|
|
|
child.removeClass("stack-trayed");
|
|
|
|
});
|
|
|
|
|
|
|
|
this.arrange({z: z + 2});
|
|
|
|
}
|
2010-03-17 01:07:00 -07:00
|
|
|
},
|
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ----------
|
2010-05-26 11:29:31 -07:00
|
|
|
_addHandlers: function(container) {
|
2010-03-17 01:07:00 -07:00
|
|
|
var self = this;
|
|
|
|
|
2010-06-15 14:38:55 -07:00
|
|
|
this.dropOptions.over = function(){
|
|
|
|
if( !self.isNewTabsGroup() )
|
|
|
|
iQ(this).addClass("acceptsDrop");
|
|
|
|
};
|
|
|
|
this.dropOptions.drop = function(event){
|
|
|
|
iQ(this).removeClass("acceptsDrop");
|
|
|
|
self.add( drag.info.$el, {left:event.pageX, top:event.pageY} );
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!this.locked.bounds)
|
|
|
|
iQ(container).draggable(this.dragOptions);
|
2010-03-17 01:07:00 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ(container)
|
2010-05-27 16:36:13 -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-05-27 16:36:13 -07:00
|
|
|
})
|
|
|
|
.mouseup(function(e){
|
2010-06-14 16:56:27 -07:00
|
|
|
if(!self._mouseDown || !self._mouseDown.location || !self._mouseDown.className)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't zoom in on clicks inside of the controls.
|
|
|
|
var className = self._mouseDown.className;
|
|
|
|
if(className.indexOf('title-shield') != -1
|
|
|
|
|| className.indexOf('name') != -1
|
|
|
|
|| className.indexOf('close') != -1
|
|
|
|
|| className.indexOf('newTabButton') != -1) {
|
|
|
|
return;
|
|
|
|
}
|
2010-06-02 12:29:13 -07:00
|
|
|
|
|
|
|
var location = new Point(e.clientX, e.clientY);
|
2010-05-27 16:36:13 -07:00
|
|
|
|
2010-06-14 16:56:27 -07:00
|
|
|
if(location.distance(self._mouseDown.location) > 1.0)
|
2010-06-09 14:45:48 -07:00
|
|
|
return;
|
2010-06-14 16:56:27 -07:00
|
|
|
|
2010-06-02 12:29:13 -07:00
|
|
|
// Don't zoom in to the last tab for the new tab group.
|
2010-06-14 16:56:27 -07:00
|
|
|
if( self.isNewTabsGroup() )
|
|
|
|
return;
|
|
|
|
|
2010-06-02 12:29:13 -07:00
|
|
|
var activeTab = self.getActiveTab();
|
2010-06-14 15:43:02 -07:00
|
|
|
if( activeTab )
|
|
|
|
activeTab.zoomIn();
|
2010-06-07 17:20:15 -07:00
|
|
|
else if(self.getChild(0))
|
2010-06-14 15:43:02 -07:00
|
|
|
self.getChild(0).zoomIn();
|
2010-06-09 14:45:48 -07:00
|
|
|
|
2010-06-14 16:56:27 -07:00
|
|
|
self._mouseDown = null;
|
2010-05-25 19:18:25 -07:00
|
|
|
});
|
|
|
|
|
2010-06-15 14:38:55 -07:00
|
|
|
iQ(container).droppable(this.dropOptions);
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
setResizable: function(value){
|
|
|
|
var self = this;
|
2010-05-13 15:57:30 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
if(value) {
|
|
|
|
this.$resizer.fadeIn();
|
2010-06-09 14:45:48 -07:00
|
|
|
iQ(this.container).resizable({
|
2010-05-26 11:29:31 -07:00
|
|
|
aspectRatio: false,
|
|
|
|
minWidth: 90,
|
|
|
|
minHeight: 90,
|
2010-06-15 11:53:53 -07:00
|
|
|
start: function(){
|
2010-06-15 12:26:35 -07:00
|
|
|
window.Trenches.activateOthersTrenches(self.container);
|
2010-06-15 11:53:53 -07:00
|
|
|
},
|
2010-05-26 11:29:31 -07:00
|
|
|
resize: function(){
|
|
|
|
self.reloadBounds();
|
2010-06-15 11:53:53 -07:00
|
|
|
var bounds = self.getBounds();
|
|
|
|
// OH SNAP!
|
2010-06-15 12:26:35 -07:00
|
|
|
var newRect = window.Trenches.snap(bounds,false);
|
2010-06-15 11:53:53 -07:00
|
|
|
if (newRect) // might be false if no changes were made
|
|
|
|
self.setBounds(bounds,true);
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
stop: function(){
|
|
|
|
self.reloadBounds();
|
|
|
|
self.setUserSize();
|
|
|
|
self.pushAway();
|
2010-06-15 12:26:35 -07:00
|
|
|
window.Trenches.disactivate();
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.$resizer.fadeOut();
|
2010-06-09 14:45:48 -07:00
|
|
|
iQ(this.container).resizable('destroy');
|
2010-05-17 16:04:36 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
newTab: function() {
|
|
|
|
Groups.setActiveGroup(this);
|
|
|
|
var newTab = Tabs.open("about:blank", true);
|
|
|
|
|
|
|
|
// Because opening a new tab happens in a different thread(?)
|
|
|
|
// calling Page.hideChrome() inline won't do anything. Instead
|
|
|
|
// 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-06-10 12:04:39 -07:00
|
|
|
iQ.timeout(function(){
|
2010-05-26 11:29:31 -07:00
|
|
|
Page.hideChrome()
|
|
|
|
}, 1);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
var doNextTab = function(tab){
|
|
|
|
var group = Groups.getActiveGroup();
|
|
|
|
|
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
|
|
|
|
})
|
|
|
|
.appendTo("body")
|
|
|
|
.animate({
|
|
|
|
opacity: 1.0
|
2010-06-07 16:16:55 -07:00
|
|
|
}, {
|
2010-06-07 17:20:15 -07:00
|
|
|
duration: 500,
|
|
|
|
complete: function() {
|
|
|
|
$anim.animate({
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
width: window.innerWidth,
|
|
|
|
height: window.innerHeight
|
|
|
|
}, {
|
|
|
|
duration: 270,
|
|
|
|
complete: function(){
|
|
|
|
iQ(tab.container).css({opacity: 1});
|
|
|
|
newTab.focus();
|
|
|
|
Page.showChrome()
|
|
|
|
UI.navBar.urlBar.focus();
|
|
|
|
$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
|
|
|
|
// of the group's tab.
|
|
|
|
// TODO: This is probably a terrible hack that sets up a race
|
|
|
|
// condition. We need a better solution.
|
2010-06-10 12:04:39 -07:00
|
|
|
iQ.timeout(function(){
|
2010-06-07 17:20:15 -07:00
|
|
|
UI.tabBar.showOnlyTheseTabs(Groups.getActiveGroup()._children);
|
|
|
|
}, 400);
|
|
|
|
}
|
|
|
|
});
|
2010-06-07 16:16:55 -07:00
|
|
|
}
|
2010-06-07 17:20:15 -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.
|
|
|
|
|
|
|
|
self.onNextNewTab(doNextTab);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: reorderBasedOnTabOrder
|
|
|
|
// Reorderes the tabs in a group based on the arrangment of the tabs
|
|
|
|
// shown in the tab bar. It doesn't it by sorting the children
|
|
|
|
// of the group by the positions of their respective tabs in the
|
|
|
|
// tab bar.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// topChild - the <Item> that should be displayed on top of a stack
|
|
|
|
reorderBasedOnTabOrder: function(topChild){
|
|
|
|
this.topChild = topChild;
|
|
|
|
|
|
|
|
var groupTabs = [];
|
|
|
|
for( var i=0; i<UI.tabBar.el.children.length; i++ ){
|
|
|
|
var tab = UI.tabBar.el.children[i];
|
|
|
|
if( tab.collapsed == false )
|
|
|
|
groupTabs.push(tab);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._children.sort(function(a,b){
|
|
|
|
return groupTabs.indexOf(a.tab.raw) - groupTabs.indexOf(b.tab.raw)
|
|
|
|
});
|
|
|
|
|
|
|
|
this.arrange({animate: false});
|
|
|
|
// this.arrange calls this.save for us
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// 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)
|
|
|
|
getChild: function(index){
|
|
|
|
if( index < 0 ) index = this._children.length+index;
|
|
|
|
if( index >= this._children.length || index < 0 ) return null;
|
|
|
|
return this._children[index];
|
|
|
|
},
|
|
|
|
|
|
|
|
// ---------
|
|
|
|
// Function: onNextNewTab
|
|
|
|
// Sets up a one-time handler that gets called the next time a
|
|
|
|
// tab is added to the group.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// callback - the one-time callback that is fired when the next
|
|
|
|
// time a tab is added to a group; it gets passed the
|
|
|
|
// new tab
|
|
|
|
onNextNewTab: function(callback){
|
|
|
|
this._nextNewTabCallback = callback;
|
|
|
|
}
|
|
|
|
});
|
2010-04-01 17:20:59 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ##########
|
|
|
|
// Class: Groups
|
|
|
|
// Singelton for managing all <Group>s.
|
|
|
|
window.Groups = {
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
init: function() {
|
|
|
|
this.groups = [];
|
|
|
|
this.nextID = 1;
|
|
|
|
this._inited = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
getNextID: function() {
|
|
|
|
var result = this.nextID;
|
|
|
|
this.nextID++;
|
|
|
|
this.save();
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
getStorageData: function() {
|
|
|
|
var data = {nextID: this.nextID, groups: []};
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this.groups, function(index, group) {
|
2010-05-26 11:29:31 -07:00
|
|
|
data.groups.push(group.getStorageData());
|
|
|
|
});
|
|
|
|
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
saveAll: function() {
|
|
|
|
this.save();
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this.groups, function(index, group) {
|
2010-05-26 11:29:31 -07:00
|
|
|
group.save();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
save: function() {
|
|
|
|
if (!this._inited) // too soon to save now
|
|
|
|
return;
|
|
|
|
|
|
|
|
Storage.saveGroupsData(Utils.getCurrentWindow(), {nextID:this.nextID});
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
reconstitute: function(groupsData, groupData) {
|
|
|
|
try {
|
|
|
|
if(groupsData && groupsData.nextID)
|
|
|
|
this.nextID = groupsData.nextID;
|
|
|
|
|
|
|
|
if(groupData) {
|
|
|
|
for (var id in groupData) {
|
|
|
|
var group = groupData[id];
|
|
|
|
if(this.groupStorageSanity(group)) {
|
|
|
|
var isNewTabsGroup = (group.title == 'New Tabs');
|
|
|
|
var options = {
|
|
|
|
locked: {
|
|
|
|
close: isNewTabsGroup,
|
|
|
|
title: isNewTabsGroup,
|
|
|
|
bounds: isNewTabsGroup
|
|
|
|
},
|
|
|
|
dontPush: true
|
|
|
|
};
|
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
new Group([], iQ.extend({}, group, options));
|
2010-05-26 11:29:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var group = this.getNewTabGroup();
|
|
|
|
if(!group) {
|
|
|
|
var box = this.getBoundsForNewTabGroup();
|
|
|
|
var options = {
|
|
|
|
locked: {
|
|
|
|
close: true,
|
|
|
|
title: true,
|
|
|
|
bounds: true
|
|
|
|
},
|
|
|
|
dontPush: true,
|
|
|
|
bounds: box,
|
|
|
|
title: 'New Tabs'
|
|
|
|
};
|
|
|
|
|
|
|
|
new Group([], options);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.repositionNewTabGroup();
|
|
|
|
|
|
|
|
this._inited = true;
|
|
|
|
this.save(); // for nextID
|
|
|
|
}catch(e){
|
|
|
|
Utils.log("error in recons: "+e);
|
2010-03-17 01:07:00 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
groupStorageSanity: function(groupData) {
|
|
|
|
// TODO: check everything
|
|
|
|
var sane = true;
|
|
|
|
if(!isRect(groupData.bounds)) {
|
|
|
|
Utils.log('Groups.groupStorageSanity: bad bounds', groupData.bounds);
|
|
|
|
sane = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sane;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
getGroupWithTitle: function(title) {
|
|
|
|
var result = null;
|
|
|
|
iQ.each(this.groups, function(index, group) {
|
|
|
|
if(group.getTitle() == title) {
|
|
|
|
result = group;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
2010-03-17 17:32:49 -07:00
|
|
|
},
|
2010-05-26 11:29:31 -07:00
|
|
|
|
|
|
|
// ----------
|
|
|
|
getNewTabGroup: function() {
|
|
|
|
var groupTitle = 'New Tabs';
|
2010-05-27 17:25:14 -07:00
|
|
|
var array = iQ.grep(this.groups, function(group) {
|
2010-05-26 11:29:31 -07:00
|
|
|
return group.getTitle() == groupTitle;
|
|
|
|
});
|
|
|
|
|
|
|
|
if(array.length)
|
|
|
|
return array[0];
|
|
|
|
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
getBoundsForNewTabGroup: function() {
|
|
|
|
var pad = 0;
|
|
|
|
var sw = window.innerWidth;
|
|
|
|
var sh = window.innerHeight;
|
|
|
|
var w = sw - (pad * 2);
|
|
|
|
var h = TabItems.tabHeight * 0.9 + pad*2;
|
|
|
|
return new Rect(pad, sh - (h + pad), w, h);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
repositionNewTabGroup: function() {
|
|
|
|
var box = this.getBoundsForNewTabGroup();
|
|
|
|
var group = this.getNewTabGroup();
|
|
|
|
group.setBounds(box, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
register: function(group) {
|
|
|
|
Utils.assert('group', group);
|
2010-06-04 15:08:24 -07:00
|
|
|
Utils.assert('only register once per group', iQ.inArray(group, this.groups) == -1);
|
2010-05-26 11:29:31 -07:00
|
|
|
this.groups.push(group);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
unregister: function(group) {
|
2010-06-04 15:08:24 -07:00
|
|
|
var index = iQ.inArray(group, this.groups);
|
2010-05-26 11:29:31 -07:00
|
|
|
if(index != -1)
|
|
|
|
this.groups.splice(index, 1);
|
|
|
|
|
|
|
|
if(group == this._activeGroup)
|
|
|
|
this._activeGroup = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: group
|
|
|
|
// Given some sort of identifier, returns the appropriate group.
|
|
|
|
// Currently only supports group ids.
|
|
|
|
group: function(a) {
|
|
|
|
var result = null;
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this.groups, function(index, candidate) {
|
2010-05-26 11:29:31 -07:00
|
|
|
if(candidate.id == a) {
|
|
|
|
result = candidate;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
2010-03-17 17:32:49 -07:00
|
|
|
|
2010-03-25 17:22:45 -07:00
|
|
|
// ----------
|
2010-03-17 17:32:49 -07:00
|
|
|
arrange: function() {
|
2010-05-26 11:29:31 -07:00
|
|
|
var bounds = Items.getPageBounds();
|
|
|
|
var count = this.groups.length - 1;
|
2010-03-17 17:32:49 -07:00
|
|
|
var columns = Math.ceil(Math.sqrt(count));
|
|
|
|
var rows = ((columns * columns) - count >= columns ? columns - 1 : columns);
|
|
|
|
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;
|
|
|
|
var box = new Rect(startX, startY,
|
|
|
|
(totalWidth / columns) - padding,
|
|
|
|
(totalHeight / rows) - padding);
|
|
|
|
|
|
|
|
var i = 0;
|
2010-06-04 15:08:24 -07:00
|
|
|
iQ.each(this.groups, function(index, group) {
|
2010-05-26 11:29:31 -07:00
|
|
|
if(group.locked.bounds)
|
|
|
|
return;
|
|
|
|
|
|
|
|
group.setBounds(box, true);
|
|
|
|
|
|
|
|
box.left += box.width + padding;
|
|
|
|
i++;
|
|
|
|
if(i % columns == 0) {
|
|
|
|
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-03-25 17:22:45 -07:00
|
|
|
|
2010-03-26 11:34:09 -07:00
|
|
|
// ----------
|
|
|
|
removeAll: function() {
|
2010-06-04 15:08:24 -07:00
|
|
|
var toRemove = iQ.merge([], this.groups);
|
|
|
|
iQ.each(toRemove, function(index, group) {
|
2010-03-30 11:05:53 -07:00
|
|
|
group.removeAll();
|
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
newTab: function(tabItem) {
|
|
|
|
var group = this.getActiveGroup();
|
|
|
|
if( group == null )
|
|
|
|
group = this.getNewTabGroup();
|
2010-05-25 17:04:59 -07:00
|
|
|
|
2010-06-04 15:08:24 -07:00
|
|
|
var $el = iQ(tabItem.container);
|
2010-05-26 11:29:31 -07:00
|
|
|
if(group) group.add($el);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: getActiveGroup
|
|
|
|
// Returns the active group. Active means the group where a new
|
|
|
|
// tab will live when it is created as well as what tabs are
|
|
|
|
// shown in the tab bar when not in the TabCandy interface.
|
|
|
|
getActiveGroup: function() {
|
|
|
|
return this._activeGroup;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: setActiveGroup
|
|
|
|
// Sets the active group, thereby showing only the relavent tabs
|
|
|
|
// to that group. The change is visible only if the tab bar is
|
|
|
|
// visible.
|
|
|
|
//
|
|
|
|
// Paramaters
|
|
|
|
// group - the active <Group> or <null> if no group is active
|
|
|
|
// (which means we have an orphaned tab selected)
|
|
|
|
setActiveGroup: function(group) {
|
|
|
|
this._activeGroup = group;
|
|
|
|
this.updateTabBarForActiveGroup();
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: updateTabBarForActiveGroup
|
|
|
|
// Hides and shows tabs in the tab bar based on the active group.
|
|
|
|
updateTabBarForActiveGroup: function() {
|
|
|
|
if(!window.UI)
|
|
|
|
return; // called too soon
|
|
|
|
|
|
|
|
if(this._activeGroup)
|
|
|
|
UI.tabBar.showOnlyTheseTabs( this._activeGroup._children );
|
|
|
|
else if( this._activeGroup == null)
|
|
|
|
UI.tabBar.showOnlyTheseTabs( this.getOrphanedTabs());
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: getOrphanedTabs
|
|
|
|
// Returns an array of all tabs that aren't in a group
|
|
|
|
getOrphanedTabs: function(){
|
|
|
|
var tabs = TabItems.getItems();
|
|
|
|
tabs = tabs.filter(function(tab){
|
|
|
|
return tab.parent == null;
|
2010-05-18 17:08:45 -07:00
|
|
|
});
|
2010-05-26 11:29:31 -07:00
|
|
|
return tabs;
|
2010-03-26 11:34:09 -07:00
|
|
|
}
|
2010-05-26 11:29:31 -07:00
|
|
|
|
2010-03-26 11:34:09 -07:00
|
|
|
};
|
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
// ----------
|
|
|
|
Groups.init();
|
2010-05-25 17:04:59 -07:00
|
|
|
|
2010-05-26 11:29:31 -07:00
|
|
|
})();
|