843 lines
24 KiB
JavaScript
Executable File
843 lines
24 KiB
JavaScript
Executable File
/*
|
|
The frame is a container for a panel, and can contain multiple panels inside it, each appearing
|
|
as a tabbed item. All docking panels have a frame, but the frame can change any time the panel
|
|
is moved.
|
|
*/
|
|
function wcFrame(container, parent, isFloating) {
|
|
this.$container = $(container);
|
|
this._parent = parent;
|
|
this._isFloating = isFloating;
|
|
|
|
this.$frame = null;
|
|
this.$title = null;
|
|
this.$tabScroll = null;
|
|
this.$center = null;
|
|
this.$tabLeft = null;
|
|
this.$tabRight = null;
|
|
this.$close = null;
|
|
this.$top = null;
|
|
this.$bottom = null;
|
|
this.$left = null;
|
|
this.$right = null;
|
|
this.$corner1 = null;
|
|
this.$corner2 = null;
|
|
this.$corner3 = null;
|
|
this.$corner4 = null;
|
|
|
|
this.$shadower = null;
|
|
this.$modalBlocker = null;
|
|
|
|
this._canScrollTabs = false;
|
|
this._tabScrollPos = 0;
|
|
this._curTab = -1;
|
|
this._panelList = [];
|
|
this._buttonList = [];
|
|
|
|
this._resizeData = {
|
|
time: -1,
|
|
timeout: false,
|
|
delta: 150,
|
|
};
|
|
|
|
this._pos = {
|
|
x: 0.5,
|
|
y: 0.5,
|
|
};
|
|
|
|
this._size = {
|
|
x: 400,
|
|
y: 400,
|
|
};
|
|
|
|
this._lastSize = {
|
|
x: 400,
|
|
y: 400,
|
|
};
|
|
|
|
this._anchorMouse = {
|
|
x: 0,
|
|
y: 0,
|
|
};
|
|
|
|
this.__init();
|
|
};
|
|
|
|
wcFrame.prototype = {
|
|
LEFT_TAB_BUFFER: 15,
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Public Functions
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Gets, or Sets the position of the frame.
|
|
// Params:
|
|
// x, y If supplied, assigns the new position.
|
|
// pixels If true, the coordinates given will be treated as a
|
|
// pixel position rather than a percentage.
|
|
pos: function(x, y, pixels) {
|
|
var width = this.$container.width();
|
|
var height = this.$container.height();
|
|
|
|
if (typeof x === 'undefined') {
|
|
if (pixels) {
|
|
return {x: this._pos.x*width, y: this._pos.y*height};
|
|
} else {
|
|
return {x: this._pos.x, y: this._pos.y};
|
|
}
|
|
}
|
|
|
|
if (pixels) {
|
|
this._pos.x = x/width;
|
|
this._pos.y = y/height;
|
|
} else {
|
|
this._pos.x = x;
|
|
this._pos.y = y;
|
|
}
|
|
},
|
|
|
|
// Gets the desired size of the panel.
|
|
initSize: function() {
|
|
var size = {
|
|
x: -1,
|
|
y: -1,
|
|
};
|
|
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
if (size.x < this._panelList[i].initSize().x) {
|
|
size.x = this._panelList[i].initSize().x;
|
|
}
|
|
if (size.y < this._panelList[i].initSize().y) {
|
|
size.y = this._panelList[i].initSize().y;
|
|
}
|
|
}
|
|
|
|
if (size.x < 0 || size.y < 0) {
|
|
return false;
|
|
}
|
|
return size;
|
|
},
|
|
|
|
// Gets the minimum size of the panel.
|
|
minSize: function() {
|
|
var size = {
|
|
x: 0,
|
|
y: 0,
|
|
};
|
|
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
size.x = Math.max(size.x, this._panelList[i].minSize().x);
|
|
size.y = Math.max(size.y, this._panelList[i].minSize().y);
|
|
}
|
|
return size;
|
|
},
|
|
|
|
// Gets the minimum size of the panel.
|
|
maxSize: function() {
|
|
var size = {
|
|
x: Infinity,
|
|
y: Infinity,
|
|
};
|
|
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
size.x = Math.min(size.x, this._panelList[i].maxSize().x);
|
|
size.y = Math.min(size.y, this._panelList[i].maxSize().y);
|
|
}
|
|
return size;
|
|
},
|
|
|
|
// Adds a given panel as a new tab item.
|
|
// Params:
|
|
// panel The panel to add.
|
|
// index An optional index to insert the tab at.
|
|
addPanel: function(panel, index) {
|
|
var found = this._panelList.indexOf(panel);
|
|
if (found !== -1) {
|
|
this._panelList.splice(found, 1);
|
|
}
|
|
|
|
if (typeof index === 'undefined') {
|
|
this._panelList.push(panel);
|
|
} else {
|
|
this._panelList.splice(index, 0, panel);
|
|
}
|
|
|
|
if (this._curTab === -1 && this._panelList.length) {
|
|
this._curTab = 0;
|
|
this._size = this.initSize();
|
|
}
|
|
|
|
this.__updateTabs();
|
|
},
|
|
|
|
// Removes a given panel from the tab item.
|
|
// Params:
|
|
// panel The panel to remove.
|
|
// Returns:
|
|
// bool Returns whether or not any panels still remain.
|
|
removePanel: function(panel) {
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
if (this._panelList[i] === panel) {
|
|
if (this._curTab >= i) {
|
|
this._curTab--;
|
|
}
|
|
|
|
this._panelList[i].__container(null);
|
|
this._panelList[i]._parent = null;
|
|
|
|
this._panelList.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (this._curTab === -1 && this._panelList.length) {
|
|
this._curTab = 0;
|
|
}
|
|
|
|
this.__updateTabs();
|
|
return this._panelList.length > 0;
|
|
},
|
|
|
|
// Gets, or Sets the currently visible panel.
|
|
// Params:
|
|
// tabIndex If supplied, sets the current tab.
|
|
// Returns:
|
|
// wcPanel The currently visible panel.
|
|
panel: function(tabIndex, autoFocus) {
|
|
if (typeof tabIndex !== 'undefined') {
|
|
if (tabIndex > -1 && tabIndex < this._panelList.length) {
|
|
this.$title.find('> .wcTabScroller > .wcPanelTab[id="' + this._curTab + '"]').removeClass('wcPanelTabActive');
|
|
this.$center.children('.wcPanelTabContent[id="' + this._curTab + '"]').addClass('wcPanelTabContentHidden');
|
|
this._curTab = tabIndex;
|
|
this.$title.find('> .wcTabScroller > .wcPanelTab[id="' + tabIndex + '"]').addClass('wcPanelTabActive');
|
|
this.$center.children('.wcPanelTabContent[id="' + tabIndex + '"]').removeClass('wcPanelTabContentHidden');
|
|
this.__updateTabs(autoFocus);
|
|
}
|
|
}
|
|
|
|
if (this._curTab > -1 && this._curTab < this._panelList.length) {
|
|
return this._panelList[this._curTab];
|
|
}
|
|
return false;
|
|
},
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Private Functions
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Initialize
|
|
__init: function() {
|
|
this.$frame = $('<div class="wcFrame wcWide wcTall wcPanelBackground">');
|
|
this.$title = $('<div class="wcFrameTitle">');
|
|
this.$tabScroll = $('<div class="wcTabScroller">');
|
|
this.$center = $('<div class="wcFrameCenter wcWide">');
|
|
this.$tabLeft = $('<div class="wcFrameButton" title="Scroll tabs to the left."><span class="fa fa-arrow-left"></span><</div>');
|
|
this.$tabRight = $('<div class="wcFrameButton" title="Scroll tabs to the right."><span class="fa fa-arrow-right"></span>></div>');
|
|
this.$close = $('<div class="wcFrameButton" title="Close the currently active panel tab"><div class="fa fa-close"></div>X</div>');
|
|
// this.$frame.append(this.$title);
|
|
this.$title.append(this.$tabScroll);
|
|
this.$frame.append(this.$close);
|
|
|
|
if (this._isFloating) {
|
|
this.$top = $('<div class="wcFrameEdgeH wcFrameEdge"></div>').css('top', '-6px').css('left', '0px').css('right', '0px');
|
|
this.$bottom = $('<div class="wcFrameEdgeH wcFrameEdge"></div>').css('bottom', '-6px').css('left', '0px').css('right', '0px');
|
|
this.$left = $('<div class="wcFrameEdgeV wcFrameEdge"></div>').css('left', '-6px').css('top', '0px').css('bottom', '0px');
|
|
this.$right = $('<div class="wcFrameEdgeV wcFrameEdge"></div>').css('right', '-6px').css('top', '0px').css('bottom', '0px');
|
|
this.$corner1 = $('<div class="wcFrameCornerNW wcFrameEdge"></div>').css('top', '-6px').css('left', '-6px');
|
|
this.$corner2 = $('<div class="wcFrameCornerNE wcFrameEdge"></div>').css('top', '-6px').css('right', '-6px');
|
|
this.$corner3 = $('<div class="wcFrameCornerNW wcFrameEdge"></div>').css('bottom', '-6px').css('right', '-6px');
|
|
this.$corner4 = $('<div class="wcFrameCornerNE wcFrameEdge"></div>').css('bottom', '-6px').css('left', '-6px');
|
|
|
|
this.$frame.append(this.$top);
|
|
this.$frame.append(this.$bottom);
|
|
this.$frame.append(this.$left);
|
|
this.$frame.append(this.$right);
|
|
this.$frame.append(this.$corner1);
|
|
this.$frame.append(this.$corner2);
|
|
this.$frame.append(this.$corner3);
|
|
this.$frame.append(this.$corner4);
|
|
}
|
|
|
|
this.$frame.append(this.$center);
|
|
|
|
// Floating windows have no container.
|
|
this.__container(this.$container);
|
|
|
|
if (this._isFloating) {
|
|
this.$frame.addClass('wcFloating');
|
|
}
|
|
|
|
this.$center.scroll(this.__scrolled.bind(this));
|
|
},
|
|
|
|
// Updates the size of the frame.
|
|
__update: function() {
|
|
var width = this.$container.width();
|
|
var height = this.$container.height();
|
|
|
|
// Floating windows manage their own sizing.
|
|
if (this._isFloating) {
|
|
var left = (this._pos.x * width) - this._size.x/2;
|
|
var top = (this._pos.y * height) - this._size.y/2;
|
|
|
|
if (top < 0) {
|
|
top = 0;
|
|
}
|
|
|
|
if (left + this._size.x/2 < 0) {
|
|
left = -this._size.x/2;
|
|
}
|
|
|
|
if (left + this._size.x/2 > width) {
|
|
left = width - this._size.x/2;
|
|
}
|
|
|
|
if (top + parseInt(this.$center.css('top')) > height) {
|
|
top = height - parseInt(this.$center.css('top'));
|
|
}
|
|
|
|
this.$frame.css('left', left + 'px');
|
|
this.$frame.css('top', top + 'px');
|
|
this.$frame.css('width', this._size.x + 'px');
|
|
this.$frame.css('height', this._size.y + 'px');
|
|
}
|
|
|
|
if (width !== this._lastSize.x || height !== this._lastSize.y) {
|
|
this._lastSize.x = width;
|
|
this._lastSize.y = height;
|
|
|
|
this._resizeData.time = new Date();
|
|
if (!this._resizeData.timeout) {
|
|
this._resizeData.timeout = true;
|
|
setTimeout(this.__resizeEnd.bind(this), this._resizeData.delta);
|
|
}
|
|
}
|
|
// this.__updateTabs();
|
|
this.__onTabChange();
|
|
},
|
|
|
|
__resizeEnd: function() {
|
|
this.__updateTabs();
|
|
if (new Date() - this._resizeData.time < this._resizeData.delta) {
|
|
setTimeout(this.__resizeEnd.bind(this), this._resizeData.delta);
|
|
} else {
|
|
this._resizeData.timeout = false;
|
|
}
|
|
},
|
|
|
|
// Triggers an event exclusively on the docker and none of its panels.
|
|
// Params:
|
|
// eventName The name of the event.
|
|
// data A custom data parameter to pass to all handlers.
|
|
__trigger: function(eventName, data) {
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
this._panelList[i].__trigger(eventName, data);
|
|
}
|
|
},
|
|
|
|
// Saves the current panel configuration into a meta
|
|
// object that can be used later to restore it.
|
|
__save: function() {
|
|
var data = {};
|
|
data.type = 'wcFrame';
|
|
data.floating = this._isFloating;
|
|
data.isFocus = this.$frame.hasClass('wcFloatingFocus')
|
|
data.pos = {
|
|
x: this._pos.x,
|
|
y: this._pos.y,
|
|
};
|
|
data.size = {
|
|
x: this._size.x,
|
|
y: this._size.y,
|
|
};
|
|
data.tab = this._curTab;
|
|
data.panels = [];
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
data.panels.push(this._panelList[i].__save());
|
|
}
|
|
return data;
|
|
},
|
|
|
|
// Restores a previously saved configuration.
|
|
__restore: function(data, docker) {
|
|
this._isFloating = data.floating;
|
|
this._pos.x = data.pos.x;
|
|
this._pos.y = data.pos.y;
|
|
this._size.x = data.size.x;
|
|
this._size.y = data.size.y;
|
|
this._curTab = data.tab;
|
|
for (var i = 0; i < data.panels.length; ++i) {
|
|
var panel = docker.__create(data.panels[i], this, this.$center);
|
|
panel.__restore(data.panels[i], docker);
|
|
this._panelList.push(panel);
|
|
}
|
|
|
|
this.__update();
|
|
|
|
if (data.isFocus) {
|
|
this.$frame.addClass('wcFloatingFocus');
|
|
}
|
|
},
|
|
|
|
__updateTabs: function(autoFocus) {
|
|
this.$tabScroll.empty();
|
|
|
|
// Move all tabbed panels to a temporary element to preserve event handlers on them.
|
|
// var $tempCenter = $('<div>');
|
|
// this.$frame.append($tempCenter);
|
|
// this.$center.children().appendTo($tempCenter);
|
|
|
|
var visibilityChanged = [];
|
|
var tabPositions = [];
|
|
var totalWidth = 0;
|
|
var parentLeft = this.$tabScroll.offset().left;
|
|
var self = this;
|
|
|
|
this.$title.removeClass('wcNotMoveable');
|
|
|
|
this.$center.children('.wcPanelTabContent').each(function() {
|
|
$(this).addClass('wcPanelTabContentHidden wcPanelTabUnused');
|
|
});
|
|
|
|
var titleVisible = true;
|
|
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
var panel = this._panelList[i];
|
|
|
|
var $tab = $('<div id="' + i + '" class="wcPanelTab">' + panel.title() + '</div>');
|
|
this.$tabScroll.append($tab);
|
|
if (panel.$icon) {
|
|
$tab.prepend(panel.$icon);
|
|
}
|
|
|
|
$tab.toggleClass('wcNotMoveable', !panel.moveable());
|
|
if (!panel.moveable()) {
|
|
this.$title.addClass('wcNotMoveable');
|
|
}
|
|
|
|
//
|
|
if (!panel._titleVisible) {
|
|
titleVisible = false;
|
|
}
|
|
|
|
var $tabContent = this.$center.children('.wcPanelTabContent[id="' + i + '"]');
|
|
if (!$tabContent.length) {
|
|
$tabContent = $('<div class="wcPanelTabContent wcPanelBackground wcPanelTabContentHidden" id="' + i + '">');
|
|
this.$center.append($tabContent);
|
|
}
|
|
|
|
panel.__container($tabContent);
|
|
panel._parent = this;
|
|
|
|
var isVisible = this._curTab === i;
|
|
if (panel.isVisible() !== isVisible) {
|
|
visibilityChanged.push({
|
|
panel: panel,
|
|
isVisible: isVisible,
|
|
});
|
|
}
|
|
|
|
$tabContent.removeClass('wcPanelTabUnused');
|
|
|
|
if (isVisible) {
|
|
$tab.addClass('wcPanelTabActive');
|
|
$tabContent.removeClass('wcPanelTabContentHidden');
|
|
}
|
|
|
|
totalWidth = $tab.offset().left - parentLeft;
|
|
tabPositions.push(totalWidth);
|
|
|
|
totalWidth += $tab.outerWidth();
|
|
}
|
|
|
|
if (titleVisible) {
|
|
this.$frame.prepend(this.$title);
|
|
if (!this.$frame.parent()) {
|
|
this.$center.css('top', '');
|
|
}
|
|
} else {
|
|
this.$title.remove();
|
|
this.$center.css('top', '0px');
|
|
}
|
|
|
|
// Now remove all unused panel tabs.
|
|
this.$center.children('.wcPanelTabUnused').each(function() {
|
|
$(this).remove();
|
|
});
|
|
|
|
// $tempCenter.remove();
|
|
if (titleVisible) {
|
|
var buttonSize = this.__onTabChange();
|
|
|
|
if (autoFocus) {
|
|
for (var i = 0; i < tabPositions.length; ++i) {
|
|
if (i === this._curTab) {
|
|
var left = tabPositions[i];
|
|
var right = totalWidth;
|
|
if (i+1 < tabPositions.length) {
|
|
right = tabPositions[i+1];
|
|
}
|
|
|
|
var scrollPos = -parseInt(this.$tabScroll.css('left'));
|
|
var titleWidth = this.$title.width() - buttonSize;
|
|
|
|
// If the tab is behind the current scroll position.
|
|
if (left < scrollPos) {
|
|
this._tabScrollPos = left - this.LEFT_TAB_BUFFER;
|
|
if (this._tabScrollPos < 0) {
|
|
this._tabScrollPos = 0;
|
|
}
|
|
}
|
|
// If the tab is beyond the current scroll position.
|
|
else if (right - scrollPos > titleWidth) {
|
|
this._tabScrollPos = right - titleWidth + this.LEFT_TAB_BUFFER;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
this._canScrollTabs = false;
|
|
if (totalWidth > this.$title.width() - buttonSize) {
|
|
this._canScrollTabs = titleVisible;
|
|
this.$frame.append(this.$tabRight);
|
|
this.$frame.append(this.$tabLeft);
|
|
var scrollLimit = totalWidth - (this.$title.width() - buttonSize)/2;
|
|
// If we are beyond our scroll limit, clamp it.
|
|
if (this._tabScrollPos > scrollLimit) {
|
|
var children = this.$tabScroll.children();
|
|
for (var i = 0; i < children.length; ++i) {
|
|
var $tab = $(children[i]);
|
|
|
|
totalWidth = $tab.offset().left - parentLeft;
|
|
if (totalWidth + $tab.outerWidth() > scrollLimit) {
|
|
this._tabScrollPos = totalWidth - this.LEFT_TAB_BUFFER;
|
|
if (this._tabScrollPos < 0) {
|
|
this._tabScrollPos = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
this._tabScrollPos = 0;
|
|
this.$tabLeft.remove();
|
|
this.$tabRight.remove();
|
|
}
|
|
|
|
this.$tabScroll.stop().animate({left: -this._tabScrollPos + 'px'}, 'fast');
|
|
|
|
// Update visibility on panels.
|
|
for (var i = 0; i < visibilityChanged.length; ++i) {
|
|
visibilityChanged[i].panel.__isVisible(visibilityChanged[i].isVisible);
|
|
}
|
|
}
|
|
},
|
|
|
|
__onTabChange: function() {
|
|
var buttonSize = 0;
|
|
var panel = this.panel();
|
|
if (panel) {
|
|
var scrollable = panel.scrollable();
|
|
this.$center.toggleClass('wcScrollableX', scrollable.x);
|
|
this.$center.toggleClass('wcScrollableY', scrollable.y);
|
|
|
|
var overflowVisible = panel.overflowVisible();
|
|
this.$center.toggleClass('wcOverflowVisible', overflowVisible);
|
|
|
|
this.$tabLeft.remove();
|
|
this.$tabRight.remove();
|
|
|
|
while (this._buttonList.length) {
|
|
this._buttonList.pop().remove();
|
|
}
|
|
|
|
if (panel.closeable()) {
|
|
this.$close.show();
|
|
buttonSize += this.$close.outerWidth();
|
|
} else {
|
|
this.$close.hide();
|
|
}
|
|
|
|
for (var i = 0; i < panel._buttonList.length; ++i) {
|
|
var buttonData = panel._buttonList[i];
|
|
var $button = $('<div>');
|
|
var buttonClass = buttonData.className;
|
|
$button.addClass('wcFrameButton');
|
|
if (buttonData.isTogglable) {
|
|
$button.addClass('wcFrameButtonToggler');
|
|
|
|
if (buttonData.isToggled) {
|
|
$button.addClass('wcFrameButtonToggled');
|
|
buttonClass = buttonData.toggleClassName || buttonClass;
|
|
}
|
|
}
|
|
$button.attr('title', buttonData.tip);
|
|
$button.data('name', buttonData.name);
|
|
$button.text(buttonData.text);
|
|
if (buttonClass) {
|
|
$button.prepend($('<div class="' + buttonClass + '">'));
|
|
}
|
|
|
|
this._buttonList.push($button);
|
|
this.$frame.append($button);
|
|
buttonSize += $button.outerWidth();
|
|
}
|
|
|
|
if (this._canScrollTabs) {
|
|
this.$frame.append(this.$tabRight);
|
|
this.$frame.append(this.$tabLeft);
|
|
|
|
buttonSize += this.$tabRight.outerWidth() + this.$tabLeft.outerWidth();
|
|
}
|
|
|
|
panel.__update();
|
|
|
|
this.$center.scrollLeft(panel._scroll.x);
|
|
this.$center.scrollTop(panel._scroll.y);
|
|
}
|
|
return buttonSize;
|
|
},
|
|
|
|
// Handles scroll notifications.
|
|
__scrolled: function() {
|
|
var panel = this.panel();
|
|
panel._scroll.x = this.$center.scrollLeft();
|
|
panel._scroll.y = this.$center.scrollTop();
|
|
|
|
panel.__trigger(wcDocker.EVENT_SCROLLED);
|
|
},
|
|
|
|
// Brings the frame into focus.
|
|
// Params:
|
|
// flash Optional, if true will flash the window.
|
|
__focus: function(flash) {
|
|
if (flash) {
|
|
var $flasher = $('<div class="wcFrameFlasher">');
|
|
this.$frame.append($flasher);
|
|
$flasher.animate({
|
|
opacity: 0.25,
|
|
},100)
|
|
.animate({
|
|
opacity: 0.0,
|
|
},100)
|
|
.animate({
|
|
opacity: 0.1,
|
|
},50)
|
|
.animate({
|
|
opacity: 0.0,
|
|
},50)
|
|
.queue(function(next) {
|
|
$flasher.remove();
|
|
next();
|
|
});
|
|
}
|
|
},
|
|
|
|
// Moves the panel based on mouse dragging.
|
|
// Params:
|
|
// mouse The current mouse position.
|
|
__move: function(mouse) {
|
|
var width = this.$container.width();
|
|
var height = this.$container.height();
|
|
|
|
this._pos.x = (mouse.x + this._anchorMouse.x) / width;
|
|
this._pos.y = (mouse.y + this._anchorMouse.y) / height;
|
|
},
|
|
|
|
// Sets the anchor position for moving the panel.
|
|
// Params:
|
|
// mouse The current mouse position.
|
|
__anchorMove: function(mouse) {
|
|
var width = this.$container.width();
|
|
var height = this.$container.height();
|
|
|
|
this._anchorMouse.x = (this._pos.x * width) - mouse.x;
|
|
this._anchorMouse.y = (this._pos.y * height) - mouse.y;
|
|
},
|
|
|
|
// Moves a tab from a given index to another index.
|
|
// Params:
|
|
// fromIndex The current tab index to move.
|
|
// toIndex The new index to move to.
|
|
// Returns:
|
|
// element The new element of the moved tab.
|
|
// false If an error occurred.
|
|
__tabMove: function(fromIndex, toIndex) {
|
|
if (fromIndex >= 0 && fromIndex < this._panelList.length &&
|
|
toIndex >= 0 && toIndex < this._panelList.length) {
|
|
var panel = this._panelList.splice(fromIndex, 1);
|
|
this._panelList.splice(toIndex, 0, panel[0]);
|
|
|
|
// Preserve the currently active tab.
|
|
if (this._curTab === fromIndex) {
|
|
this._curTab = toIndex;
|
|
}
|
|
|
|
this.__updateTabs();
|
|
|
|
return this.$title.find('> .wcTabScroller > .wcPanelTab[id="' + toIndex + '"]')[0];
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// Checks if the mouse is in a valid anchor position for docking a panel.
|
|
// Params:
|
|
// mouse The current mouse position.
|
|
// same Whether the moving frame and this one are the same.
|
|
__checkAnchorDrop: function(mouse, same, ghost, canSplit) {
|
|
var panel = this.panel();
|
|
if (panel && panel.moveable()) {
|
|
return panel.layout().__checkAnchorDrop(mouse, same, ghost, (!this._isFloating && canSplit), this.$frame, panel.moveable() && panel.title());
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// Resizes the panel based on mouse dragging.
|
|
// Params:
|
|
// edges A list of edges being moved.
|
|
// mouse The current mouse position.
|
|
__resize: function(edges, mouse) {
|
|
var width = this.$container.width();
|
|
var height = this.$container.height();
|
|
var offset = this.$container.offset();
|
|
|
|
mouse.x -= offset.left;
|
|
mouse.y -= offset.top;
|
|
|
|
var minSize = this.minSize();
|
|
var maxSize = this.maxSize();
|
|
|
|
var pos = {
|
|
x: (this._pos.x * width) - this._size.x/2,
|
|
y: (this._pos.y * height) - this._size.y/2,
|
|
};
|
|
|
|
for (var i = 0; i < edges.length; ++i) {
|
|
switch (edges[i]) {
|
|
case 'top':
|
|
this._size.y += pos.y - mouse.y-2;
|
|
pos.y = mouse.y+2;
|
|
if (this._size.y < minSize.y) {
|
|
pos.y += this._size.y - minSize.y;
|
|
this._size.y = minSize.y;
|
|
}
|
|
if (this._size.y > maxSize.y) {
|
|
pos.y += this._size.y - maxSize.y;
|
|
this._size.y = maxSize.y;
|
|
}
|
|
break;
|
|
case 'bottom':
|
|
this._size.y = mouse.y-4 - pos.y;
|
|
if (this._size.y < minSize.y) {
|
|
this._size.y = minSize.y;
|
|
}
|
|
if (this._size.y > maxSize.y) {
|
|
this._size.y = maxSize.y;
|
|
}
|
|
break;
|
|
case 'left':
|
|
this._size.x += pos.x - mouse.x-2;
|
|
pos.x = mouse.x+2;
|
|
if (this._size.x < minSize.x) {
|
|
pos.x += this._size.x - minSize.x;
|
|
this._size.x = minSize.x;
|
|
}
|
|
if (this._size.x > maxSize.x) {
|
|
pos.x += this._size.x - maxSize.x;
|
|
this._size.x = maxSize.x;
|
|
}
|
|
break;
|
|
case 'right':
|
|
this._size.x = mouse.x-4 - pos.x;
|
|
if (this._size.x < minSize.x) {
|
|
this._size.x = minSize.x;
|
|
}
|
|
if (this._size.x > maxSize.x) {
|
|
this._size.x = maxSize.x;
|
|
}
|
|
break;
|
|
}
|
|
|
|
this._pos.x = (pos.x + this._size.x/2) / width;
|
|
this._pos.y = (pos.y + this._size.y/2) / height;
|
|
}
|
|
},
|
|
|
|
// Turn off or on a shadowing effect to signify this widget is being moved.
|
|
// Params:
|
|
// enabled Whether to enable __shadow mode.
|
|
__shadow: function(enabled) {
|
|
if (enabled) {
|
|
if (!this.$shadower) {
|
|
this.$shadower = $('<div class="wcFrameShadower">');
|
|
this.$frame.append(this.$shadower);
|
|
this.$shadower.animate({
|
|
opacity: 0.5,
|
|
}, 300);
|
|
}
|
|
} else {
|
|
if (this.$shadower) {
|
|
var self = this;
|
|
this.$shadower.animate({
|
|
opacity: 0.0,
|
|
}, 300)
|
|
.queue(function(next) {
|
|
self.$shadower.remove();
|
|
self.$shadower = null;
|
|
next();
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
// Retrieves the bounding rect for this frame.
|
|
__rect: function() {
|
|
var offset = this.$frame.offset();
|
|
var width = this.$frame.width();
|
|
var height = this.$frame.height();
|
|
|
|
return {
|
|
x: offset.left,
|
|
y: offset.top,
|
|
w: width,
|
|
h: height,
|
|
};
|
|
},
|
|
|
|
// Gets, or Sets a new container for this layout.
|
|
// Params:
|
|
// $container If supplied, sets a new container for this layout.
|
|
// parent If supplied, sets a new parent for this layout.
|
|
// Returns:
|
|
// JQuery collection The current container.
|
|
__container: function($container) {
|
|
if (typeof $container === 'undefined') {
|
|
return this.$container;
|
|
}
|
|
|
|
this.$container = $container;
|
|
if (this.$container) {
|
|
this.$container.append(this.$frame);
|
|
} else {
|
|
this.$frame.remove();
|
|
}
|
|
return this.$container;
|
|
},
|
|
|
|
// Disconnects and prepares this widget for destruction.
|
|
__destroy: function() {
|
|
this._curTab = -1;
|
|
for (var i = 0; i < this._panelList.length; ++i) {
|
|
this._panelList[i].__destroy();
|
|
}
|
|
|
|
while (this._panelList.length) this._panelList.pop();
|
|
if (this.$modalBlocker) {
|
|
this.$modalBlocker.remove();
|
|
this.$modalBlocker = null;
|
|
}
|
|
this.__container(null);
|
|
this._parent = null;
|
|
},
|
|
}; |