oxjs/source/Ox.UI/js/Panel/SplitPanel.js

364 lines
14 KiB
JavaScript
Raw Normal View History

2011-11-05 16:46:53 +00:00
'use strict';
2012-05-21 10:38:18 +00:00
2011-05-16 08:24:46 +00:00
/*@
2012-05-31 10:32:54 +00:00
Ox.SplitPanel <f> SpliPanel Object
2011-05-16 08:24:46 +00:00
options <o> Options object
elements <[o]|[]> Array of two or three element objects
collapsible <b|false> If true, can be collapsed (if outer element)
collapsed <b|false> If true, is collapsed (if collapsible)
2013-12-06 16:26:35 +00:00
defaultSize <n|s|auto> Default size in px (restorable via reset)
element <o> Any Ox.Element
If any element is collapsible or resizable, all elements must
have an id.
resizable <b|false> If true, can be resized (if outer element)
resize <[n]|[]> Min size, optional snappy points, and max size
size <n|s|"auto"> Size in px (one element must be "auto")
tooltip <b|s|false> If true, show tooltip, if string, append it
orientation <s|"horizontal"> orientation ("horizontal" or "vertical")
self <o> Shared private variable
([options[, self]]) -> <o:Ox.Element> SpliPanel Object
resize <!> resize
Fires on resize, on both elements being resized
toggle <!> toggle
Fires on collapse or expand, on the element being toggled
2011-05-16 08:24:46 +00:00
@*/
2011-04-22 22:03:10 +00:00
Ox.SplitPanel = function(options, self) {
2011-10-16 12:32:02 +00:00
// fixme: doubleclick on resizebar should reset to initial size
2013-12-06 16:26:35 +00:00
// (but anyclick would become singleclick, i.e. less responsive)
2011-10-16 12:32:02 +00:00
self = self || {};
2013-12-06 16:26:35 +00:00
var that = Ox.Element({}, self)
2011-04-22 22:03:10 +00:00
.defaults({
elements: [],
orientation: 'horizontal'
})
.options(options || {})
.addClass('OxSplitPanel');
Ox.extend(self, {
2011-04-22 22:03:10 +00:00
dimensions: Ox.UI.DIMENSIONS[self.options.orientation],
edges: Ox.UI.EDGES[self.options.orientation],
defaultSize: self.options.elements.map(function(element) {
return !Ox.isUndefined(element.defaultSize)
? element.defaultSize : element.size;
}),
2011-04-22 22:03:10 +00:00
length: self.options.elements.length,
resizebarElements: [],
$resizebars: []
});
// create elements
2013-12-06 16:26:35 +00:00
// fixme: attach to self instead?
that.$elements = Ox.map(self.options.elements, function(element, i) {
self.options.elements[i] = Ox.extend({
2011-04-22 22:03:10 +00:00
collapsible: false,
collapsed: false,
resizable: false,
resize: [],
size: 'auto'
}, element);
2013-12-06 16:26:35 +00:00
// top and bottom (horizontal) or left and right (vertical)
self.edges.slice(2).forEach(function(edge) {
2014-01-17 11:23:22 +00:00
element.element.css(edge, (parseInt(element.element.css(edge)) || 0) + 'px');
2013-12-06 16:26:35 +00:00
});
return element.element;
2011-04-22 22:03:10 +00:00
});
// create resizebars
self.options.elements.forEach(function(element, i) {
2011-04-22 22:03:10 +00:00
var index = i == 0 ? 0 : 1;
that.$elements[i].appendTo(that.$element); // fixme: that.$content
if (element.collapsible || element.resizable) {
2011-04-22 22:03:10 +00:00
self.resizebarElements[index] = i < 2 ? [0, 1] : [1, 2];
self.$resizebars[index] = Ox.Resizebar({
collapsed: element.collapsed,
collapsible: element.collapsible,
2011-04-22 22:03:10 +00:00
edge: self.edges[index],
elements: [
that.$elements[self.resizebarElements[index][0]],
that.$elements[self.resizebarElements[index][1]]
],
id: element.element.options('id'),
2013-12-06 16:26:35 +00:00
orientation: self.options.orientation == 'horizontal'
? 'vertical' : 'horizontal',
2011-04-22 22:03:10 +00:00
parent: that, // fixme: that.$content
resizable: element.resizable,
resize: element.resize,
size: element.size,
tooltip: element.tooltip
2011-04-22 22:03:10 +00:00
});
2013-12-06 16:26:35 +00:00
self.$resizebars[index][
i == 0 ? 'insertAfter' : 'insertBefore'
](that.$elements[i]);
2011-04-22 22:03:10 +00:00
}
});
self.options.elements.forEach(function(element, i) {
element.collapsed && that.css(
2014-01-17 11:17:27 +00:00
// left/right (horizontal) or top/bottom (vertical)
self.edges[i == 0 ? 0 : 1], -element.size + 'px'
2011-04-22 22:03:10 +00:00
);
});
setSizes(true);
2013-12-06 16:26:35 +00:00
function getIndexById(id) {
var index = -1;
2011-04-22 22:03:10 +00:00
Ox.forEach(self.options.elements, function(element, i) {
if (element.element.options('id') == id) {
2013-12-06 16:26:35 +00:00
index = i;
2012-07-05 08:58:08 +00:00
return false; // break
2011-04-22 22:03:10 +00:00
}
});
2013-12-06 16:26:35 +00:00
return index;
2011-04-22 22:03:10 +00:00
}
2013-12-06 16:26:35 +00:00
function getSize(index) {
var element = self.options.elements[index];
2011-04-22 22:03:10 +00:00
return element.size + (element.collapsible || element.resizable);
}
2013-12-06 16:26:35 +00:00
function getVisibleSize(index) {
var element = self.options.elements[index];
2014-01-17 11:17:27 +00:00
return getSize(index) * !element.collapsed;
2011-04-22 22:03:10 +00:00
}
function setSizes(init, animate) {
2012-05-27 19:32:39 +00:00
// will animate if animate is truthy and call animate if it's a function
self.options.elements.forEach(function(element, i) {
2011-04-22 22:03:10 +00:00
// fixme: maybe we can add a conditional here, since init
// is about elements that are collapsed splitpanels
var css = {},
2013-12-06 16:26:35 +00:00
edges = self.edges.slice(0, 2).map(function(edge) {
// left/right (horizontal) or top/bottom (vertical)
return init && parseInt(that.$elements[i].css(edge)) || 0;
});
if (element.size != 'auto') {
2013-12-06 16:26:35 +00:00
// width (horizontal) or height (vertical)
css[self.dimensions[0]] = element.size + 'px';
}
2011-04-22 22:03:10 +00:00
if (i == 0) {
2013-12-06 16:26:35 +00:00
// left (horizontal) or top (vertical)
css[self.edges[0]] = edges[0] + 'px';
2013-12-06 16:26:35 +00:00
// right (horizontal) or bottom (vertical)
if (element.size == 'auto') {
2013-12-06 16:26:35 +00:00
css[self.edges[1]] = getSize(1)
+ (
2013-12-06 16:26:35 +00:00
self.length == 3 ? getVisibleSize(2) : 0
) + 'px';
}
2011-04-22 22:03:10 +00:00
} else if (i == 1) {
2013-12-06 16:26:35 +00:00
// left (horizontal) or top (vertical)
if (self.options.elements[0].size != 'auto') {
2013-12-06 16:26:35 +00:00
css[self.edges[0]] = edges[0] + getSize(0) + 'px'
} else {
css[self.edges[0]] = 'auto'; // fixme: why is this needed?
}
2013-12-06 16:26:35 +00:00
// right (horizontal) or bottom (vertical)
css[self.edges[1]] = (
2013-12-06 16:26:35 +00:00
self.length == 3 ? getSize(2) : 0
) + 'px';
} else {
2013-12-06 16:26:35 +00:00
// left (horizontal) or top (vertical)
if (element.size == 'auto') {
2014-01-17 11:17:27 +00:00
css[self.edges[0]] = getVisibleSize(0) + getSize(1) + 'px';
} else {
css[self.edges[0]] = 'auto'; // fixme: why is this needed?
}
2013-12-06 16:26:35 +00:00
// right (horizontal) or bottom (vertical)
css[self.edges[1]] = edges[1] + 'px';
}
if (animate) {
that.$elements[i].animate(css, 250, function() {
i == 0 && Ox.isFunction(animate) && animate();
});
2011-04-22 22:03:10 +00:00
} else {
that.$elements[i].css(css);
2011-04-22 22:03:10 +00:00
}
if (element.collapsible || element.resizable) {
css = {};
2013-12-06 16:26:35 +00:00
// left or right (horizontal) or top or bottom (vertical)
css[self.edges[i == 0 ? 0 : 1]] = element.size + 'px'
if (animate) {
self.$resizebars[i == 0 ? 0 : 1].animate(css, 250);
} else {
self.$resizebars[i == 0 ? 0 : 1].css(css);
}
self.$resizebars[i == 0 ? 0 : 1].options({size: element.size});
2011-04-22 22:03:10 +00:00
}
});
}
/*@
getSize <f> get size of panel
2013-12-06 16:26:35 +00:00
(id) -> <i> id or index of element, returns size
id <s|i> The element's id or index
@*/
// fixme: what is this? there is that.size()
that.getSize = function(id) {
2013-12-06 16:26:35 +00:00
var index = Ox.isNumber(id) ? id : getIndexById(id),
element = self.options.elements[index];
return element.element[self.dimensions[0]]() * !that.isCollapsed(index);
};
2011-09-05 12:42:37 +00:00
/*@
2011-11-03 15:42:41 +00:00
isCollapsed <f> panel collapsed state
2013-12-06 16:26:35 +00:00
(id) -> <b> id or index of element, returns collapsed state
id <i> The element's id or index
2011-09-05 12:42:37 +00:00
@*/
2011-04-22 22:03:10 +00:00
that.isCollapsed = function(id) {
2013-12-06 16:26:35 +00:00
var index = Ox.isNumber(id) ? id : getIndexById(id);
return self.options.elements[index].collapsed;
2011-04-22 22:03:10 +00:00
};
2011-09-05 12:42:37 +00:00
/*@
2012-04-07 10:43:41 +00:00
replaceElement <f> Replace panel element
2011-09-05 12:42:37 +00:00
(id, element) -> <f> replace element
2013-12-06 16:26:35 +00:00
id <s|n> The element's id or index
element <o> New element
2011-09-05 12:42:37 +00:00
@*/
2011-04-22 22:03:10 +00:00
that.replaceElement = function(id, element) {
2013-12-06 16:26:35 +00:00
// one can pass index instead of id
var index = Ox.isNumber(id) ? id : getIndexById(id);
// top and bottom (horizontal) or left and right (vertical)
self.edges.slice(2).forEach(function(edge) {
2014-01-17 11:23:22 +00:00
element.css(edge, (parseInt(element.css(edge)) || 0) + 'px');
2013-12-06 16:26:35 +00:00
});
that.$elements[index] = element;
self.options.elements[index].element.replaceWith(
self.options.elements[index].element = element
);
2011-04-22 22:03:10 +00:00
setSizes();
self.$resizebars.forEach(function($resizebar, i) {
$resizebar.options({
elements: [
that.$elements[self.resizebarElements[i][0]],
that.$elements[self.resizebarElements[i][1]]
]
});
});
return that;
};
2011-09-05 12:42:37 +00:00
/*@
replaceElements <f> replace panel elements
2011-09-05 12:42:37 +00:00
(elements) -> <f> replace elements
elements <a> array of new elements
@*/
2011-04-22 22:03:10 +00:00
that.replaceElements = function(elements) {
elements.forEach(function(element, i) {
if (Ox.isNumber(element.size)) {
that.size(i, element.size);
if (element.collapsible || element.resizable) {
2012-08-29 15:41:40 +00:00
self.$resizebars[i == 0 ? 0 : 1].options({
2011-04-22 22:03:10 +00:00
collapsible: element.collapsible,
resizable: element.resizable,
2012-08-29 15:41:40 +00:00
size: element.size
});
}
2011-04-22 22:03:10 +00:00
}
that.replace(i, element.element);
});
self.options.elements = elements;
self.$resizebars.forEach(function($resizebar, i) {
$resizebar.options({
elements: [
that.$elements[self.resizebarElements[i][0]],
that.$elements[self.resizebarElements[i][1]]
]
});
});
return that;
};
2011-04-22 22:03:10 +00:00
/*@
reset <f> Reset an outer element to its initial size
@*/
that.reset = function(id) {
2013-12-06 16:26:35 +00:00
// one can pass index instead of id
var index = Ox.isNumber(id) ? id : getIndexById(id),
element = self.options.elements[index];
element.size = self.defaultSize[index];
2012-08-29 15:41:40 +00:00
setSizes(false, function() {
element.element.triggerEvent('resize', {
size: element.size
});
2013-12-06 16:26:35 +00:00
element = self.options.elements[index == 0 ? 1 : index - 1];
element.element.triggerEvent('resize', {
2012-08-29 15:41:40 +00:00
size: element.element[self.dimensions[0]]()
});
});
};
2011-09-05 12:42:37 +00:00
/*@
size <f> Get or set size of an element
(id) -> <i> Returns size
(id, size) -> <o> Sets size, returns SplitPanel
(id, size, callback) -> <o> Sets size with animation, returns SplitPanel
2013-12-06 16:26:35 +00:00
id <i> The element's id or index
size <i> New size, in px
callback <b|f> Callback function (passing true animates w/o callback)
2011-09-05 12:42:37 +00:00
@*/
that.size = function(id, size, callback) {
2013-12-06 16:26:35 +00:00
// one can pass index instead of id
var index = Ox.isNumber(id) ? id : getIndexById(id),
2013-12-09 01:28:42 +00:00
element = self.options.elements[index];
2012-08-29 15:41:40 +00:00
if (arguments.length == 1) {
2013-12-06 16:26:35 +00:00
return element.element[self.dimensions[0]]() * !that.isCollapsed(index);
2012-08-29 15:41:40 +00:00
} else {
element.size = size;
setSizes(false, callback);
2011-04-22 22:03:10 +00:00
return that;
2012-08-29 15:41:40 +00:00
}
2011-04-22 22:03:10 +00:00
};
2011-09-05 12:42:37 +00:00
/*@
toggle <f> Toggles collapsed state of an outer element
(id) -> <o> The SplitPanel
2013-12-06 16:26:35 +00:00
id <s|i> The element's id or index
2011-09-05 12:42:37 +00:00
@*/
2013-12-09 01:28:42 +00:00
// FIXME: 'toggle' is reserved by jQuery
2011-04-22 22:03:10 +00:00
that.toggle = function(id) {
2013-12-06 16:26:35 +00:00
// one can pass index instead of id
if (self.toggling) {
return false;
}
2013-12-06 16:26:35 +00:00
var index = Ox.isNumber(id) ? id : getIndexById(id),
element = self.options.elements[index],
value = parseInt(that.css(self.edges[index == 0 ? 0 : 1]), 10)
+ element.element[self.dimensions[0]]() * (element.collapsed ? 1 : -1),
2011-04-22 22:03:10 +00:00
animate = {};
self.toggling = true;
2013-12-06 16:26:35 +00:00
animate[self.edges[index == 0 ? 0 : 1]] = value;
that.animate(animate, 250, function() {
2011-04-22 22:03:10 +00:00
element.collapsed = !element.collapsed;
element.element.triggerEvent('toggle', {
collapsed: element.collapsed
2011-04-22 22:03:10 +00:00
});
2013-12-06 16:26:35 +00:00
self.$resizebars[index == 0 ? 0 : 1].options({collapsed: element.collapsed});
element = self.options.elements[index == 0 ? 1 : index - 1];
element.element.triggerEvent('resize', {
2012-08-29 15:41:40 +00:00
size: element.element[self.dimensions[0]]()
});
self.toggling = false;
2011-04-22 22:03:10 +00:00
});
};
2011-09-05 12:42:37 +00:00
/*@
updateSize <f> update size of element
2013-12-06 16:26:35 +00:00
(index, size) -> <o> update size of element
index <i> index of element
2011-09-05 12:42:37 +00:00
size <n> new size
@*/
2013-12-06 16:26:35 +00:00
that.updateSize = function(index, size) {
2011-04-22 22:03:10 +00:00
// this is called from resizebar
2013-12-06 16:26:35 +00:00
index = index == 0 ? 0 : self.options.elements.length - 1; // fixme: silly that 0 or 1 is passed, and not index
self.options.elements[index].size = size;
};
2011-04-22 22:03:10 +00:00
return that;
2012-05-21 10:38:18 +00:00
};