+ added the title and close box back to groups, with some basic styling

+ the "new tab" group now always lives at the bottom of the page
+ the candy switcher and the "grid" and "sites" features are now hidden by default. add ?dev=1 to the url to show them
+ the Firefox nav bar is now hidden when you're in the tab candy page
+ the starting grid now scales depending on how many tabs you have. (it is in fact sharing the arrange code that groups use for their children)
This commit is contained in:
Ian Gilman 2010-04-14 17:12:06 -07:00
parent 81e6b81193
commit d54682ea3a
6 changed files with 243 additions and 184 deletions

View File

@ -31,20 +31,21 @@ window.Group = function(listOfEls, options) {
options = {};
this._children = []; // an array of Items
this._padding = 30;
this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
this.title = '';
var self = this;
var boundingBox = this._getBoundingBox(listOfEls);
var padding = 30;
var rectToBe = new Rect(
boundingBox.left-padding,
boundingBox.top-padding,
boundingBox.width+padding*2,
boundingBox.height+padding*2
);
var rectToBe = 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) {
@ -69,9 +70,13 @@ window.Group = function(listOfEls, options) {
.hide();
// ___ Titlebar
this.$titlebar = $("<div class='titlebar'><input class='name' value=''/><div class='close'>x</div></div>")
.appendTo($container)
var html = "<div class='titlebar'><input class='name' value='"
+ (options.title || "")
+ "'/><div class='close'></div></div>";
this.$titlebar = $(html)
.appendTo($container);
this.$titlebar.css({
position: "absolute",
});
@ -79,23 +84,8 @@ window.Group = function(listOfEls, options) {
$('.close', this.$titlebar).click(function() {
self.close();
});
// On delay, show the title bar.
var shouldShow = false;
$container.mouseover(function(){
shouldShow = true;
setTimeout(function(){
if( shouldShow == false ) return;
$container.find("input").focus();
self.$titlebar
.css({width: $container.width()})
.animate({ opacity: 1}).dequeue();
}, 500);
}).mouseout(function(e){
shouldShow = false;
if( isEventOverElement(e, $container.get(0) )) return;
self.$titlebar.animate({opacity:0}).dequeue();
})
this.$title = $('.name', this.$titlebar);
// ___ Content
this.$content = $('<div class="group-content"/>')
@ -132,6 +122,16 @@ window.Group = function(listOfEls, options) {
// ----------
window.Group.prototype = $.extend(new Item(), new Subscribable(), {
// ----------
getTitle: function() {
return (this.$title ? this.$title.val() : '');
},
// ----------
setValue: function(value) {
this.$title.val(value);
},
// ----------
_getBoundingBox: function(els) {
var el;
@ -366,71 +366,10 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
// ----------
arrange: function(options) {
var animate;
if(!options || typeof(options.animate) == 'undefined')
animate = true;
else
animate = options.animate;
if(typeof(options) == 'undefined')
options = {};
var bb = this.getContentBounds();
bb.inset(6, 6);
var tabAspect = TabItems.tabHeight / TabItems.tabWidth;
var count = this._children.length;
if(!count)
return;
var columns = 1;
var padding = 0;
var rows;
var tabWidth;
var tabHeight;
function figure() {
rows = Math.ceil(count / columns);
tabWidth = (bb.width / columns) - padding;
tabHeight = tabWidth * tabAspect;
}
figure();
while(rows > 1 && tabHeight * rows > bb.height) {
columns++;
figure();
}
if(rows == 1) {
tabWidth = Math.min(bb.width / count, bb.height / tabAspect);
tabHeight = tabWidth * tabAspect;
}
var box = new Rect(bb.left, bb.top, tabWidth, tabHeight);
var row = 0;
var column = 0;
var immediately;
$.each(this._children, function(index, child) {
if(animate == 'sometimes')
immediately = (typeof(child.groupData.row) == 'undefined' || child.groupData.row == row);
else
immediately = !animate;
child.setBounds(box, immediately);
child.groupData.column = column;
child.groupData.row = row;
box.left += box.width + padding;
column++;
if(column == columns) {
box.left = bb.left;
box.top += box.height + padding;
column = 0;
row++;
}
});
Items.arrange(this._children, bb, options);
},
// ----------
@ -601,6 +540,21 @@ window.Groups = {
}
},
// ----------
init: function() {
var self = this;
setTimeout(function() {
// we do this in a timeout, as window.innerHeight hasn't adjusted for Firebug initially
var pad = 5;
var sw = window.innerWidth;
var sh = window.innerHeight;
var w = sw - (pad * 2);
var h = TabItems.tabHeight;
var box = new Rect(pad, sh - (h + pad), w, h);
self.newTabGroup = new Group([], {bounds: box, title: 'New Tabs'});
}, 1000);
},
// ----------
register: function(group) {
Utils.assert('only register once per group', $.inArray(group, this.groups) == -1);
@ -651,21 +605,18 @@ window.Groups = {
newTab: function(tabItem) {
var groupTitle = 'New Tabs';
var array = jQuery.grep(this.groups, function(group) {
return group.title == groupTitle;
return group.getTitle() == groupTitle;
});
var $el = $(tabItem.container);
if(array.length)
array[0].add($el);
else {
var p = Page.findOpenSpaceFor($el); // TODO shouldn't know about Page
tabItem.setPosition(p.x, p.y, true);
var group = new Group([$el]);
group.title = groupTitle;
}
}
};
// ----------
Groups.init();
// ##########
$(".tab").data('isDragging', false)
.draggable(window.Groups.dragOptions)

View File

@ -219,6 +219,87 @@ window.Items = {
});
return items;
},
// ----------
// Function: arrange
// Arranges the given items in a grid within the given bounds,
// maximizing item size but maintaining standard tab aspect ratio for each
//
// Parameters:
// items - an array of <Item>s
// bounds - a <Rect> defining the space to arrange within
// options - an object with options. If options.animate is false, doesn't animate, otherwise it does.
arrange: function(items, bounds, options) {
var animate;
if(!options || typeof(options.animate) == 'undefined')
animate = true;
else
animate = options.animate;
if(typeof(options) == 'undefined')
options = {};
var tabAspect = TabItems.tabHeight / TabItems.tabWidth;
var count = items.length;
if(!count)
return;
var columns = 1;
var padding = options.padding || 0;
var yScale = 1.1; // to allow for titles
var rows;
var tabWidth;
var tabHeight;
var totalHeight;
function figure() {
rows = Math.ceil(count / columns);
tabWidth = (bounds.width - (padding * (columns - 1))) / columns;
tabHeight = tabWidth * tabAspect;
totalHeight = (tabHeight * yScale * rows) + (padding * (rows - 1));
}
figure();
while(rows > 1 && totalHeight > bounds.height) {
columns++;
figure();
}
if(rows == 1) {
tabWidth = Math.min(bounds.width / count, bounds.height / tabAspect);
tabHeight = tabWidth * tabAspect;
}
var box = new Rect(bounds.left, bounds.top, tabWidth, tabHeight);
var row = 0;
var column = 0;
var immediately;
$.each(items, function(index, item) {
/*
if(animate == 'sometimes')
immediately = (typeof(item.groupData.row) == 'undefined' || item.groupData.row == row);
else
*/
immediately = !animate;
item.setBounds(box, immediately);
/*
item.groupData.column = column;
item.groupData.row = row;
*/
box.left += box.width + padding;
column++;
if(column == columns) {
box.left = bounds.left;
box.top += (box.height * yScale) + padding;
column = 0;
row++;
}
});
}
};

View File

@ -214,13 +214,24 @@ window.TabItems = {
$("<div class='close'></div>").appendTo($div);
$("<div class='expander'></div>").appendTo($div);
var items = [];
$div.each(function() {
var tab = Tabs.tab(this);
$(this).data('tabItem', new TabItem(this, tab));
var item = new TabItem(this, tab);
$(this).data('tabItem', item);
items.push(item);
});
if($div.length == 1)
Groups.newTab($div.data('tabItem'));
else {
var top = 20;
var bottom = TabItems.tabHeight + 10; // MAGIC NUMBER: giving room for the "new tabs" group
var box = new Rect(0, top, window.innerWidth, window.innerHeight - (top + bottom));
box.inset(20, 20);
Items.arrange(items, box, {padding: 10});
}
// TODO: Figure out this really weird bug?
// Why is that:

View File

@ -23,6 +23,7 @@ window.Page = {
startX: 30,
startY: 70,
// ----------
init: function() {
Utils.homeTab.raw.maxWidth = 60;
Utils.homeTab.raw.minWidth = 60;
@ -77,6 +78,7 @@ window.Page = {
);
},
// ----------
findOpenSpaceFor: function($div) {
var w = window.innerWidth;
var h = 0;
@ -162,8 +164,7 @@ window.Page = {
}
}
//----------------------------------------------------------
// ##########
function ArrangeClass(name, func){ this.init(name, func); };
ArrangeClass.prototype = {
init: function(name, func){
@ -177,90 +178,107 @@ ArrangeClass.prototype = {
}
}
//----------------------------------------------------------
var grid = new ArrangeClass("Grid", function(value) {
if(typeof(Groups) != 'undefined')
Groups.removeAll();
var immediately = false;
if(typeof(value) == 'boolean')
immediately = value;
var box = new Rect(Page.startX, Page.startY, TabItems.tabWidth, TabItems.tabHeight);
$(".tab:visible").each(function(i){
var item = Items.item(this);
item.setBounds(box, immediately);
box.left += box.width + 30;
if( box.left > window.innerWidth - (box.width + Page.startX)){ // includes allowance for the box shadow
box.left = Page.startX;
box.top += box.height + 30;
}
});
});
//----------------------------------------------------------
var site = new ArrangeClass("Site", function() {
Groups.removeAll();
// ##########
function UIClass(){
this.navBar = Navbar;
this.tabBar = Tabbar;
this.devMode = false;
var groups = [];
$(".tab:visible").each(function(i) {
$el = $(this);
var tab = Tabs.tab(this);
var url = tab.url;
var domain = url.split('/')[2];
var domainParts = domain.split('.');
var mainDomain = domainParts[domainParts.length - 2];
if(groups[mainDomain])
groups[mainDomain].push($(this));
else
groups[mainDomain] = [$(this)];
var self = this;
// ___ URL Params
var params = document.location.search.replace('?', '').split('&');
$.each(params, function(index, param) {
var parts = param.split('=');
if(parts[0] == 'dev' && parts[1] == '1')
self.devMode = true;
});
var createOptions = {dontPush: true, dontArrange: true};
var leftovers = [];
for(key in groups) {
var set = groups[key];
if(set.length > 1) {
group = new Group(set, createOptions);
} else
leftovers.push(set[0]);
// ___ Dev Mode
if(this.devMode) {
Switch.insert('#nav', '');
this._addArrangements();
}
/* if(leftovers.length > 1) { */
group = new Group(leftovers, createOptions);
/* } */
// ___ Navbar
this.navBar.hide();
Utils.homeTab.onFocus(function(){
self.navBar.hide();
});
Groups.arrange();
});
$(window).blur(function(){
self.navBar.show();
});
//----------------------------------------------------------
var Arrange = {
initialized: false,
// ___ Finish up
Page.init();
};
init: function(){
this.initialized = true;
grid.arrange(true);
}
}
//----------------------------------------------------------
function UIClass(){ this.init(); };
// ----------
UIClass.prototype = {
navbar: Navbar,
tabbar: Tabbar,
init: function(){
Page.init();
Arrange.init();
_addArrangements: function() {
this.grid = new ArrangeClass("Grid", function(value) {
if(typeof(Groups) != 'undefined')
Groups.removeAll();
var immediately = false;
if(typeof(value) == 'boolean')
immediately = value;
var box = new Rect(Page.startX, Page.startY, TabItems.tabWidth, TabItems.tabHeight);
$(".tab:visible").each(function(i){
var item = Items.item(this);
item.setBounds(box, immediately);
box.left += box.width + 30;
if( box.left > window.innerWidth - (box.width + Page.startX)){ // includes allowance for the box shadow
box.left = Page.startX;
box.top += box.height + 30;
}
});
});
this.site = new ArrangeClass("Site", function() {
Groups.removeAll();
var groups = [];
$(".tab:visible").each(function(i) {
$el = $(this);
var tab = Tabs.tab(this);
var url = tab.url;
var domain = url.split('/')[2];
var domainParts = domain.split('.');
var mainDomain = domainParts[domainParts.length - 2];
if(groups[mainDomain])
groups[mainDomain].push($(this));
else
groups[mainDomain] = [$(this)];
});
var createOptions = {dontPush: true, dontArrange: true};
var leftovers = [];
for(key in groups) {
var set = groups[key];
if(set.length > 1) {
group = new Group(set, createOptions);
} else
leftovers.push(set[0]);
}
/* if(leftovers.length > 1) { */
group = new Group(leftovers, createOptions);
/* } */
Groups.arrange();
});
}
}
};
//----------------------------------------------------------
var UI = new UIClass();
window.UI = UI;
// ----------
window.UI = new UIClass();
})();

View File

@ -77,6 +77,7 @@ body {
height: 16px;
background: url(../img/closetab.png) no-repeat;
opacity: 0.2;
cursor: pointer;
}
.close:hover{ opacity: 1.0;}
@ -155,18 +156,17 @@ body {
}
.titlebar{
border: 1px solid #ccc;
font-size: 12px;
line-height: 18px;
height: 18px;
background: -moz-linear-gradient(90deg, #ccc,#eee);
display: none;
visibility: hidden;
}
input.name{
background: transparent;
border: none;
border: 1px solid #ddd;
color: #999;
margin: 3px 0px 0px 3px;
padding: 1px;
}
/* Resizable

View File

@ -49,15 +49,13 @@
<script type="text/javascript;version=1.8" src="js/items.js"></script>
<script type="text/javascript;version=1.8" src="js/tabitems.js"></script>
<script type="text/javascript;version=1.8" src="js/groups.js"></script>
<script type="text/javascript;version=1.8" src="js/ui.js"></script>
<!-- BEGIN Switch Control -->
<script type="text/javascript;version=1.8" src="../../js/optional/switch.js"></script>
<script type="text/javascript;version=1.8">
Switch.insert('#nav', '');
</script>
<!-- We create the actual switch in ui.js -->
<!-- END Switch Control -->
<script type="text/javascript;version=1.8" src="js/ui.js"></script>
<script type="text/javascript;version=1.8">
</script>