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.
|
|
|
|
*
|
|
|
|
* The Original Code is tabitems.js.
|
|
|
|
*
|
|
|
|
* 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-06-23 02:25:34 -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 ***** */
|
|
|
|
|
|
|
|
// **********
|
|
|
|
// Title: tabitems.js
|
2010-05-11 17:22:06 -07:00
|
|
|
|
2010-03-29 11:55:13 -07:00
|
|
|
// ##########
|
2010-05-13 17:24:37 -07:00
|
|
|
// Class: TabItem
|
|
|
|
// An <Item> that represents a tab.
|
2010-03-29 11:55:13 -07:00
|
|
|
window.TabItem = function(container, tab) {
|
2010-05-18 17:08:45 -07:00
|
|
|
Utils.assert('container', container);
|
|
|
|
Utils.assert('tab', tab);
|
|
|
|
Utils.assert('tab.mirror', tab.mirror);
|
|
|
|
|
2010-04-30 17:24:03 -07:00
|
|
|
this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
|
2010-05-11 12:03:31 -07:00
|
|
|
this.locked = {};
|
2010-06-14 15:43:02 -07:00
|
|
|
this.isATabItem = true;
|
|
|
|
this._zoomPrep = false;
|
|
|
|
this.sizeExtra = new Point();
|
2010-06-19 17:45:23 -07:00
|
|
|
this.keepProportional = true;
|
2010-05-11 12:03:31 -07:00
|
|
|
|
2010-06-15 16:33:58 -07:00
|
|
|
// ___ set up div
|
2010-06-14 15:43:02 -07:00
|
|
|
var $div = iQ(container);
|
|
|
|
var self = this;
|
|
|
|
|
2010-06-15 14:38:55 -07:00
|
|
|
$div.data('tabItem', this);
|
2010-06-19 11:56:07 -07:00
|
|
|
this.isDragging = false;
|
2010-06-14 15:43:02 -07:00
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
this.sizeExtra.x = parseInt($div.css('padding-left'))
|
|
|
|
+ parseInt($div.css('padding-right'));
|
|
|
|
|
|
|
|
this.sizeExtra.y = parseInt($div.css('padding-top'))
|
|
|
|
+ parseInt($div.css('padding-bottom'));
|
|
|
|
|
|
|
|
this.bounds = $div.bounds();
|
|
|
|
this.bounds.width += this.sizeExtra.x;
|
|
|
|
this.bounds.height += this.sizeExtra.y;
|
|
|
|
|
2010-06-15 14:38:55 -07:00
|
|
|
// ___ superclass setup
|
|
|
|
this._init(container);
|
|
|
|
|
2010-06-15 16:33:58 -07:00
|
|
|
// ___ drag/drop
|
2010-06-15 14:38:55 -07:00
|
|
|
// override dropOptions with custom tabitem methods
|
|
|
|
// This is mostly to support the phantom groups.
|
|
|
|
this.dropOptions.drop = function(e){
|
2010-06-25 16:00:51 -07:00
|
|
|
var $target = iQ(this.container);
|
2010-06-22 16:42:06 -07:00
|
|
|
$target.removeClass("acceptsDrop");
|
2010-06-25 16:00:51 -07:00
|
|
|
var phantom = $target.data("phantomGroup");
|
2010-06-19 12:05:36 -07:00
|
|
|
|
|
|
|
var group = drag.info.item.parent;
|
|
|
|
if( group == null ){
|
|
|
|
phantom.removeClass("phantom");
|
|
|
|
phantom.removeClass("group-content");
|
2010-06-22 16:42:06 -07:00
|
|
|
group = new Group([$target, drag.info.$el], {container:phantom});
|
2010-06-19 12:05:36 -07:00
|
|
|
} else
|
|
|
|
group.add( drag.info.$el );
|
|
|
|
};
|
2010-06-22 16:42:06 -07:00
|
|
|
|
2010-06-15 14:38:55 -07:00
|
|
|
this.dropOptions.over = function(e){
|
2010-06-22 16:42:06 -07:00
|
|
|
var $target = iQ(this.container);
|
2010-06-15 14:38:55 -07:00
|
|
|
|
2010-07-03 18:13:31 -07:00
|
|
|
var groupBounds = Groups.getBoundingBox( [drag.info.$el, $target] );
|
|
|
|
groupBounds.inset( -20, -20 );
|
2010-06-15 14:38:55 -07:00
|
|
|
|
2010-06-19 12:05:36 -07:00
|
|
|
iQ(".phantom").remove();
|
|
|
|
var phantom = iQ("<div>")
|
|
|
|
.addClass('group phantom group-content')
|
|
|
|
.css({
|
2010-07-03 18:13:31 -07:00
|
|
|
position: "absolute",
|
2010-06-19 12:05:36 -07:00
|
|
|
zIndex: -99
|
|
|
|
})
|
2010-07-03 18:13:31 -07:00
|
|
|
.css(groupBounds)
|
2010-06-19 12:05:36 -07:00
|
|
|
.appendTo("body")
|
|
|
|
.hide()
|
|
|
|
.fadeIn();
|
|
|
|
|
|
|
|
$target.data("phantomGroup", phantom);
|
|
|
|
};
|
2010-06-22 16:42:06 -07:00
|
|
|
|
|
|
|
this.dropOptions.out = function(e){
|
|
|
|
var phantom = iQ(this.container).data("phantomGroup");
|
2010-06-19 12:05:36 -07:00
|
|
|
if(phantom) {
|
|
|
|
phantom.fadeOut(function(){
|
|
|
|
iQ(this).remove();
|
|
|
|
});
|
|
|
|
}
|
2010-06-22 16:42:06 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
this.draggable();
|
|
|
|
this.droppable(true);
|
2010-06-14 15:43:02 -07:00
|
|
|
|
2010-06-15 16:33:58 -07:00
|
|
|
// ___ more div setup
|
2010-06-14 15:43:02 -07:00
|
|
|
$div.mousedown(function(e) {
|
|
|
|
if(!Utils.isRightClick(e))
|
|
|
|
self.lastMouseDownTarget = e.target;
|
|
|
|
});
|
|
|
|
|
|
|
|
$div.mouseup(function(e) {
|
|
|
|
var same = (e.target == self.lastMouseDownTarget);
|
|
|
|
self.lastMouseDownTarget = null;
|
|
|
|
if(!same)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(iQ(e.target).hasClass("close"))
|
|
|
|
tab.close();
|
|
|
|
else {
|
2010-06-21 13:38:33 -07:00
|
|
|
if(!Items.item(this).isDragging)
|
2010-06-14 15:43:02 -07:00
|
|
|
self.zoomIn();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
iQ("<div>")
|
|
|
|
.addClass('close')
|
|
|
|
.appendTo($div);
|
|
|
|
|
|
|
|
iQ("<div>")
|
|
|
|
.addClass('expander')
|
|
|
|
.appendTo($div);
|
|
|
|
|
|
|
|
// ___ additional setup
|
2010-05-24 14:49:03 -07:00
|
|
|
this.reconnected = false;
|
2010-05-04 15:57:19 -07:00
|
|
|
this._hasBeenDrawn = false;
|
2010-03-29 11:55:13 -07:00
|
|
|
this.tab = tab;
|
2010-04-12 17:20:35 -07:00
|
|
|
this.setResizable(true);
|
2010-05-18 17:08:45 -07:00
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
this._updateDebugBounds();
|
|
|
|
|
2010-05-18 17:08:45 -07:00
|
|
|
TabItems.register(this);
|
|
|
|
this.tab.mirror.addOnClose(this, function(who, info) {
|
|
|
|
TabItems.unregister(self);
|
2010-06-15 21:08:39 -07:00
|
|
|
self.removeTrenches();
|
2010-05-24 14:49:03 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
this.tab.mirror.addSubscriber(this, 'urlChanged', function(who, info) {
|
|
|
|
if(!self.reconnected && (info.oldURL == 'about:blank' || !info.oldURL))
|
|
|
|
TabItems.reconnect(self);
|
|
|
|
|
|
|
|
self.save();
|
|
|
|
});
|
2010-03-29 11:55:13 -07:00
|
|
|
};
|
|
|
|
|
2010-06-03 15:31:18 -07:00
|
|
|
window.TabItem.prototype = iQ.extend(new Item(), {
|
2010-04-23 17:11:06 -07:00
|
|
|
// ----------
|
|
|
|
getStorageData: function() {
|
|
|
|
return {
|
2010-05-11 12:03:31 -07:00
|
|
|
bounds: this.getBounds(),
|
|
|
|
userSize: (isPoint(this.userSize) ? new Point(this.userSize) : null),
|
2010-04-26 13:37:39 -07:00
|
|
|
url: this.tab.url,
|
|
|
|
groupID: (this.parent ? this.parent.id : 0)
|
2010-04-23 17:11:06 -07:00
|
|
|
};
|
|
|
|
},
|
2010-05-21 15:44:15 -07:00
|
|
|
|
|
|
|
// ----------
|
|
|
|
save: function() {
|
|
|
|
try{
|
2010-06-14 16:56:27 -07:00
|
|
|
if(!this.tab || !this.tab.raw || !this.reconnected) // too soon/late to save
|
2010-05-21 15:44:15 -07:00
|
|
|
return;
|
2010-05-24 14:49:03 -07:00
|
|
|
|
2010-05-21 15:44:15 -07:00
|
|
|
var data = this.getStorageData();
|
2010-05-24 14:49:03 -07:00
|
|
|
if(TabItems.storageSanity(data))
|
|
|
|
Storage.saveTab(this.tab.raw, data);
|
2010-05-21 15:44:15 -07:00
|
|
|
}catch(e){
|
|
|
|
Utils.log("Error in saving tab value: "+e);
|
|
|
|
}
|
|
|
|
},
|
2010-04-23 17:11:06 -07:00
|
|
|
|
|
|
|
// ----------
|
|
|
|
getURL: function() {
|
|
|
|
return this.tab.url;
|
|
|
|
},
|
2010-06-28 16:11:22 -07:00
|
|
|
|
2010-03-29 16:08:48 -07:00
|
|
|
// ----------
|
2010-06-14 15:43:02 -07:00
|
|
|
setBounds: function(rect, immediately, options) {
|
2010-05-11 17:22:06 -07:00
|
|
|
if(!isRect(rect)) {
|
|
|
|
Utils.trace('TabItem.setBounds: rect is not a real rectangle!', rect);
|
|
|
|
return;
|
|
|
|
}
|
2010-06-03 15:31:18 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
if(!options)
|
|
|
|
options = {};
|
2010-06-15 16:08:21 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
if(this._zoomPrep)
|
|
|
|
this.bounds.copy(rect);
|
|
|
|
else {
|
|
|
|
var $container = iQ(this.container);
|
|
|
|
var $title = iQ('.tab-title', $container);
|
|
|
|
var $thumb = iQ('.thumb', $container);
|
|
|
|
var $close = iQ('.close', $container);
|
2010-06-24 23:59:32 -07:00
|
|
|
var $fav = iQ('.favicon', $container);
|
2010-06-14 15:43:02 -07:00
|
|
|
var css = {};
|
2010-04-01 17:20:59 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
const minFontSize = 8;
|
|
|
|
const maxFontSize = 15;
|
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
if(rect.left != this.bounds.left || options.force)
|
2010-06-14 15:43:02 -07:00
|
|
|
css.left = rect.left;
|
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
if(rect.top != this.bounds.top || options.force)
|
2010-06-14 15:43:02 -07:00
|
|
|
css.top = rect.top;
|
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
if(rect.width != this.bounds.width || options.force) {
|
2010-06-15 15:55:47 -07:00
|
|
|
css.width = rect.width - this.sizeExtra.x;
|
2010-06-14 15:43:02 -07:00
|
|
|
var scale = css.width / TabItems.tabWidth;
|
|
|
|
|
|
|
|
// The ease function ".5+.5*Math.tanh(2*x-2)" is a pretty
|
|
|
|
// little graph. It goes from near 0 at x=0 to near 1 at x=2
|
|
|
|
// smoothly and beautifully.
|
|
|
|
css.fontSize = minFontSize + (maxFontSize-minFontSize)*(.5+.5*Math.tanh(2*scale-2))
|
|
|
|
}
|
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
if(rect.height != this.bounds.height || options.force)
|
2010-06-15 15:55:47 -07:00
|
|
|
css.height = rect.height - this.sizeExtra.y;
|
2010-06-14 15:43:02 -07:00
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
if(iQ.isEmptyObject(css))
|
2010-06-14 15:43:02 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
this.bounds.copy(rect);
|
2010-04-01 17:20:59 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
// If this is a brand new tab don't animate it in from
|
|
|
|
// a random location (i.e., from [0,0]). Instead, just
|
|
|
|
// have it appear where it should be.
|
|
|
|
if(immediately || (!this._hasBeenDrawn) ) {
|
|
|
|
/* $container.stop(true, true); */
|
|
|
|
$container.css(css);
|
|
|
|
} else {
|
|
|
|
TabMirror.pausePainting();
|
|
|
|
$container.animate(css, {
|
|
|
|
duration: 200,
|
|
|
|
easing: 'tabcandyBounce',
|
|
|
|
complete: function() {
|
|
|
|
TabMirror.resumePainting();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/* }).dequeue(); */
|
|
|
|
}
|
|
|
|
|
|
|
|
if(css.fontSize && !this.inStack()) {
|
|
|
|
if(css.fontSize < minFontSize )
|
|
|
|
$title.fadeOut();//.dequeue();
|
|
|
|
else
|
|
|
|
$title.fadeIn();//.dequeue();
|
|
|
|
}
|
|
|
|
|
2010-06-25 15:15:51 -07:00
|
|
|
|
|
|
|
// Usage
|
|
|
|
// slide({60:0, 70:1}, 66);
|
|
|
|
// @60- return 0; at @70+ return 1; @65 return 0.5
|
|
|
|
function slider(bounds, val){
|
|
|
|
var keys = [];
|
|
|
|
for(var key in bounds){ keys.push(key); bounds[key] = parseFloat(bounds[key]); };
|
|
|
|
keys.sort(function(a,b){return a-b});
|
|
|
|
var min = keys[0], max = keys[1];
|
|
|
|
|
|
|
|
function slide(value){
|
|
|
|
if( value >= max ) return bounds[max];
|
|
|
|
if( value <= min ) return bounds[min];
|
|
|
|
var rise = bounds[max] - bounds[min];
|
|
|
|
var run = max-min;
|
|
|
|
var value = rise * (value-min)/run;
|
|
|
|
if( value >= bounds[max] ) return bounds[max];
|
|
|
|
if( value <= bounds[min] ) return bounds[min];
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( val == undefined ) return slide;
|
|
|
|
else return slide(val);
|
|
|
|
};
|
|
|
|
|
2010-06-24 23:59:32 -07:00
|
|
|
if(css.width && !this.inStack()) {
|
|
|
|
$fav.css({top:4,left:4});
|
2010-06-25 15:15:51 -07:00
|
|
|
|
|
|
|
var opacity = slider({70:1, 60:0}, css.width);
|
|
|
|
$close.show().css({opacity:opacity});
|
|
|
|
if( opacity <= .1 ) $close.hide()
|
|
|
|
|
|
|
|
var pad = slider({70:6, 60:1}, css.width);
|
|
|
|
$fav.css({
|
|
|
|
"padding-left": pad + "px",
|
|
|
|
"padding-right": pad + 2 + "px",
|
|
|
|
"padding-top": pad + "px",
|
|
|
|
"padding-bottom": pad + "px",
|
|
|
|
"border-color": "rgba(0,0,0,"+ slider({70:.2, 60:.1}, css.width) +")",
|
|
|
|
});
|
2010-06-24 23:59:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if(css.width && this.inStack()){
|
2010-06-25 15:15:51 -07:00
|
|
|
$fav.css({top:0, left:0});
|
|
|
|
var opacity = slider({90:1, 70:0}, css.width);
|
|
|
|
|
|
|
|
var pad = slider({90:6, 70:1}, css.width);
|
|
|
|
$fav.css({
|
|
|
|
"padding-left": pad + "px",
|
|
|
|
"padding-right": pad + 2 + "px",
|
|
|
|
"padding-top": pad + "px",
|
|
|
|
"padding-bottom": pad + "px",
|
|
|
|
"border-color": "rgba(0,0,0,"+ slider({90:.2, 70:.1}, css.width) +")",
|
|
|
|
});
|
2010-06-24 23:59:32 -07:00
|
|
|
}
|
2010-03-29 16:08:48 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
this._hasBeenDrawn = true;
|
2010-04-12 17:20:35 -07:00
|
|
|
}
|
|
|
|
|
2010-04-01 17:20:59 -07:00
|
|
|
this._updateDebugBounds();
|
2010-06-22 20:45:51 -07:00
|
|
|
rect = this.getBounds(); // ensure that it's a <Rect>
|
2010-05-11 17:22:06 -07:00
|
|
|
|
|
|
|
if(!isRect(this.bounds))
|
|
|
|
Utils.trace('TabItem.setBounds: this.bounds is not a real rectangle!', this.bounds);
|
2010-06-19 12:05:36 -07:00
|
|
|
|
2010-06-15 16:08:21 -07:00
|
|
|
if (this.parent === null)
|
2010-06-19 12:05:36 -07:00
|
|
|
this.setTrenches(rect);
|
2010-05-21 15:44:15 -07:00
|
|
|
|
|
|
|
this.save();
|
2010-04-01 17:20:59 -07:00
|
|
|
},
|
2010-04-02 17:33:06 -07:00
|
|
|
|
2010-05-12 20:31:51 -07:00
|
|
|
// ----------
|
|
|
|
inStack: function(){
|
2010-06-04 12:34:03 -07:00
|
|
|
return iQ(this.container).hasClass("stacked");
|
2010-05-12 20:31:51 -07:00
|
|
|
},
|
|
|
|
|
2010-04-02 17:33:06 -07:00
|
|
|
// ----------
|
|
|
|
setZ: function(value) {
|
2010-06-04 12:34:03 -07:00
|
|
|
iQ(this.container).css({zIndex: value});
|
2010-04-02 17:33:06 -07:00
|
|
|
},
|
|
|
|
|
2010-03-29 16:08:48 -07:00
|
|
|
// ----------
|
|
|
|
close: function() {
|
|
|
|
this.tab.close();
|
2010-05-21 15:44:15 -07:00
|
|
|
|
|
|
|
// No need to explicitly delete the tab data, becasue sessionstore data
|
|
|
|
// associated with the tab will automatically go away
|
2010-03-29 16:08:48 -07:00
|
|
|
},
|
|
|
|
|
2010-04-29 00:42:37 -07:00
|
|
|
// ----------
|
|
|
|
addClass: function(className) {
|
2010-06-04 12:34:03 -07:00
|
|
|
iQ(this.container).addClass(className);
|
2010-04-29 00:42:37 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
removeClass: function(className) {
|
2010-06-04 12:34:03 -07:00
|
|
|
iQ(this.container).removeClass(className);
|
2010-04-29 00:42:37 -07:00
|
|
|
},
|
|
|
|
|
2010-03-29 16:08:48 -07:00
|
|
|
// ----------
|
|
|
|
addOnClose: function(referenceObject, callback) {
|
|
|
|
this.tab.mirror.addOnClose(referenceObject, callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
removeOnClose: function(referenceObject) {
|
|
|
|
this.tab.mirror.removeOnClose(referenceObject);
|
2010-04-12 17:20:35 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
setResizable: function(value){
|
2010-06-09 14:45:48 -07:00
|
|
|
var $resizer = iQ('.expander', this.container);
|
2010-06-19 17:45:23 -07:00
|
|
|
|
|
|
|
this.resizeOptions.minWidth = TabItems.minTabWidth;
|
|
|
|
this.resizeOptions.minHeight = TabItems.minTabWidth * (TabItems.tabHeight / TabItems.tabWidth);
|
|
|
|
|
2010-04-12 17:20:35 -07:00
|
|
|
if(value) {
|
|
|
|
$resizer.fadeIn();
|
2010-06-22 16:42:06 -07:00
|
|
|
this.resizable(true);
|
2010-04-12 17:20:35 -07:00
|
|
|
} else {
|
|
|
|
$resizer.fadeOut();
|
2010-06-22 16:42:06 -07:00
|
|
|
this.resizable(false);
|
2010-04-12 17:20:35 -07:00
|
|
|
}
|
2010-05-12 19:56:35 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
makeActive: function(){
|
2010-06-04 12:34:03 -07:00
|
|
|
iQ(this.container).find("canvas").addClass("focus")
|
2010-05-12 19:56:35 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
makeDeactive: function(){
|
2010-06-04 12:34:03 -07:00
|
|
|
iQ(this.container).find("canvas").removeClass("focus")
|
2010-05-12 19:56:35 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
2010-06-14 15:43:02 -07:00
|
|
|
// Function: zoomIn
|
|
|
|
// Allows you to select the tab and zoom in on it, thereby bringing you
|
|
|
|
// to the tab in Firefox to interact with.
|
|
|
|
zoomIn: function() {
|
|
|
|
var self = this;
|
|
|
|
var $tabEl = iQ(this.container);
|
|
|
|
var childHitResult = { shouldZoom: true };
|
|
|
|
if(this.parent)
|
|
|
|
childHitResult = this.parent.childHit(this);
|
|
|
|
|
|
|
|
if(childHitResult.shouldZoom) {
|
|
|
|
// Zoom in!
|
|
|
|
var orig = {
|
|
|
|
width: $tabEl.width(),
|
|
|
|
height: $tabEl.height(),
|
|
|
|
pos: $tabEl.position()
|
2010-06-25 16:00:51 -07:00
|
|
|
};
|
2010-06-14 15:43:02 -07:00
|
|
|
|
|
|
|
var scale = window.innerWidth/orig.width;
|
|
|
|
|
|
|
|
var tab = this.tab;
|
2010-06-23 02:25:34 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
function onZoomDone(){
|
|
|
|
TabMirror.resumePainting();
|
2010-06-23 02:25:34 -07:00
|
|
|
// If it's not focused, the onFocus lsitener would handle it.
|
2010-07-02 02:28:53 -07:00
|
|
|
Page.tabOnFocus(tab);
|
|
|
|
if (!tab.isFocused()) {
|
2010-06-23 02:25:34 -07:00
|
|
|
tab.focus();
|
|
|
|
}
|
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
$tabEl
|
|
|
|
.css({
|
|
|
|
top: orig.pos.top,
|
|
|
|
left: orig.pos.left,
|
|
|
|
width: orig.width,
|
|
|
|
height:orig.height,
|
|
|
|
})
|
2010-06-23 02:25:34 -07:00
|
|
|
.removeClass("front");
|
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
// If the tab is in a group set then set the active
|
|
|
|
// group to the tab's parent.
|
|
|
|
if( self.parent ){
|
|
|
|
var gID = self.parent.id;
|
|
|
|
var group = Groups.group(gID);
|
|
|
|
Groups.setActiveGroup( group );
|
|
|
|
group.setActiveTab( self );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Groups.setActiveGroup( null );
|
|
|
|
|
|
|
|
if(childHitResult.callback)
|
|
|
|
childHitResult.callback();
|
|
|
|
}
|
|
|
|
|
|
|
|
// The scaleCheat is a clever way to speed up the zoom-in code.
|
|
|
|
// Because image scaling is slowest on big images, we cheat and stop the image
|
|
|
|
// at scaled-down size and placed accordingly. Because the animation is fast, you can't
|
|
|
|
// see the difference but it feels a lot zippier. The only trick is choosing the
|
|
|
|
// right animation function so that you don't see a change in percieved
|
|
|
|
// animation speed.
|
|
|
|
var scaleCheat = 1.7;
|
|
|
|
TabMirror.pausePainting();
|
|
|
|
$tabEl
|
|
|
|
.addClass("front")
|
|
|
|
.animate({
|
|
|
|
top: orig.pos.top * (1-1/scaleCheat),
|
|
|
|
left: orig.pos.left * (1-1/scaleCheat),
|
|
|
|
width: orig.width*scale/scaleCheat,
|
|
|
|
height: orig.height*scale/scaleCheat
|
|
|
|
}, {
|
|
|
|
duration: 230,
|
|
|
|
easing: 'fast',
|
|
|
|
complete: onZoomDone
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: zoomOut
|
|
|
|
// Handles the zoom down animation after returning to TabCandy.
|
|
|
|
// It is expected that this routine will be called from the chrome thread
|
|
|
|
// (in response to Tabs.onFocus()).
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// complete - a function to call after the zoom down animation
|
|
|
|
zoomOut: function(complete) {
|
|
|
|
var $tab = iQ(this.container);
|
|
|
|
|
|
|
|
var box = this.getBounds();
|
|
|
|
box.width -= this.sizeExtra.x;
|
|
|
|
box.height -= this.sizeExtra.y;
|
|
|
|
|
|
|
|
TabMirror.pausePainting();
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
$tab.animate({
|
|
|
|
left: box.left,
|
|
|
|
top: box.top,
|
|
|
|
width: box.width,
|
|
|
|
height: box.height
|
|
|
|
}, {
|
|
|
|
duration: 300,
|
2010-06-14 17:23:17 -07:00
|
|
|
easing: 'cubic-bezier', // note that this is legal easing, even without parameters
|
2010-06-14 15:43:02 -07:00
|
|
|
complete: function() { // note that this will happen on the DOM thread
|
|
|
|
$tab.removeClass('front');
|
|
|
|
|
|
|
|
TabMirror.resumePainting();
|
|
|
|
|
|
|
|
self._zoomPrep = false;
|
|
|
|
self.setBounds(self.getBounds(), true, {force: true});
|
|
|
|
|
|
|
|
if(iQ.isFunction(complete))
|
|
|
|
complete();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: setZoomPrep
|
|
|
|
// Either go into or return from (depending on <value>) "zoom prep" mode,
|
|
|
|
// where the tab fills a large portion of the screen in anticipation of
|
|
|
|
// the zoom out animation.
|
|
|
|
setZoomPrep: function(value) {
|
|
|
|
var $div = iQ(this.container);
|
|
|
|
var data;
|
|
|
|
|
2010-06-25 16:00:51 -07:00
|
|
|
var box = this.getBounds();
|
2010-06-14 15:43:02 -07:00
|
|
|
if(value) {
|
|
|
|
this._zoomPrep = true;
|
|
|
|
|
|
|
|
// The divide by two part here is a clever way to speed up the zoom-out code.
|
|
|
|
// Because image scaling is slowest on big images, we cheat and start the image
|
|
|
|
// at half-size and placed accordingly. Because the animation is fast, you can't
|
|
|
|
// see the difference but it feels a lot zippier. The only trick is choosing the
|
|
|
|
// right animation function so that you don't see a change in percieved
|
|
|
|
// animation speed from frame #1 (the tab) to frame #2 (the half-size image) to
|
|
|
|
// frame #3 (the first frame of real animation). Choosing an animation that starts
|
|
|
|
// fast is key.
|
|
|
|
var scaleCheat = 2;
|
|
|
|
$div
|
|
|
|
.addClass('front')
|
|
|
|
.css({
|
|
|
|
left: box.left * (1-1/scaleCheat),
|
|
|
|
top: box.top * (1-1/scaleCheat),
|
|
|
|
width: window.innerWidth/scaleCheat,
|
|
|
|
height: box.height * (window.innerWidth / box.width)/scaleCheat
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this._zoomPrep = false;
|
2010-06-25 16:00:51 -07:00
|
|
|
$div.removeClass('front');
|
2010-06-14 15:43:02 -07:00
|
|
|
|
2010-06-28 16:11:22 -07:00
|
|
|
this.setBounds(box, true, {force: true});
|
2010-06-14 15:43:02 -07:00
|
|
|
}
|
2010-03-29 16:08:48 -07:00
|
|
|
}
|
|
|
|
});
|
2010-03-29 11:55:13 -07:00
|
|
|
|
|
|
|
// ##########
|
2010-05-13 17:24:37 -07:00
|
|
|
// Class: TabItems
|
|
|
|
// Singleton for managing <TabItem>s
|
2010-03-29 11:55:13 -07:00
|
|
|
window.TabItems = {
|
2010-04-12 17:20:35 -07:00
|
|
|
minTabWidth: 40,
|
2010-03-31 17:24:16 -07:00
|
|
|
tabWidth: 160,
|
2010-04-09 12:49:54 -07:00
|
|
|
tabHeight: 120,
|
2010-03-31 17:24:16 -07:00
|
|
|
fontSize: 9,
|
|
|
|
|
2010-04-23 17:11:06 -07:00
|
|
|
// ----------
|
2010-03-29 11:55:13 -07:00
|
|
|
init: function() {
|
2010-05-18 17:08:45 -07:00
|
|
|
this.items = [];
|
|
|
|
|
2010-03-29 11:55:13 -07:00
|
|
|
var self = this;
|
2010-06-14 15:43:02 -07:00
|
|
|
window.TabMirror.customize(function(mirror) {
|
2010-06-04 12:34:03 -07:00
|
|
|
var $div = iQ(mirror.el);
|
2010-05-26 17:04:56 -07:00
|
|
|
var tab = mirror.tab;
|
2010-06-14 15:43:02 -07:00
|
|
|
|
2010-06-23 02:25:34 -07:00
|
|
|
//if(tab == Utils.homeTab)
|
|
|
|
// $div.hide();
|
|
|
|
//else {
|
2010-05-26 17:04:56 -07:00
|
|
|
var item = new TabItem(mirror.el, tab);
|
|
|
|
|
|
|
|
item.addOnClose(self, function() {
|
|
|
|
Items.unsquish(null, item);
|
|
|
|
});
|
2010-05-07 15:49:54 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
if(!self.reconnect(item))
|
2010-05-26 17:04:56 -07:00
|
|
|
Groups.newTab(item);
|
2010-06-23 02:25:34 -07:00
|
|
|
//}
|
2010-06-14 15:43:02 -07:00
|
|
|
});
|
2010-04-23 17:11:06 -07:00
|
|
|
},
|
2010-05-18 17:08:45 -07:00
|
|
|
|
|
|
|
// ----------
|
|
|
|
register: function(item) {
|
2010-06-04 12:34:03 -07:00
|
|
|
Utils.assert('only register once per item', iQ.inArray(item, this.items) == -1);
|
2010-05-18 17:08:45 -07:00
|
|
|
this.items.push(item);
|
|
|
|
},
|
2010-05-12 19:56:35 -07:00
|
|
|
|
2010-05-18 17:08:45 -07:00
|
|
|
// ----------
|
|
|
|
unregister: function(item) {
|
2010-06-04 12:34:03 -07:00
|
|
|
var index = iQ.inArray(item, this.items);
|
2010-05-18 17:08:45 -07:00
|
|
|
if(index != -1)
|
|
|
|
this.items.splice(index, 1);
|
|
|
|
},
|
|
|
|
|
2010-04-23 17:11:06 -07:00
|
|
|
// ----------
|
|
|
|
getItems: function() {
|
2010-05-18 17:08:45 -07:00
|
|
|
return Utils.copy(this.items);
|
2010-04-23 17:11:06 -07:00
|
|
|
},
|
2010-05-05 00:47:07 -07:00
|
|
|
|
2010-04-27 15:31:20 -07:00
|
|
|
// ----------
|
2010-06-04 12:34:03 -07:00
|
|
|
// Function: getItemByTabElement
|
|
|
|
// Given the DOM element that contains the tab's representation on screen,
|
|
|
|
// returns the <TabItem> it belongs to.
|
|
|
|
getItemByTabElement: function(tabElement) {
|
|
|
|
return iQ(tabElement).data("tabItem");
|
2010-04-27 15:31:20 -07:00
|
|
|
},
|
|
|
|
|
2010-05-24 14:49:03 -07:00
|
|
|
// ----------
|
|
|
|
saveAll: function() {
|
|
|
|
var items = this.getItems();
|
2010-06-04 12:34:03 -07:00
|
|
|
iQ.each(items, function(index, item) {
|
2010-05-24 14:49:03 -07:00
|
|
|
item.save();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2010-05-11 12:03:31 -07:00
|
|
|
// ----------
|
|
|
|
storageSanity: function(data) {
|
|
|
|
// TODO: check everything
|
|
|
|
var sane = true;
|
2010-05-24 14:49:03 -07:00
|
|
|
if(!isRect(data.bounds)) {
|
|
|
|
Utils.log('TabItems.storageSanity: bad bounds', data.bounds);
|
|
|
|
sane = false;
|
|
|
|
}
|
2010-05-11 12:03:31 -07:00
|
|
|
|
|
|
|
return sane;
|
|
|
|
},
|
|
|
|
|
2010-04-26 13:37:39 -07:00
|
|
|
// ----------
|
|
|
|
reconnect: function(item) {
|
2010-05-24 14:49:03 -07:00
|
|
|
var found = false;
|
|
|
|
|
2010-05-21 15:44:15 -07:00
|
|
|
try{
|
2010-06-14 16:56:27 -07:00
|
|
|
Utils.assert('item', item);
|
|
|
|
Utils.assert('item.tab', item.tab);
|
|
|
|
|
|
|
|
if(item.reconnected)
|
2010-05-21 15:44:15 -07:00
|
|
|
return true;
|
2010-06-14 16:56:27 -07:00
|
|
|
|
|
|
|
if(!item.tab.raw)
|
|
|
|
return false;
|
2010-05-21 15:44:15 -07:00
|
|
|
|
2010-07-01 17:05:46 -07:00
|
|
|
var tab = Storage.getTabData(item.tab.raw);
|
2010-05-24 14:49:03 -07:00
|
|
|
if (tab && this.storageSanity(tab)) {
|
2010-05-21 15:44:15 -07:00
|
|
|
if(item.parent)
|
|
|
|
item.parent.remove(item);
|
2010-05-11 12:03:31 -07:00
|
|
|
|
2010-05-21 15:44:15 -07:00
|
|
|
item.setBounds(tab.bounds, true);
|
|
|
|
|
|
|
|
if(isPoint(tab.userSize))
|
|
|
|
item.userSize = new Point(tab.userSize);
|
2010-05-13 17:24:37 -07:00
|
|
|
|
2010-05-21 15:44:15 -07:00
|
|
|
if(tab.groupID) {
|
|
|
|
var group = Groups.group(tab.groupID);
|
2010-05-24 14:49:03 -07:00
|
|
|
if(group) {
|
|
|
|
group.add(item);
|
2010-04-26 13:37:39 -07:00
|
|
|
|
2010-05-24 14:49:03 -07:00
|
|
|
if(item.tab == Utils.activeTab)
|
|
|
|
Groups.setActiveGroup(item.parent);
|
|
|
|
}
|
2010-05-21 15:44:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Groups.updateTabBarForActiveGroup();
|
|
|
|
|
|
|
|
item.reconnected = true;
|
|
|
|
found = true;
|
|
|
|
} else
|
|
|
|
item.reconnected = (item.tab.url != 'about:blank');
|
2010-04-26 13:37:39 -07:00
|
|
|
|
2010-05-21 15:44:15 -07:00
|
|
|
item.save();
|
|
|
|
}catch(e){
|
2010-06-14 16:56:27 -07:00
|
|
|
Utils.log(e);
|
2010-05-21 15:44:15 -07:00
|
|
|
}
|
|
|
|
|
2010-04-26 13:37:39 -07:00
|
|
|
return found;
|
2010-03-29 11:55:13 -07:00
|
|
|
}
|
|
|
|
};
|