Files
iTop/js/splitter.js
Denis Flaven ec48618573 Initial import on SourceForge
SVN:code[2]
2009-03-13 17:13:48 +00:00

181 lines
7.7 KiB
JavaScript

/*
* jquery.splitter.js - two-pane splitter window plugin
*
* version 1.01 (01/05/2007)
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
/**
* The splitter() plugin implements a two-pane resizable splitter window.
* The selected elements in the jQuery object are converted to a splitter;
* each element should have two child elements which are used for the panes
* of the splitter. The plugin adds a third child element for the splitbar.
*
* For more details see: http://methvin.com/jquery/splitter/
*
*
* @example $('#MySplitter').splitter();
* @desc Create a vertical splitter with default settings
*
* @example $('#MySplitter').splitter({direction: 'h', accessKey: 'M'});
* @desc Create a horizontal splitter resizable via Alt+Shift+M
*
* @name splitter
* @type jQuery
* @param String options Options for the splitter
* @cat Plugins/Splitter
* @return jQuery
* @author Dave Methvin (dave.methvin@gmail.com)
*/
jQuery.fn.splitter = function(opts){
opts = jQuery.extend({
type: 'v', // v=vertical, h=horizontal split
activeClass: 'active', // class name for active splitter
pxPerKey: 5, // splitter px moved per keypress
tabIndex: 0, // tab order indicator
accessKey: '' // accelerator key for splitter
// initA initB // initial A/B size (pick ONE)
// minA maxA minB maxB // min/max pane sizes
},{
v: { // Vertical splitters:
keyGrowA: 39, // left arrow key
keyShrinkA: 37, // right arrow key
cursor: "e-resize", // double-arrow horizontal
splitbarClass: "vsplitbar",
eventPos: "pageX", set: "left",
adjust: "width", offsetAdjust: "offsetWidth", adjSide1: "Left", adjSide2: "Right",
fixed: "height", offsetFixed: "offsetHeight", fixSide1: "Top", fixSide2: "Bottom"
},
h: { // Horizontal splitters:
keyGrowA: 40, // down arrow key
keyShrinkA: 38, // up arrow key
cursor: "n-resize", // double-arrow vertical
splitbarClass: "hsplitbar",
eventPos: "pageY", set: "top",
adjust: "height", offsetAdjust: "offsetHeight", adjSide1: "Top", adjSide2: "Bottom",
fixed: "width", offsetFixed: "offsetWidth", fixSide1: "Left", fixSide2: "Right"
}
}[((opts||{}).type||'v').charAt(0).toLowerCase()], opts||{});
return this.each(function() {
function startSplit(e) {
splitbar.addClass(opts.activeClass);
if ( e.type == "mousedown" ) {
paneA._posAdjust = paneA[0][opts.offsetAdjust] - e[opts.eventPos];
jQuery(document)
.bind("mousemove", doSplitMouse)
.bind("mouseup", endSplit);
}
return true; // required???
}
function doSplitKey(e) {
var key = e.which || e.keyCode;
var dir = key==opts.keyGrowA? 1 : key==opts.keyShrinkA? -1 : 0;
if ( dir )
moveSplitter(paneA[0][opts.offsetAdjust]+dir*opts.pxPerKey);
return true; // required???
}
function doSplitMouse(e) {
moveSplitter(paneA._posAdjust+e[opts.eventPos]);
}
function endSplit(e) {
splitbar.removeClass(opts.activeClass);
jQuery(document)
.unbind("mousemove", doSplitMouse)
.unbind("mouseup", endSplit);
}
function moveSplitter(np) {
// Constrain new position to fit pane size limits; 16=scrollbar fudge factor
// TODO: enforce group width in IE6 since it lacks min/max css properties?
np = Math.max(paneA._min+paneA._padAdjust, group._adjust - (paneB._max||9999), 16,
Math.min(np, paneA._max||9999, group._adjust - splitbar._adjust -
Math.max(paneB._min+paneB._padAdjust, 16)));
// Resize/position the two panes and splitbar
splitbar.css(opts.set, np+"px");
paneA.css(opts.adjust, np-paneA._padAdjust+"px");
paneB.css(opts.set, np+splitbar._adjust+"px")
.css(opts.adjust, group._adjust-splitbar._adjust-paneB._padAdjust-np+"px");
// IE fires resize for us; all others pay cash
if ( !jQuery.browser.msie ) {
paneA.trigger("resize");
paneB.trigger("resize");
}
}
function cssCache(jq, n, pf, m1, m2) {
// IE backCompat mode thinks width/height includes border and padding
jq[n] = jQuery.boxModel? (parseInt(jq.css(pf+m1))||0) + (parseInt(jq.css(pf+m2))||0) : 0;
}
function optCache(jq, pane) {
// Opera returns -1px for min/max dimensions when they're not there!
jq._min = Math.max(0, opts["min"+pane] || parseInt(jq.css("min-"+opts.adjust)) || 0);
jq._max = Math.max(0, opts["max"+pane] || parseInt(jq.css("max-"+opts.adjust)) || 0);
}
// Create jQuery object closures for splitter group and both panes
var group = jQuery(this).css({position: "relative"});
var divs = jQuery(">div", group).css({
position: "absolute", // positioned inside splitter container
margin: "0", // remove any stylesheet margin or ...
border: "0", // ... border added for non-script situations
"-moz-user-focus": "ignore" // disable focusability in Firefox
});
var paneA = jQuery(divs[0]); // left or top
var paneB = jQuery(divs[1]); // right or bottom
// Focuser element, provides keyboard support
var focuser = jQuery('<a href="javascript:void(0)"></a>')
.bind("focus", startSplit).bind("keydown", doSplitKey).bind("blur", endSplit)
.attr({accessKey: opts.accessKey, tabIndex: opts.tabIndex});
// Splitbar element, displays actual splitter bar
// The select-related properties prevent unintended text highlighting
var splitbar = jQuery('<div></div>')
.insertAfter(paneA).append(focuser)
.attr({"class": opts.splitbarClass, unselectable: "on"})
.css({position: "absolute", "-khtml-user-select": "none",
"-moz-user-select": "none", "user-select": "none"})
.bind("mousedown", startSplit);
if ( /^(auto|default)$/.test(splitbar.css("cursor") || "auto") )
splitbar.css("cursor", opts.cursor);
// Cache several dimensions for speed--assume these don't change
splitbar._adjust = splitbar[0][opts.offsetAdjust];
cssCache(group, "_borderAdjust", "border", opts.adjSide1+"Width", opts.adjSide2+"Width");
cssCache(group, "_borderFixed", "border", opts.fixSide1+"Width", opts.fixSide2+"Width");
cssCache(paneA, "_padAdjust", "padding", opts.adjSide1, opts.adjSide2);
cssCache(paneA, "_padFixed", "padding", opts.fixSide1, opts.fixSide2);
cssCache(paneB, "_padAdjust", "padding", opts.adjSide1, opts.adjSide2);
cssCache(paneB, "_padFixed", "padding", opts.fixSide1, opts.fixSide2);
optCache(paneA, 'A');
optCache(paneB, 'B');
// Initial splitbar position as measured from left edge of splitter
paneA._init = (opts.initA==true? parseInt(jQuery.curCSS(paneA[0],opts.adjust)) : opts.initA) || 0;
paneB._init = (opts.initB==true? parseInt(jQuery.curCSS(paneB[0],opts.adjust)) : opts.initB) || 0;
if ( paneB._init )
paneB._init = group[0][opts.offsetAdjust] - group._borderAdjust - paneB._init - splitbar._adjust;
// Set up resize event handler and trigger immediately to set initial position
group.bind("resize", function(e,size){
// Determine new width/height of splitter container
group._fixed = group[0][opts.offsetFixed] - group._borderFixed;
group._adjust = group[0][opts.offsetAdjust] - group._borderAdjust;
// Bail if splitter isn't visible or content isn't there yet
if ( group._fixed <= 0 || group._adjust <= 0 ) return;
// Set the fixed dimension (e.g., height on a vertical splitter)
paneA.css(opts.fixed, group._fixed-paneA._padFixed+"px");
paneB.css(opts.fixed, group._fixed-paneB._padFixed+"px");
splitbar.css(opts.fixed, group._fixed+"px");
// Re-divvy the adjustable dimension; maintain size of the preferred pane
moveSplitter(size || (!opts.initB? paneA[0][opts.offsetAdjust] :
group._adjust-paneB[0][opts.offsetAdjust]-splitbar._adjust));
}).trigger("resize" , [paneA._init || paneB._init ||
Math.round((group[0][opts.offsetAdjust] - group._borderAdjust - splitbar._adjust)/2)]);
});
};