2010-02-24 01:47:55 -08:00
|
|
|
(function(){
|
|
|
|
|
|
|
|
$.expr[':'].icontains = function(obj, index, meta, stack){
|
|
|
|
return (obj.textContent || obj.innerText || jQuery(obj).text() || '').toLowerCase().indexOf(meta[3].toLowerCase()) >= 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
Navbar = {
|
|
|
|
get el(){
|
|
|
|
var win = Utils.activeWindow;
|
|
|
|
var navbar = win.gBrowser.ownerDocument.getElementById("navigator-toolbox");
|
|
|
|
return navbar;
|
|
|
|
},
|
|
|
|
show: function(){ this.el.collapsed = false; },
|
|
|
|
hide: function(){ this.el.collapsed = true;}
|
|
|
|
}
|
|
|
|
|
|
|
|
var Tabbar = {
|
|
|
|
get el(){ return window.Tabs[0].raw.parentNode; },
|
|
|
|
hide: function(){ this.el.collapsed = true },
|
|
|
|
show: function(){ this.el.collapsed = false }
|
|
|
|
}
|
|
|
|
|
|
|
|
var Page = {
|
2010-03-24 14:38:23 -07:00
|
|
|
init: function() {
|
|
|
|
var self = this;
|
|
|
|
|
2010-03-09 13:50:26 -08:00
|
|
|
function mod($div, tab){
|
2010-03-09 15:46:39 -08:00
|
|
|
if(window.Groups) {
|
|
|
|
$div.draggable(window.Groups.dragOptions);
|
|
|
|
$div.droppable(window.Groups.dropOptions);
|
|
|
|
}
|
2010-03-24 14:38:23 -07:00
|
|
|
|
|
|
|
$div.mousedown(function(e) {
|
2010-03-24 16:54:48 -07:00
|
|
|
if(!Utils.isRightClick(e))
|
|
|
|
self.lastMouseDownTarget = e.target;
|
2010-03-24 14:38:23 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
$div.mouseup(function(e) {
|
|
|
|
var same = (e.target == self.lastMouseDownTarget);
|
|
|
|
self.lastMouseDownTarget = null;
|
|
|
|
if(!same)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(e.target.className == "close") {
|
2010-02-24 01:47:55 -08:00
|
|
|
$(this).find("canvas").data("link").tab.close(); }
|
|
|
|
else {
|
2010-03-19 13:46:22 -07:00
|
|
|
if(!$(this).data('isDragging')) {
|
|
|
|
var ffVersion = parseFloat(navigator.userAgent.match(/\d{8}.*(\d\.\d)/)[1]);
|
2010-03-19 13:51:05 -07:00
|
|
|
if( ffVersion < 3.7 ) Utils.error("css-transitions require Firefox 3.7+");
|
2010-03-19 13:46:22 -07:00
|
|
|
|
2010-03-22 19:19:40 -07:00
|
|
|
// ZOOM!
|
|
|
|
var [w,h,z] = [$(this).width(), $(this).height(), $(this).css("zIndex")];
|
2010-03-19 13:21:21 -07:00
|
|
|
var origPos = $(this).position();
|
2010-03-20 11:01:45 -07:00
|
|
|
var zIndex = $(this).css("zIndex");
|
2010-03-19 13:21:21 -07:00
|
|
|
var scale = window.innerWidth/w;
|
|
|
|
|
2010-03-22 17:12:32 -07:00
|
|
|
var tab = Tabs.tab(this);
|
|
|
|
var mirror = tab.mirror;
|
2010-03-22 19:19:40 -07:00
|
|
|
//mirror.forceCanvasSize(w * scale/3, h * scale);
|
2010-03-22 17:12:32 -07:00
|
|
|
|
2010-03-22 19:19:40 -07:00
|
|
|
var overflow = $("body").css("overflow");
|
|
|
|
$("body").css("overflow", "hidden");
|
|
|
|
|
|
|
|
$(this).css("zIndex",99999).animate({
|
2010-03-23 18:16:19 -07:00
|
|
|
top: -10, left: 0, easing: "easein",
|
2010-03-22 19:19:40 -07:00
|
|
|
width:w*scale, height:h*scale}, 200, function(){
|
|
|
|
$(this).find("canvas").data("link").tab.focus();
|
|
|
|
$(this)
|
|
|
|
.css({top: origPos.top, left: origPos.left, width:w, height:h, zIndex:z});
|
|
|
|
Navbar.show();
|
|
|
|
$("body").css("overflow", overflow);
|
|
|
|
});
|
|
|
|
|
|
|
|
// I'm temporarily giving up on CSS-Transforms and Firefox 3.7.
|
|
|
|
// It introduces way to many errors... -- Aza
|
|
|
|
|
|
|
|
/*$(this).addClass("scale-animate").css({
|
2010-03-19 13:21:21 -07:00
|
|
|
top: 0, left: 0,
|
2010-03-20 11:01:45 -07:00
|
|
|
width:w*scale, height:h*scale,
|
|
|
|
zIndex: 999999
|
2010-03-19 13:21:21 -07:00
|
|
|
}).bind("transitionend", function(e){
|
|
|
|
// We will get one of this events for every property CSS-animated...
|
2010-03-22 19:19:40 -07:00
|
|
|
// I chose one randomly (width) and only do things for that.
|
2010-03-19 13:21:21 -07:00
|
|
|
if( e.originalEvent.propertyName != "width" ) return;
|
|
|
|
// Switch tabs, and the re-size and re-position the animated
|
|
|
|
// tab image.
|
2010-03-22 19:19:40 -07:00
|
|
|
// Don't forget to unbind the transitioned event handler!
|
|
|
|
$(this).unbind("transitionend");
|
2010-03-19 13:21:21 -07:00
|
|
|
$(this).find("canvas").data("link").tab.focus();
|
|
|
|
$(this)
|
|
|
|
.removeClass("scale-animate")
|
2010-03-20 11:01:45 -07:00
|
|
|
.css({top: origPos.top, left: origPos.left, width:w, height:h, zIndex:zIndex})
|
|
|
|
.unbind("transitionend");
|
2010-03-19 13:21:21 -07:00
|
|
|
Navbar.show();
|
2010-03-22 19:19:40 -07:00
|
|
|
//mirror.unforceCanvasSize();
|
|
|
|
})*/
|
2010-03-19 13:21:21 -07:00
|
|
|
// END ZOOM
|
|
|
|
|
2010-02-24 01:47:55 -08:00
|
|
|
} else {
|
|
|
|
$(this).find("canvas").data("link").tab.raw.pos = $(this).position();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2010-03-09 13:50:26 -08:00
|
|
|
$("<div class='close'>x</div>").appendTo($div);
|
2010-03-24 10:34:25 -07:00
|
|
|
|
|
|
|
if(Arrange.initialized) {
|
|
|
|
var p = Page.findOpenSpaceFor($div);
|
|
|
|
$div.css({left: p.x, top: p.y});
|
|
|
|
}
|
2010-03-09 13:50:26 -08:00
|
|
|
|
|
|
|
// TODO: Figure out this really weird bug?
|
|
|
|
// Why is that:
|
|
|
|
// $div.find("canvas").data("link").tab.url
|
|
|
|
// returns chrome://tabcandy/content/candies/original/index.html for
|
|
|
|
// every $div (which isn't right), but that
|
|
|
|
// $div.bind("test", function(){
|
|
|
|
// var url = $(this).find("canvas").data("link").tab.url;
|
|
|
|
// });
|
|
|
|
// $div.trigger("test")
|
|
|
|
// returns the right result (i.e., the per-tab URL)?
|
|
|
|
// I'm so confused...
|
|
|
|
// Although I can use the trigger trick, I was thinking about
|
|
|
|
// adding code in here which sorted the tabs into groups.
|
|
|
|
// -- Aza
|
2010-02-24 01:47:55 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
window.TabMirror.customize(mod);
|
|
|
|
|
|
|
|
Utils.homeTab.raw.maxWidth = 60;
|
|
|
|
Utils.homeTab.raw.minWidth = 60;
|
|
|
|
|
|
|
|
Tabs.onClose(function(){
|
|
|
|
Utils.homeTab.focus();
|
|
|
|
return false;
|
2010-03-19 15:21:12 -07:00
|
|
|
});
|
|
|
|
|
2010-03-19 16:10:43 -07:00
|
|
|
var lastTab = null;
|
|
|
|
Tabs.onFocus(function(){
|
|
|
|
// If we switched to TabCandy window...
|
2010-03-24 14:38:23 -07:00
|
|
|
if( this.contentWindow == window && lastTab != null && lastTab.mirror != null){
|
2010-03-19 16:10:43 -07:00
|
|
|
// If there was a lastTab we want to animate
|
|
|
|
// its mirror for the zoom out.
|
|
|
|
var $tab = $(lastTab.mirror.el);
|
|
|
|
|
2010-03-22 19:19:40 -07:00
|
|
|
var [w,h, pos, z] = [$tab.width(), $tab.height(), $tab.position(), $tab.css("zIndex")];
|
2010-03-22 17:12:32 -07:00
|
|
|
var scale = window.innerWidth / w;
|
2010-03-22 19:19:40 -07:00
|
|
|
|
|
|
|
var overflow = $("body").css("overflow");
|
|
|
|
$("body").css("overflow", "hidden");
|
|
|
|
|
2010-03-22 17:12:32 -07:00
|
|
|
var mirror = lastTab.mirror;
|
2010-03-19 16:10:43 -07:00
|
|
|
$tab.css({
|
|
|
|
top: 0, left: 0,
|
|
|
|
width: window.innerWidth,
|
|
|
|
height: h * (window.innerWidth/w),
|
|
|
|
zIndex: 999999,
|
2010-03-22 17:12:32 -07:00
|
|
|
}).animate({
|
2010-03-19 16:10:43 -07:00
|
|
|
top: pos.top, left: pos.left,
|
|
|
|
width: w, height: h
|
2010-03-22 17:12:32 -07:00
|
|
|
},250, '', function() {
|
2010-03-22 19:19:40 -07:00
|
|
|
$tab.css("zIndex",z);
|
|
|
|
$("body").css("overflow", overflow);
|
2010-03-22 17:12:32 -07:00
|
|
|
});
|
2010-03-19 16:10:43 -07:00
|
|
|
}
|
|
|
|
lastTab = this;
|
2010-03-19 15:21:12 -07:00
|
|
|
});
|
2010-02-24 01:47:55 -08:00
|
|
|
|
|
|
|
$("#tabbar").toggle(
|
|
|
|
function(){Tabbar.hide()},
|
|
|
|
function(){Tabbar.show()}
|
2010-03-22 19:19:40 -07:00
|
|
|
);
|
2010-03-24 10:34:25 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
findOpenSpaceFor: function($div) {
|
|
|
|
var w = window.innerWidth;
|
|
|
|
var h = 0;
|
|
|
|
var startX = 30;
|
|
|
|
var startY = 100;
|
|
|
|
var bufferX = 30;
|
|
|
|
var bufferY = 30;
|
|
|
|
var rects = [];
|
|
|
|
var r;
|
|
|
|
var $el;
|
|
|
|
$(".tab:visible").each(function(i) {
|
|
|
|
if(this == $div.get(0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
$el = $(this);
|
|
|
|
r = {x: parseInt($el.css('left')),
|
|
|
|
y: parseInt($el.css('top')),
|
|
|
|
w: parseInt($el.css('width')) + bufferX,
|
|
|
|
h: parseInt($el.css('height')) + bufferY};
|
|
|
|
|
|
|
|
if(r.x + r.w > w)
|
|
|
|
w = r.x + r.w;
|
|
|
|
|
|
|
|
if(r.y + r.h > h)
|
|
|
|
h = r.y + r.h;
|
|
|
|
|
|
|
|
rects.push(r);
|
|
|
|
});
|
|
|
|
|
|
|
|
if(!h)
|
|
|
|
return { 'x': startX, 'y': startY };
|
|
|
|
|
|
|
|
var canvas = document.createElement('canvas');
|
|
|
|
$(canvas).attr({width:w,height:h});
|
|
|
|
|
|
|
|
var ctx = canvas.getContext('2d');
|
|
|
|
ctx.fillStyle = 'rgb(255, 255, 255)';
|
|
|
|
ctx.fillRect(0, 0, w, h);
|
|
|
|
ctx.fillStyle = 'rgb(0, 0, 0)';
|
|
|
|
var count = rects.length;
|
|
|
|
var a;
|
|
|
|
for(a = 0; a < count; a++) {
|
|
|
|
r = rects[a];
|
|
|
|
ctx.fillRect(r.x, r.y, r.w, r.h);
|
|
|
|
}
|
|
|
|
|
|
|
|
var divWidth = parseInt($div.css('width')) + bufferX;
|
|
|
|
var divHeight = parseInt($div.css('height')) + bufferY;
|
|
|
|
var strideX = divWidth / 4;
|
|
|
|
var strideY = divHeight / 4;
|
|
|
|
var data = ctx.getImageData(0, 0, w, h).data;
|
|
|
|
|
|
|
|
function isEmpty(x1, y1) {
|
|
|
|
return (x1 >= 0 && y1 >= 0
|
|
|
|
&& x1 < w && y1 < h
|
|
|
|
&& data[(y1 * w + x1) * 4]);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isEmptyBox(x1, y1) {
|
|
|
|
return (isEmpty(x1, y1)
|
|
|
|
&& isEmpty(x1 + (divWidth - 1), y1)
|
|
|
|
&& isEmpty(x1, y1 + (divHeight - 1))
|
|
|
|
&& isEmpty(x1 + (divWidth - 1), y1 + (divHeight - 1)));
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var y = startY; y < h; y += strideY) {
|
|
|
|
for(var x = startX; x < w; x += strideX) {
|
|
|
|
if(isEmptyBox(x, y)) {
|
|
|
|
for(; y > startY + 1; y--) {
|
|
|
|
if(!isEmptyBox(x, y - 1))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(; x > startX + 1; x--) {
|
|
|
|
if(!isEmptyBox(x - 1, y))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return { 'x': x, 'y': y };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return { 'x': startX, 'y': h };
|
2010-02-24 01:47:55 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
//----------------------------------------------------------
|
2010-02-24 01:47:55 -08:00
|
|
|
function ArrangeClass(name, func){ this.init(name, func); };
|
|
|
|
ArrangeClass.prototype = {
|
|
|
|
init: function(name, func){
|
|
|
|
this.$el = this._create(name);
|
|
|
|
this.arrange = func;
|
|
|
|
if(func) this.$el.click(func);
|
|
|
|
},
|
|
|
|
|
|
|
|
_create: function(name){
|
2010-03-24 10:34:25 -07:00
|
|
|
return $("<a class='action' href='#'/>").text(name).appendTo("#actions");
|
2010-02-24 01:47:55 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
|
|
|
|
//----------------------------------------------------------
|
|
|
|
var grid = new ArrangeClass("Grid", function(value) {
|
|
|
|
var immediately = false;
|
|
|
|
if(typeof(value) == 'boolean')
|
|
|
|
immediately = value;
|
|
|
|
|
|
|
|
var startX = 30;
|
|
|
|
var x = startX;
|
2010-02-24 01:47:55 -08:00
|
|
|
var y = 100;
|
|
|
|
$(".tab:visible").each(function(i){
|
|
|
|
$el = $(this);
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
if(immediately)
|
|
|
|
$el.css({top: y,left: x});
|
|
|
|
else {
|
|
|
|
TabMirror.pausePainting();
|
|
|
|
$el.animate({top: y,left: x}, 500, null, function() {
|
|
|
|
TabMirror.resumePainting();
|
|
|
|
});
|
2010-02-24 01:47:55 -08:00
|
|
|
}
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
x += $el.width() + 30;
|
|
|
|
if( x > window.innerWidth - ($el.width() + startX)){ // includes allowance for the box shadow
|
|
|
|
x = startX;
|
2010-02-24 01:47:55 -08:00
|
|
|
y += $el.height() + 30;
|
|
|
|
}
|
2010-03-24 10:34:25 -07:00
|
|
|
});
|
|
|
|
});
|
2010-02-24 01:47:55 -08:00
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
//----------------------------------------------------------
|
|
|
|
var site = new ArrangeClass("Site", function() {
|
|
|
|
var startX = 30;
|
|
|
|
var startY = 100;
|
|
|
|
var x = startX;
|
|
|
|
var y = startY;
|
|
|
|
var x2;
|
|
|
|
var y2;
|
|
|
|
var groups = [];
|
|
|
|
$(".tab:visible").each(function(i) {
|
|
|
|
$el = $(this);
|
|
|
|
var tab = Tabs.tab(this);
|
2010-02-24 01:47:55 -08:00
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
var group = $el.data('group');
|
|
|
|
if(group)
|
|
|
|
group.remove(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];
|
2010-02-24 01:47:55 -08:00
|
|
|
});
|
2010-03-24 10:34:25 -07:00
|
|
|
|
|
|
|
var leftovers = [];
|
|
|
|
for(key in groups) {
|
|
|
|
var set = groups[key];
|
|
|
|
if(set.length > 1) {
|
|
|
|
group = new Groups.Group();
|
|
|
|
group.create(set);
|
|
|
|
} else
|
|
|
|
leftovers.push(set[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(leftovers.length > 1) {
|
|
|
|
group = new Groups.Group();
|
|
|
|
group.create(leftovers);
|
|
|
|
}
|
|
|
|
|
|
|
|
Groups.arrange();
|
2010-02-24 01:47:55 -08:00
|
|
|
});
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
//----------------------------------------------------------
|
2010-02-24 01:47:55 -08:00
|
|
|
var Arrange = {
|
2010-03-24 10:34:25 -07:00
|
|
|
initialized: false,
|
|
|
|
|
2010-02-24 01:47:55 -08:00
|
|
|
init: function(){
|
2010-03-24 10:34:25 -07:00
|
|
|
this.initialized = true;
|
|
|
|
grid.arrange(true);
|
2010-02-24 01:47:55 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
//----------------------------------------------------------
|
2010-02-24 01:47:55 -08:00
|
|
|
function UIClass(){ this.init(); };
|
|
|
|
UIClass.prototype = {
|
|
|
|
navbar: Navbar,
|
|
|
|
tabbar: Tabbar,
|
|
|
|
init: function(){
|
|
|
|
Page.init();
|
|
|
|
Arrange.init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-24 10:34:25 -07:00
|
|
|
//----------------------------------------------------------
|
2010-02-24 01:47:55 -08:00
|
|
|
var UI = new UIClass();
|
|
|
|
window.UI = UI;
|
|
|
|
|
|
|
|
})();
|
|
|
|
|