mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
70bc3f688b
+ The function you give to TabMirror.customize now takes a Mirror rather than a jQuery object + iQ is coming along. I tried to put asserts in for all the standard functionality that's missing (and that we might not need, but that someone used to jQuery might expect). I've also been adding Natural Docs comments, to give a sense of the scope+ Got rid of TabCanvas.animate(), which we haven't used in a while. + No longer including switch.js, which we also haven't used in a while
501 lines
12 KiB
JavaScript
501 lines
12 KiB
JavaScript
// Title: tabitems.js (revision-a)
|
|
|
|
// ##########
|
|
// Class: TabItem
|
|
// An <Item> that represents a tab.
|
|
window.TabItem = function(container, tab) {
|
|
Utils.assert('container', container);
|
|
Utils.assert('tab', tab);
|
|
Utils.assert('tab.mirror', tab.mirror);
|
|
|
|
this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
|
|
this.locked = {};
|
|
|
|
this._init(container);
|
|
|
|
this.reconnected = false;
|
|
this._hasBeenDrawn = false;
|
|
this.tab = tab;
|
|
this.setResizable(true);
|
|
|
|
TabItems.register(this);
|
|
var self = this;
|
|
this.tab.mirror.addOnClose(this, function(who, info) {
|
|
TabItems.unregister(self);
|
|
});
|
|
|
|
this.tab.mirror.addSubscriber(this, 'urlChanged', function(who, info) {
|
|
if(!self.reconnected && (info.oldURL == 'about:blank' || !info.oldURL))
|
|
TabItems.reconnect(self);
|
|
|
|
self.save();
|
|
});
|
|
};
|
|
|
|
window.TabItem.prototype = $.extend(new Item(), {
|
|
// ----------
|
|
getStorageData: function() {
|
|
return {
|
|
bounds: this.getBounds(),
|
|
userSize: (isPoint(this.userSize) ? new Point(this.userSize) : null),
|
|
url: this.tab.url,
|
|
groupID: (this.parent ? this.parent.id : 0)
|
|
};
|
|
},
|
|
|
|
// ----------
|
|
save: function() {
|
|
try{
|
|
if (!("tab" in this) || !("raw" in this.tab) || !this.reconnected) // too soon to save
|
|
return;
|
|
|
|
var data = this.getStorageData();
|
|
if(TabItems.storageSanity(data))
|
|
Storage.saveTab(this.tab.raw, data);
|
|
}catch(e){
|
|
Utils.log("Error in saving tab value: "+e);
|
|
}
|
|
},
|
|
|
|
// ----------
|
|
getURL: function() {
|
|
return this.tab.url;
|
|
},
|
|
|
|
// ----------
|
|
_getSizeExtra: function() {
|
|
var $container = $(this.container);
|
|
|
|
var widthExtra = parseInt($container.css('padding-left'))
|
|
+ parseInt($container.css('padding-right'));
|
|
|
|
var heightExtra = parseInt($container.css('padding-top'))
|
|
+ parseInt($container.css('padding-bottom'));
|
|
|
|
return new Point(widthExtra, heightExtra);
|
|
},
|
|
|
|
// ----------
|
|
reloadBounds: function() {
|
|
var newBounds = Utils.getBounds(this.container);
|
|
|
|
/*
|
|
if(!this.bounds || newBounds.width != this.bounds.width || newBounds.height != this.bounds.height) {
|
|
// if resizing, or first time, do the whole deal
|
|
if(!this.bounds)
|
|
this.bounds = new Rect(0, 0, 0, 0);
|
|
|
|
this.setBounds(newBounds, true);
|
|
} else {
|
|
*/
|
|
// if we're just moving, this is more efficient
|
|
this.bounds = newBounds;
|
|
this._updateDebugBounds();
|
|
/* } */
|
|
|
|
this.save();
|
|
},
|
|
|
|
// ----------
|
|
setBounds: function(rect, immediately) {
|
|
if(!isRect(rect)) {
|
|
Utils.trace('TabItem.setBounds: rect is not a real rectangle!', rect);
|
|
return;
|
|
}
|
|
|
|
var $container = $(this.container);
|
|
var $title = $('.tab-title', $container);
|
|
var $thumb = $('.thumb', $container);
|
|
var $close = $('.close', $container);
|
|
var extra = this._getSizeExtra();
|
|
var css = {};
|
|
|
|
const minFontSize = 8;
|
|
const maxFontSize = 15;
|
|
|
|
if(rect.left != this.bounds.left)
|
|
css.left = rect.left;
|
|
|
|
if(rect.top != this.bounds.top)
|
|
css.top = rect.top;
|
|
|
|
if(rect.width != this.bounds.width) {
|
|
css.width = rect.width - extra.x;
|
|
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))
|
|
}
|
|
|
|
if(rect.height != this.bounds.height) {
|
|
css.height = rect.height - extra.y;
|
|
}
|
|
|
|
if($.isEmptyObject(css))
|
|
return;
|
|
|
|
this.bounds.copy(rect);
|
|
|
|
// 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,{
|
|
complete: function() {TabMirror.resumePainting();},
|
|
duration: 350,
|
|
easing: "tabcandyBounce"
|
|
}).dequeue();
|
|
}
|
|
|
|
if(css.fontSize && !this.inStack()) {
|
|
if(css.fontSize < minFontSize )
|
|
$title.fadeOut().dequeue();
|
|
else
|
|
$title.fadeIn().dequeue();
|
|
}
|
|
|
|
if(css.width) {
|
|
if(css.width < 30) {
|
|
$thumb.fadeOut();
|
|
$close.fadeOut();
|
|
} else {
|
|
$thumb.fadeIn();
|
|
$close.fadeIn();
|
|
}
|
|
}
|
|
|
|
this._updateDebugBounds();
|
|
this._hasBeenDrawn = true;
|
|
|
|
if(!isRect(this.bounds))
|
|
Utils.trace('TabItem.setBounds: this.bounds is not a real rectangle!', this.bounds);
|
|
|
|
this.save();
|
|
},
|
|
|
|
// ----------
|
|
inStack: function(){
|
|
return $(this.container).hasClass("stacked");
|
|
},
|
|
|
|
// ----------
|
|
setZ: function(value) {
|
|
$(this.container).css({zIndex: value});
|
|
},
|
|
|
|
// ----------
|
|
close: function() {
|
|
this.tab.close();
|
|
|
|
// No need to explicitly delete the tab data, becasue sessionstore data
|
|
// associated with the tab will automatically go away
|
|
},
|
|
|
|
// ----------
|
|
addClass: function(className) {
|
|
$(this.container).addClass(className);
|
|
},
|
|
|
|
// ----------
|
|
removeClass: function(className) {
|
|
$(this.container).removeClass(className);
|
|
},
|
|
|
|
// ----------
|
|
addOnClose: function(referenceObject, callback) {
|
|
this.tab.mirror.addOnClose(referenceObject, callback);
|
|
},
|
|
|
|
// ----------
|
|
removeOnClose: function(referenceObject) {
|
|
this.tab.mirror.removeOnClose(referenceObject);
|
|
},
|
|
|
|
// ----------
|
|
setResizable: function(value){
|
|
var self = this;
|
|
|
|
var $resizer = $('.expander', this.container);
|
|
if(value) {
|
|
$resizer.fadeIn();
|
|
$(this.container).resizable({
|
|
handles: "se",
|
|
aspectRatio: true,
|
|
minWidth: TabItems.minTabWidth,
|
|
minHeight: TabItems.minTabWidth * (TabItems.tabHeight / TabItems.tabWidth),
|
|
resize: function(){
|
|
self.reloadBounds();
|
|
},
|
|
stop: function(){
|
|
self.reloadBounds();
|
|
self.setUserSize();
|
|
self.pushAway();
|
|
}
|
|
});
|
|
} else {
|
|
$resizer.fadeOut();
|
|
$(this.container).resizable('destroy');
|
|
}
|
|
},
|
|
|
|
// ----------
|
|
makeActive: function(){
|
|
$(this.container).find("canvas").addClass("focus")
|
|
},
|
|
|
|
// ----------
|
|
makeDeactive: function(){
|
|
$(this.container).find("canvas").removeClass("focus")
|
|
},
|
|
|
|
// ----------
|
|
// Function: zoom
|
|
// Zooms into this tab thereby allowing you to interact with it.
|
|
zoom: function(){
|
|
TabItems.zoomTo(this.container);
|
|
}
|
|
});
|
|
|
|
// ##########
|
|
// Class: TabItems
|
|
// Singleton for managing <TabItem>s
|
|
window.TabItems = {
|
|
minTabWidth: 40,
|
|
tabWidth: 160,
|
|
tabHeight: 120,
|
|
fontSize: 9,
|
|
|
|
// ----------
|
|
init: function() {
|
|
this.items = [];
|
|
|
|
var self = this;
|
|
|
|
function mod(mirror) {
|
|
var $div = $(mirror.el);
|
|
var tab = mirror.tab;
|
|
|
|
if(window.Groups) {
|
|
$div.data('isDragging', false);
|
|
$div.draggable(window.Groups.dragOptions);
|
|
$div.droppable(window.Groups.dropOptions);
|
|
}
|
|
|
|
$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(e.target.className == "close")
|
|
tab.close();
|
|
else {
|
|
if(!$(this).data('isDragging'))
|
|
self.zoomTo(this);
|
|
else
|
|
tab.raw.pos = $(this).position(); // TODO: is this necessary?
|
|
}
|
|
});
|
|
|
|
$("<div class='close'></div>").appendTo($div);
|
|
$("<div class='expander'></div>").appendTo($div);
|
|
|
|
if(tab == Utils.homeTab)
|
|
$div.hide();
|
|
else {
|
|
var item = new TabItem(mirror.el, tab);
|
|
$div.data('tabItem', item);
|
|
|
|
item.addOnClose(self, function() {
|
|
Items.unsquish(null, item);
|
|
});
|
|
|
|
if(!TabItems.reconnect(item))
|
|
Groups.newTab(item);
|
|
}
|
|
}
|
|
|
|
window.TabMirror.customize(mod);
|
|
},
|
|
|
|
// ----------
|
|
register: function(item) {
|
|
Utils.assert('only register once per item', $.inArray(item, this.items) == -1);
|
|
this.items.push(item);
|
|
},
|
|
|
|
// ----------
|
|
unregister: function(item) {
|
|
var index = $.inArray(item, this.items);
|
|
if(index != -1)
|
|
this.items.splice(index, 1);
|
|
},
|
|
|
|
// ----------
|
|
// Function: zoomTo(container)
|
|
// Given the containing element of a tab, allows you to
|
|
// select that tab and zoom in on it, thereby bringing you
|
|
// to the tab in Firefox to interact with.
|
|
//
|
|
zoomTo: function(tabEl){
|
|
var self = this;
|
|
var item = $(tabEl).data('tabItem');
|
|
var childHitResult = { shouldZoom: true };
|
|
if(item.parent)
|
|
childHitResult = item.parent.childHit(item);
|
|
|
|
if(childHitResult.shouldZoom) {
|
|
// Zoom in!
|
|
var orig = {
|
|
width: $(tabEl).width(),
|
|
height: $(tabEl).height(),
|
|
pos: $(tabEl).position()
|
|
}
|
|
|
|
var scale = window.innerWidth/orig.width;
|
|
|
|
var tab = Tabs.tab(tabEl);
|
|
var mirror = tab.mirror;
|
|
|
|
var overflow = $("body").css("overflow");
|
|
$("body").css("overflow", "hidden");
|
|
|
|
function onZoomDone(){
|
|
UI.tabBar.show(false);
|
|
TabMirror.resumePainting();
|
|
tab.focus();
|
|
$(tabEl).css({
|
|
top: orig.pos.top,
|
|
left: orig.pos.left,
|
|
width: orig.width,
|
|
height:orig.height,
|
|
})
|
|
.removeClass("front");
|
|
Navbar.show();
|
|
|
|
// If the tab is in a group set then set the active
|
|
// group to the tab's parent.
|
|
if( self.getItemByTab(tabEl).parent ){
|
|
var gID = self.getItemByTab(tabEl).parent.id;
|
|
var group = Groups.group(gID);
|
|
Groups.setActiveGroup( group );
|
|
group.setActiveTab( tabEl );
|
|
}
|
|
else
|
|
Groups.setActiveGroup( null );
|
|
|
|
$("body").css("overflow", overflow);
|
|
|
|
if(childHitResult.callback)
|
|
childHitResult.callback();
|
|
}
|
|
|
|
TabMirror.pausePainting();
|
|
$(tabEl)
|
|
.addClass("front")
|
|
.animate({
|
|
top: -10,
|
|
left: 0,
|
|
width: orig.width*scale,
|
|
height: orig.height*scale
|
|
}, 200, "easeInQuad", onZoomDone);
|
|
}
|
|
},
|
|
|
|
// ----------
|
|
getItems: function() {
|
|
return Utils.copy(this.items);
|
|
},
|
|
|
|
// ----------
|
|
getItemByTab: function(tab) {
|
|
return $(tab).data("tabItem");
|
|
},
|
|
|
|
// ----------
|
|
saveAll: function() {
|
|
var items = this.getItems();
|
|
$.each(items, function(index, item) {
|
|
item.save();
|
|
});
|
|
},
|
|
|
|
// ----------
|
|
reconstitute: function() {
|
|
var items = this.getItems();
|
|
var self = this;
|
|
$.each(items, function(index, item) {
|
|
if(!self.reconnect(item))
|
|
Groups.newTab(item);
|
|
});
|
|
},
|
|
|
|
// ----------
|
|
storageSanity: function(data) {
|
|
// TODO: check everything
|
|
var sane = true;
|
|
if(!isRect(data.bounds)) {
|
|
Utils.log('TabItems.storageSanity: bad bounds', data.bounds);
|
|
sane = false;
|
|
}
|
|
|
|
return sane;
|
|
},
|
|
|
|
// ----------
|
|
reconnect: function(item) {
|
|
var found = false;
|
|
|
|
try{
|
|
if(item.reconnected) {
|
|
return true;
|
|
}
|
|
|
|
var tab = Storage.getTabData(item.tab.raw);
|
|
if (tab && this.storageSanity(tab)) {
|
|
if(item.parent)
|
|
item.parent.remove(item);
|
|
|
|
item.setBounds(tab.bounds, true);
|
|
|
|
if(isPoint(tab.userSize))
|
|
item.userSize = new Point(tab.userSize);
|
|
|
|
if(tab.groupID) {
|
|
var group = Groups.group(tab.groupID);
|
|
if(group) {
|
|
group.add(item);
|
|
|
|
if(item.tab == Utils.activeTab)
|
|
Groups.setActiveGroup(item.parent);
|
|
}
|
|
}
|
|
|
|
Groups.updateTabBarForActiveGroup();
|
|
|
|
item.reconnected = true;
|
|
found = true;
|
|
} else
|
|
item.reconnected = (item.tab.url != 'about:blank');
|
|
|
|
item.save();
|
|
}catch(e){
|
|
Utils.log("Error in TabItems.reconnect: "+e + " at " + e.fileName + "(" + e.lineNumber + ")");
|
|
}
|
|
|
|
return found;
|
|
}
|
|
};
|
|
|