-
Notifications
You must be signed in to change notification settings - Fork 0
/
jquery.splitter.js
180 lines (170 loc) · 7.69 KB
/
jquery.splitter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* 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 ([email protected])
*/
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)]);
});
};