// vim: et:ts=4:sw=4:sts=4:ft=js /*@ Ox.SplitPanel SpliPanel Object () -> SpliPanel Object (options) -> SpliPanel Object (options, self) -> SpliPanel Object options Options object self shared private variable resize resize toggle toggle @*/ /** options: elements: [{ array of one, two or three elements collapsible: false, collapsible or not (only for outer elements) collapsed: false, collapsed or not (only for collapsible elements) element: {}, OxElement (if any element is resizable or collapsible, all OxElements must have an id) resizable: false, resizable or not (only for outer elements) resize: [], array of sizes (only for resizable elements, first value is min, last value is max, other values are 'snappy' points in between) size: 0 size in px (one element must have no size) }], orientation: '' 'horizontal' or 'vertical' events: resize toggle */ Ox.SplitPanel = function(options, self) { var self = self || {}, that = new Ox.Element({}, self) // fixme: Container .defaults({ elements: [], orientation: 'horizontal' }) .options(options || {}) .addClass('OxSplitPanel'); $.extend(self, { dimensions: Ox.UI.DIMENSIONS[self.options.orientation], edges: Ox.UI.EDGES[self.options.orientation], length: self.options.elements.length, resizebarElements: [], $resizebars: [] }); // create elements that.$elements = []; self.options.elements.forEach(function(v, i) { self.options.elements[i] = $.extend({ collapsible: false, collapsed: false, resizable: false, resize: [], size: 'auto' }, v); that.$elements[i] = v.element .css(self.edges[2], (parseInt(v.element.css(self.edges[2])) || 0) + 'px') .css(self.edges[3], (parseInt(v.element.css(self.edges[3])) || 0) + 'px'); //alert(v.element.css(self.edges[3])) }); // create resizebars self.options.elements.forEach(function(v, i) { //that.append(element) //Ox.print('V: ', v, that.$elements[i]) var index = i == 0 ? 0 : 1; that.$elements[i].appendTo(that.$element); // fixme: that.$content if (v.collapsible || v.resizable) { //Ox.print('v.size', v.size) self.resizebarElements[index] = i < 2 ? [0, 1] : [1, 2]; self.$resizebars[index] = new Ox.Resizebar({ collapsible: v.collapsible, edge: self.edges[index], elements: [ that.$elements[self.resizebarElements[index][0]], that.$elements[self.resizebarElements[index][1]] ], id: v.element.options('id'), orientation: self.options.orientation == 'horizontal' ? 'vertical' : 'horizontal', parent: that, // fixme: that.$content resizable: v.resizable, resize: v.resize, size: v.size }); self.$resizebars[index][i == 0 ? 'insertAfter' : 'insertBefore'](that.$elements[i]); } }); self.options.elements.forEach(function(v, i) { v.collapsed && that.css( self.edges[i == 0 ? 0 : 1], -self.options.elements[i].size + 'px' ); }); setSizes(true); function getPositionById(id) { var position = -1; Ox.forEach(self.options.elements, function(element, i) { if (element.element.options('id') == id) { position = i; return false; } }); //Ox.print('getPositionById', id, position); return position; } function getSize(element) { return element.size + (element.collapsible || element.resizable); //return (element.size + (element.collapsible || element.resizable)) * !element.collapsed; } function getVisibleSize(element) { return getSize(element) * !element.collapsed; } function setSizes(init) { self.options.elements.forEach(function(v, i) { // fixme: maybe we can add a conditional here, since init // is about elements that are collapsed splitpanels var edges = [ (init && parseInt(that.$elements[i].css(self.edges[0]))) || 0, (init && parseInt(that.$elements[i].css(self.edges[1]))) || 0 ]; v.size != 'auto' && that.$elements[i].css(self.dimensions[0], v.size + 'px'); if (i == 0) { that.$elements[i].css( self.edges[0], edges[0] + 'px' ); that.$elements[i].css( self.edges[1], (getSize(self.options.elements[1]) + (length == 3 ? getSize(self.options.elements[2]) : 0)) + 'px' ); } else if (i == 1) { that.$elements[i].css( self.edges[0], self.options.elements[0].size == 'auto' ? 'auto' : edges[0] + getSize(self.options.elements[0]) + 'px' ); (self.options.elements[0].size != 'auto' || v.size != 'auto') && that.$elements[i].css( self.edges[1], (self.length == 3 ? getSize(self.options.elements[2]) : 0) + 'px' ); } else { that.$elements[i].css( self.edges[0], (self.options.elements[1].size == 'auto' || v.size == 'auto') ? 'auto' : (getVisibleSize(self.options.elements[0]) + getVisibleSize(self.options.elements[1])) + 'px' ); that.$elements[i].css( self.edges[1], edges[1] + 'px' ); } if (v.collapsible || v.resizable) { self.$resizebars[i == 0 ? 0 : 1].css(self.edges[i == 0 ? 0 : 1], v.size); } }); } that.isCollapsed = function(id) { var pos = Ox.isNumber(id) ? id : getPositionById(id); return self.options.elements[pos].collapsed; }; that.replaceElement = function(id, element) { // one can pass pos instead of id var pos = Ox.isNumber(id) ? id : getPositionById(id); //Ox.print('replace', pos, element); //Ox.print('element', self.options.elements[pos].element, element) that.$elements[pos] = element .css(self.edges[2], (parseInt(element.css(self.edges[2])) || 0) + 'px') .css(self.edges[3], (parseInt(element.css(self.edges[3])) || 0) + 'px'); //alert(element.css(self.edges[3])) self.options.elements[pos].element.replaceWith(element.$element.$element || element.$element); self.options.elements[pos].element = element; setSizes(); self.$resizebars.forEach(function($resizebar, i) { $resizebar.options({ elements: [ that.$elements[self.resizebarElements[i][0]], that.$elements[self.resizebarElements[i][1]] ] }); }); //Ox.print(self.options.elements[pos]) return that; }; that.replaceElements = function(elements) { elements.forEach(function(element, i) { if (Ox.isNumber(element.size)) { that.size(i, element.size); if (element.collapsible || element.resizable) { self.$resizebars[i == 0 ? 0 : 1].options({ collapsible: element.collapsible, resizable: element.resizable, size: element.size }); } } 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; } that.size = function(id, size) { // one can pass pos instead of id var pos = Ox.isNumber(id) ? id : getPositionById(id), element = self.options.elements[pos]; if (arguments.length == 1) { return element.element[self.dimensions[0]]() * !that.isCollapsed(pos); } else { element.size = size; setSizes(); return that; } }; that.toggle = function(id) { // one can pass pos instead of id var pos = Ox.isNumber(id) ? id : getPositionById(id), element = self.options.elements[pos], value = parseInt(that.css(self.edges[pos == 0 ? 0 : 1])) + element.element[self.dimensions[0]]() * (element.collapsed ? 1 : -1), animate = {}; animate[self.edges[pos == 0 ? 0 : 1]] = value; that.animate(animate, 200, function() { // fixme: 250? element.collapsed = !element.collapsed; element.element.triggerEvent('toggle', { 'collapsed': element.collapsed }); element = self.options.elements[pos == 0 ? 1 : pos - 1]; element.element.triggerEvent( 'resize', element.element[self.dimensions[0]]() ); }); }; that.updateSize = function(pos, size) { // this is called from resizebar var pos = pos == 0 ? 0 : self.options.elements.length - 1; // fixme: silly that 0 or 1 is passed, and not pos self.options.elements[pos].size = size; } return that; }; Ox.SplitPanel_ = function(options, self) { var self = self || {}, that = new Ox.Element({}, self) .defaults({ elements: [], orientation: 'horizontal' }) .options(options) .addClass( 'OxSplitPanel_ Ox' + Ox.toTitleCase(self.options.orientation) ); Ox.extend(self, { $separators: [], clientXY: self.options.orientation == 'horizontal' ? 'clientX' : 'clientY', dimensions: Ox.UI.DIMENSIONS[self.options.orientation], edges: Ox.UI.EDGES[self.options.orientation] }); self.options.elements.forEach(function(element, i) { self.options.elements[i] = Ox.extend({ collapsible: false, collapsed: false, resizable: false, resize: [], size: 'auto' }, element); }); self.autoPercent = (100 - self.options.elements.reduce(function(val, element) { return val + (Ox.endsWith(element.size, '%') ? parseFloat(element.size) : 0); }, 0)) / self.options.elements.filter(function(element) { return element.size == 'auto'; }).length + '%'; self.options.elements.forEach(function(element, i) { var flex, index = i == 0 ? 0 : 1; if (Ox.isNumber(element.size)) { element.element.css(self.dimensions[0], element.size + 'px'); } else { flex = ( element.size == 'auto' ? self.autoPercent : element.size ).replace('%', ''); element.element.css({ boxFlex: flex, MozBoxFlex: flex, WebkitBoxFlex: flex }); } element.element.appendTo(that); if (element.collapsible || element.resizable) { self.$separators.push( Ox.Element() .addClass('OxSeparator') .bindEvent({ anyclick: function() { that.toggle(i); }, dragstart: function(event, e) { dragstart(i, e); }, drag: function(event, e) { drag(i, e); }, dragend: function(event, e) { dragend(i, e); }, }) .append($('
').addClass('OxSpace')) .append($('
').addClass('OxLine')) .append($('
').addClass('OxSpace')) ['insert' + (index ? 'Before' : 'After')](element.element) ); } }); function dragstart(pos, e) { var element = self.options.elements[pos], size = element.element[self.dimensions[0]](); if (element.resizable && !element.collapsed) { self.drag = { size: size, startPos: e[self.clientXY], startSize: size }; Ox.print('self.drag', self.drag) } } function drag(pos, e) { var data = {}, element = self.options.elements[pos], index = pos == 0 ? 0 : 1; if (element.resizable && !element.collapsed) { var d = e[self.clientXY] - self.drag.startPos, size = Ox.limit( self.drag.startSize + d * (index ? -1 : 1), element.resize[0], element.resize[element.resize.length - 1] ); Ox.forEach(element.resize, function(v) { if (size >= v - 8 && size <= v + 8) { size = v; return false; } }); if (size != self.drag.size) { self.drag.size = size; data[self.dimensions[0]] = size; element.element .css(self.dimensions[0], size + 'px') .triggerEvent('resize', data); triggerEvents('resize', pos); } } } function dragend(pos, e) { var data = {}, element = self.options.elements[pos]; if (element.resizable && !element.collapsed) { data[self.dimensions[0]] = self.drag.size element.element.triggerEvent('resizeend', data); triggerEvents('resizeend', pos); } } function triggerEvents(event, pos) { var data = {}; self.options.elements.forEach(function(element, i) { if (i != pos && element.size == 'auto') { data[self.dimensions[0]] = element.element[self.dimensions[0]](); element.element.triggerEvent(event, data); } }); } that.replaceElement = function(pos, element) { var $element = self.options.elements[pos].element, size = self.options.elements[pos].size; $element.replaceWith(self.options.elements[pos].element = element); if (size == 'auto') { $element.css(self.boxFlexCSS); } else { $element.css(self.dimensions[0], size + 'px') } return that; }; that.size = function(pos, size) { var element = self.options.elements[pos], ret; if (Ox.isUndefined(size)) { ret = element.element[self.dimensions[0]](); } else { element.size = size; element.element.css(self.dimensions[0], size + 'px') ret = that; } return that; } that.toggle = function(pos) { var css = {}, element = self.options.elements[pos], flex, index = pos == 0 ? 0 : 1, size = element.element[self.dimensions[0]](); if (element.collapsible) { element.collapsed = !element.collapsed; css['margin' + Ox.toTitleCase(self.edges[0][index])] = element.collapsed ? -size : 0; Ox.print('css', css); that.animate(css, 250, function() { element.element.triggerEvent('toggle', {collapsed: element.collapsed}); triggerEvents('resize', pos); }); } } return that; };