www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

jquery.layout.js (82920B)


      1 /*
      2  * jquery.layout 1.2.0
      3  *
      4  * Copyright (c) 2008 
      5  *   Fabrizio Balliano (http://www.fabrizioballiano.net)
      6  *   Kevin Dalman (http://allpro.net)
      7  *
      8  * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
      9  * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
     10  *
     11  * $Date: 2008-12-27 02:17:22 +0100 (sab, 27 dic 2008) $
     12  * $Rev: 203 $
     13  * 
     14  * NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars
     15  */
     16 (function($) {
     17 
     18 $.fn.layout = function (opts) {
     19 
     20 /*
     21  * ###########################
     22  *   WIDGET CONFIG & OPTIONS
     23  * ###########################
     24  */
     25 
     26 	// DEFAULTS for options
     27 	var 
     28 		prefix = "ui-layout-" // prefix for ALL selectors and classNames
     29 	,	defaults = { //	misc default values
     30 			paneClass:				prefix+"pane"		// ui-layout-pane
     31 		,	resizerClass:			prefix+"resizer"	// ui-layout-resizer
     32 		,	togglerClass:			prefix+"toggler"	// ui-layout-toggler
     33 		,	togglerInnerClass:		prefix+""			// ui-layout-open / ui-layout-closed
     34 		,	buttonClass:			prefix+"button"		// ui-layout-button
     35 		,	contentSelector:		"."+prefix+"content"// ui-layout-content
     36 		,	contentIgnoreSelector:	"."+prefix+"ignore"	// ui-layout-mask 
     37 		}
     38 	;
     39 
     40 	// DEFAULT PANEL OPTIONS - CHANGE IF DESIRED
     41 	var options = {
     42 		name:						""			// FUTURE REFERENCE - not used right now
     43 	,	scrollToBookmarkOnLoad:		true		// after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
     44 	,	defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
     45 			applyDefaultStyles: 	false		// apply basic styles directly to resizers & buttons? If not, then stylesheet must handle it
     46 		,	closable:				true		// pane can open & close
     47 		,	resizable:				true		// when open, pane can be resized 
     48 		,	slidable:				true		// when closed, pane can 'slide' open over other panes - closes on mouse-out
     49 		//,	paneSelector:			[ ]			// MUST be pane-specific!
     50 		,	contentSelector:		defaults.contentSelector	// INNER div/element to auto-size so only it scrolls, not the entire pane!
     51 		,	contentIgnoreSelector:	defaults.contentIgnoreSelector	// elem(s) to 'ignore' when measuring 'content'
     52 		,	paneClass:				defaults.paneClass		// border-Pane - default: 'ui-layout-pane'
     53 		,	resizerClass:			defaults.resizerClass	// Resizer Bar		- default: 'ui-layout-resizer'
     54 		,	togglerClass:			defaults.togglerClass	// Toggler Button	- default: 'ui-layout-toggler'
     55 		,	buttonClass:			defaults.buttonClass	// CUSTOM Buttons	- default: 'ui-layout-button-toggle/-open/-close/-pin'
     56 		,	resizerDragOpacity:		1			// option for ui.draggable
     57 		//,	resizerCursor:			""			// MUST be pane-specific - cursor when over resizer-bar
     58 		,	maskIframesOnResize:	true		// true = all iframes OR = iframe-selector(s) - adds masking-div during resizing/dragging
     59 		//,	size:					100			// inital size of pane - defaults are set 'per pane'
     60 		,	minSize:				0			// when manually resizing a pane
     61 		,	maxSize:				0			// ditto, 0 = no limit
     62 		,	spacing_open:			6			// space between pane and adjacent panes - when pane is 'open'
     63 		,	spacing_closed:			6			// ditto - when pane is 'closed'
     64 		,	togglerLength_open:		50			// Length = WIDTH of toggler button on north/south edges - HEIGHT on east/west edges
     65 		,	togglerLength_closed: 	50			// 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
     66 		,	togglerAlign_open:		"center"	// top/left, bottom/right, center, OR...
     67 		,	togglerAlign_closed:	"center"	// 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
     68 		,	togglerTip_open:		"Close"		// Toggler tool-tip (title)
     69 		,	togglerTip_closed:		"Open"		// ditto
     70 		,	resizerTip:				"Resize"	// Resizer tool-tip (title)
     71 		,	sliderTip:				"Slide Open" // resizer-bar triggers 'sliding' when pane is closed
     72 		,	sliderCursor:			"pointer"	// cursor when resizer-bar will trigger 'sliding'
     73 		,	slideTrigger_open:		"click"		// click, dblclick, mouseover
     74 		,	slideTrigger_close:		"mouseout"	// click, mouseout
     75 		,	hideTogglerOnSlide:		false		// when pane is slid-open, should the toggler show?
     76 		,	togglerContent_open:	""			// text or HTML to put INSIDE the toggler
     77 		,	togglerContent_closed:	""			// ditto
     78 		,	showOverflowOnHover:	false		// will bind allowOverflow() utility to pane.onMouseOver
     79 		,	enableCursorHotkey:		true		// enabled 'cursor' hotkeys
     80 		//,	customHotkey:			""			// MUST be pane-specific - EITHER a charCode OR a character
     81 		,	customHotkeyModifier:	"SHIFT"		// either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
     82 		//	NOTE: fxSss_open & fxSss_close options (eg: fxName_open) are auto-generated if not passed
     83 		,	fxName:					"slide" 	// ('none' or blank), slide, drop, scale
     84 		,	fxSpeed:				null		// slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
     85 		,	fxSettings:				{}			// can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
     86 		,	initClosed:				false		// true = init pane as 'closed'
     87 		,	initHidden: 			false 		// true = init pane as 'hidden' - no resizer or spacing
     88 		
     89 		/*	callback options do not have to be set - listed here for reference only
     90 		,	onshow_start:			""			// CALLBACK when pane STARTS to Show	- BEFORE onopen/onhide_start
     91 		,	onshow_end:				""			// CALLBACK when pane ENDS being Shown	- AFTER  onopen/onhide_end
     92 		,	onhide_start:			""			// CALLBACK when pane STARTS to Close	- BEFORE onclose_start
     93 		,	onhide_end:				""			// CALLBACK when pane ENDS being Closed	- AFTER  onclose_end
     94 		,	onopen_start:			""			// CALLBACK when pane STARTS to Open
     95 		,	onopen_end:				""			// CALLBACK when pane ENDS being Opened
     96 		,	onclose_start:			""			// CALLBACK when pane STARTS to Close
     97 		,	onclose_end:			""			// CALLBACK when pane ENDS being Closed
     98 		,	onresize_start:			""			// CALLBACK when pane STARTS to be ***MANUALLY*** Resized
     99 		,	onresize_end:			""			// CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
    100 		*/
    101 		}
    102 	,	north: {
    103 			paneSelector:			"."+prefix+"north" // default = .ui-layout-north
    104 		,	size:					"auto"
    105 		,	resizerCursor:			"n-resize"
    106 		}
    107 	,	south: {
    108 			paneSelector:			"."+prefix+"south" // default = .ui-layout-south
    109 		,	size:					"auto"
    110 		,	resizerCursor:			"s-resize"
    111 		}
    112 	,	east: {
    113 			paneSelector:			"."+prefix+"east" // default = .ui-layout-east
    114 		,	size:					200
    115 		,	resizerCursor:			"e-resize"
    116 		}
    117 	,	west: {
    118 			paneSelector:			"."+prefix+"west" // default = .ui-layout-west
    119 		,	size:					200
    120 		,	resizerCursor:			"w-resize"
    121 		}
    122 	,	center: {
    123 			paneSelector:			"."+prefix+"center" // default = .ui-layout-center
    124 		}
    125 
    126 	};
    127 
    128 
    129 	var effects = { // LIST *PREDEFINED EFFECTS* HERE, even if effect has no settings
    130 		slide:	{
    131 			all:	{ duration:  "fast"	} // eg: duration: 1000, easing: "easeOutBounce"
    132 		,	north:	{ direction: "up"	}
    133 		,	south:	{ direction: "down"	}
    134 		,	east:	{ direction: "right"}
    135 		,	west:	{ direction: "left"	}
    136 		}
    137 	,	drop:	{
    138 			all:	{ duration:  "slow"	} // eg: duration: 1000, easing: "easeOutQuint"
    139 		,	north:	{ direction: "up"	}
    140 		,	south:	{ direction: "down"	}
    141 		,	east:	{ direction: "right"}
    142 		,	west:	{ direction: "left"	}
    143 		}
    144 	,	scale:	{
    145 			all:	{ duration:  "fast"	}
    146 		}
    147 	};
    148 
    149 
    150 	// STATIC, INTERNAL CONFIG - DO NOT CHANGE THIS!
    151 	var config = {
    152 		allPanes:		"north,south,east,west,center"
    153 	,	borderPanes:	"north,south,east,west"
    154 	,	zIndex: { // set z-index values here
    155 			resizer_normal:	1		// normal z-index for resizer-bars
    156 		,	pane_normal:	2		// normal z-index for panes
    157 		,	mask:			4		// overlay div used to mask pane(s) during resizing
    158 		,	sliding:		100		// applied to both the pane and its resizer when a pane is 'slid open'
    159 		,	resizing:		10000	// applied to the CLONED resizer-bar when being 'dragged'
    160 		,	animation:		10000	// applied to the pane when being animated - not applied to the resizer
    161 		}
    162 	,	resizers: {
    163 			cssReq: {
    164 				position: 	"absolute"
    165 			,	padding: 	0
    166 			,	margin: 	0
    167 			,	fontSize:	"1px"
    168 			,	textAlign:	"left" // to counter-act "center" alignment!
    169 			,	overflow: 	"hidden" // keep toggler button from overflowing
    170 			,	zIndex: 	1
    171 			}
    172 		,	cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
    173 				background: "#DDD"
    174 			,	border:		"none"
    175 			}
    176 		}
    177 	,	togglers: {
    178 			cssReq: {
    179 				position: 	"absolute"
    180 			,	display: 	"block"
    181 			,	padding: 	0
    182 			,	margin: 	0
    183 			,	overflow:	"hidden"
    184 			,	textAlign:	"center"
    185 			,	fontSize:	"1px"
    186 			,	cursor: 	"pointer"
    187 			,	zIndex: 	1
    188 			}
    189 		,	cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
    190 				background: "#AAA"
    191 			}
    192 		}
    193 	,	content: {
    194 			cssReq: {
    195 				overflow:	"auto"
    196 			}
    197 		,	cssDef: {}
    198 		}
    199 	,	defaults: { // defaults for ALL panes - overridden by 'per-pane settings' below
    200 			cssReq: {
    201 				position: 	"absolute"
    202 			,	margin:		0
    203 			,	zIndex: 	2
    204 			}
    205 		,	cssDef: {
    206 				padding:	"10px"
    207 			,	background:	"#FFF"
    208 			,	border:		"1px solid #BBB"
    209 			,	overflow:	"auto"
    210 			}
    211 		}
    212 	,	north: {
    213 			edge:			"top"
    214 		,	sizeType:		"height"
    215 		,	dir:			"horz"
    216 		,	cssReq: {
    217 				top: 		0
    218 			,	bottom: 	"auto"
    219 			,	left: 		0
    220 			,	right: 		0
    221 			,	width: 		"auto"
    222 			//	height: 	DYNAMIC
    223 			}
    224 		}
    225 	,	south: {
    226 			edge:			"bottom"
    227 		,	sizeType:		"height"
    228 		,	dir:			"horz"
    229 		,	cssReq: {
    230 				top: 		"auto"
    231 			,	bottom: 	0
    232 			,	left: 		0
    233 			,	right: 		0
    234 			,	width: 		"auto"
    235 			//	height: 	DYNAMIC
    236 			}
    237 		}
    238 	,	east: {
    239 			edge:			"right"
    240 		,	sizeType:		"width"
    241 		,	dir:			"vert"
    242 		,	cssReq: {
    243 				left: 		"auto"
    244 			,	right: 		0
    245 			,	top: 		"auto" // DYNAMIC
    246 			,	bottom: 	"auto" // DYNAMIC
    247 			,	height: 	"auto"
    248 			//	width: 		DYNAMIC
    249 			}
    250 		}
    251 	,	west: {
    252 			edge:			"left"
    253 		,	sizeType:		"width"
    254 		,	dir:			"vert"
    255 		,	cssReq: {
    256 				left: 		0
    257 			,	right: 		"auto"
    258 			,	top: 		"auto" // DYNAMIC
    259 			,	bottom: 	"auto" // DYNAMIC
    260 			,	height: 	"auto"
    261 			//	width: 		DYNAMIC
    262 			}
    263 		}
    264 	,	center: {
    265 			dir:			"center"
    266 		,	cssReq: {
    267 				left: 		"auto" // DYNAMIC
    268 			,	right: 		"auto" // DYNAMIC
    269 			,	top: 		"auto" // DYNAMIC
    270 			,	bottom: 	"auto" // DYNAMIC
    271 			,	height: 	"auto"
    272 			,	width: 		"auto"
    273 			}
    274 		}
    275 	};
    276 
    277 
    278 	// DYNAMIC DATA
    279 	var state = {
    280 		// generate random 'ID#' to identify layout - used to create global namespace for timers
    281 		id:			Math.floor(Math.random() * 10000)
    282 	,	container:	{}
    283 	,	north:		{}
    284 	,	south:		{}
    285 	,	east:		{}
    286 	,	west:		{}
    287 	,	center:		{}
    288 	};
    289 
    290 
    291 	var 
    292 		altEdge = {
    293 			top:	"bottom"
    294 		,	bottom: "top"
    295 		,	left:	"right"
    296 		,	right:	"left"
    297 		}
    298 	,	altSide = {
    299 			north:	"south"
    300 		,	south:	"north"
    301 		,	east: 	"west"
    302 		,	west: 	"east"
    303 		}
    304 	;
    305 
    306 
    307 /*
    308  * ###########################
    309  *  INTERNAL HELPER FUNCTIONS
    310  * ###########################
    311  */
    312 
    313 	/**
    314 	 * isStr
    315 	 *
    316 	 * Returns true if passed param is EITHER a simple string OR a 'string object' - otherwise returns false
    317 	 */
    318 	var isStr = function (o) {
    319 		if (typeof o == "string")
    320 			return true;
    321 		else if (typeof o == "object") {
    322 			try {
    323 				var match = o.constructor.toString().match(/string/i); 
    324 				return (match !== null);
    325 			} catch (e) {} 
    326 		}
    327 		return false;
    328 	};
    329 
    330 	/**
    331 	 * str
    332 	 *
    333 	 * Returns a simple string if the passed param is EITHER a simple string OR a 'string object',
    334 	 *  else returns the original object
    335 	 */
    336 	var str = function (o) {
    337 		if (typeof o == "string" || isStr(o)) return $.trim(o); // trim converts 'String object' to a simple string
    338 		else return o;
    339 	};
    340 
    341 	/**
    342 	 * min / max
    343 	 *
    344 	 * Alias for Math.min/.max to simplify coding
    345 	 */
    346 	var min = function (x,y) { return Math.min(x,y); };
    347 	var max = function (x,y) { return Math.max(x,y); };
    348 
    349 	/**
    350 	 * transformData
    351 	 *
    352 	 * Processes the options passed in and transforms them into the format used by layout()
    353 	 * Missing keys are added, and converts the data if passed in 'flat-format' (no sub-keys)
    354 	 * In flat-format, pane-specific-settings are prefixed like: north__optName  (2-underscores)
    355 	 * To update effects, options MUST use nested-keys format, with an effects key
    356 	 *
    357 	 * @callers  initOptions()
    358 	 * @params  JSON  d  Data/options passed by user - may be a single level or nested levels
    359 	 * @returns JSON  Creates a data struture that perfectly matches 'options', ready to be imported
    360 	 */
    361 	var transformData = function (d) {
    362 		var json = { defaults:{fxSettings:{}}, north:{fxSettings:{}}, south:{fxSettings:{}}, east:{fxSettings:{}}, west:{fxSettings:{}}, center:{fxSettings:{}} };
    363 		d = d || {};
    364 		if (d.effects || d.defaults || d.north || d.south || d.west || d.east || d.center)
    365 			json = $.extend( json, d ); // already in json format - add to base keys
    366 		else
    367 			// convert 'flat' to 'nest-keys' format - also handles 'empty' user-options
    368 			$.each( d, function (key,val) {
    369 				a = key.split("__");
    370 				json[ a[1] ? a[0] : "defaults" ][ a[1] ? a[1] : a[0] ] = val;
    371 			});
    372 		return json;
    373 	};
    374 
    375 	/**
    376 	 * setFlowCallback
    377 	 *
    378 	 * Set an INTERNAL callback to avoid simultaneous animation
    379 	 * Runs only if needed and only if all callbacks are not 'already set'!
    380 	 *
    381 	 * @param String   action  Either 'open' or 'close'
    382 	 * @pane  String   pane    A valid border-pane name, eg 'west'
    383 	 * @pane  Boolean  param   Extra param for callback (optional)
    384 	 */
    385 	var setFlowCallback = function (action, pane, param) {
    386 		var
    387 			cb = action +","+ pane +","+ (param ? 1 : 0)
    388 		,	cP, cbPane
    389 		;
    390 		$.each(c.borderPanes.split(","), function (i,p) {
    391 			if (c[p].isMoving) {
    392 				bindCallback(p); // TRY to bind a callback
    393 				return false; // BREAK
    394 			}
    395 		});
    396 
    397 		function bindCallback (p, test) {
    398 			cP = c[p];
    399 			if (!cP.doCallback) {
    400 				cP.doCallback = true;
    401 				cP.callback = cb;
    402 			}
    403 			else { // try to 'chain' this callback
    404 				cpPane = cP.callback.split(",")[1]; // 2nd param is 'pane'
    405 				if (cpPane != p && cpPane != pane) // callback target NOT 'itself' and NOT 'this pane'
    406 					bindCallback (cpPane, true); // RECURSE
    407 			}
    408 		}
    409 	};
    410 
    411 	/**
    412 	 * execFlowCallback
    413 	 *
    414 	 * RUN the INTERNAL callback for this pane - if one exists
    415 	 *
    416 	 * @param String   action  Either 'open' or 'close'
    417 	 * @pane  String   pane    A valid border-pane name, eg 'west'
    418 	 * @pane  Boolean  param   Extra param for callback (optional)
    419 	 */
    420 	var execFlowCallback = function (pane) {
    421 		var cP = c[pane];
    422 
    423 		// RESET flow-control flaGs
    424 		c.isLayoutBusy = false;
    425 		delete cP.isMoving;
    426 		if (!cP.doCallback || !cP.callback) return;
    427 
    428 		cP.doCallback = false; // RESET logic flag
    429 
    430 		// EXECUTE the callback
    431 		var
    432 			cb = cP.callback.split(",")
    433 		,	param = (cb[2] > 0 ? true : false)
    434 		;
    435 		if (cb[0] == "open")
    436 			open( cb[1], param  );
    437 		else if (cb[0] == "close")
    438 			close( cb[1], param );
    439 
    440 		if (!cP.doCallback) cP.callback = null; // RESET - unless callback above enabled it again!
    441 	};
    442 
    443 	/**
    444 	 * execUserCallback
    445 	 *
    446 	 * Executes a Callback function after a trigger event, like resize, open or close
    447 	 *
    448 	 * @param String  pane   This is passed only so we can pass the 'pane object' to the callback
    449 	 * @param String  v_fn  Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
    450 	 */
    451 	var execUserCallback = function (pane, v_fn) {
    452 		if (!v_fn) return;
    453 		var fn;
    454 		try {
    455 			if (typeof v_fn == "function")
    456 				fn = v_fn;	
    457 			else if (typeof v_fn != "string")
    458 				return;
    459 			else if (v_fn.indexOf(",") > 0) {
    460 				// function name cannot contain a comma, so must be a function name AND a 'name' parameter
    461 				var
    462 					args = v_fn.split(",")
    463 				,	fn = eval(args[0])
    464 				;
    465 				if (typeof fn=="function" && args.length > 1)
    466 					return fn(args[1]); // pass the argument parsed from 'list'
    467 			}
    468 			else // just the name of an external function?
    469 				fn = eval(v_fn);
    470 
    471 			if (typeof fn=="function")
    472 				// pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
    473 				return fn( pane, $Ps[pane], $.extend({},state[pane]), $.extend({},options[pane]), options.name );
    474 		}
    475 		catch (ex) {}
    476 	};
    477 
    478 	/**
    479 	 * cssNum
    480 	 *
    481 	 * Returns the 'current CSS value' for an element - returns 0 if property does not exist
    482 	 *
    483 	 * @callers  Called by many methods
    484 	 * @param jQuery  $Elem  Must pass a jQuery object - first element is processed
    485 	 * @param String  property  The name of the CSS property, eg: top, width, etc.
    486 	 * @returns Variant  Usually is used to get an integer value for position (top, left) or size (height, width)
    487 	 */
    488 	var cssNum = function ($E, prop) {
    489 		var
    490 			val = 0
    491 		,	hidden = false
    492 		,	visibility = ""
    493 		;
    494 		if (!$.browser.msie) { // IE CAN read dimensions of 'hidden' elements - FF CANNOT
    495 			if ($.curCSS($E[0], "display", true) == "none") {
    496 				hidden = true;
    497 				visibility = $.curCSS($E[0], "visibility", true); // SAVE current setting
    498 				$E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so we can measure it
    499 			}
    500 		}
    501 
    502 		val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
    503 
    504 		if (hidden) { // WAS hidden, so put back the way it was
    505 			$E.css({ display: "none" });
    506 			if (visibility && visibility != "hidden")
    507 				$E.css({ visibility: visibility }); // reset 'visibility'
    508 		}
    509 
    510 		return val;
    511 	};
    512 
    513 	/**
    514 	 * cssW / cssH / cssSize
    515 	 *
    516 	 * Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
    517 	 *
    518 	 * @callers  initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
    519 	 * @param Variant  elem  Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
    520 	 * @param Integer  outerWidth/outerHeight  (optional) Can pass a width, allowing calculations BEFORE element is resized
    521 	 * @returns Integer  Returns the innerHeight of the elem by subtracting padding and borders
    522 	 *
    523 	 * @TODO  May need to add additional logic to handle more browser/doctype variations?
    524 	 */
    525 	var cssW = function (e, outerWidth) {
    526 		var $E;
    527 		if (isStr(e)) {
    528 			e = str(e);
    529 			$E = $Ps[e];
    530 		}
    531 		else
    532 			$E = $(e);
    533 
    534 		// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
    535 		if (outerWidth <= 0)
    536 			return 0;
    537 		else if (!(outerWidth>0))
    538 			outerWidth = isStr(e) ? getPaneSize(e) : $E.outerWidth();
    539 
    540 		if (!$.boxModel)
    541 			return outerWidth;
    542 
    543 		else // strip border and padding size from outerWidth to get CSS Width
    544 			return outerWidth
    545 				- cssNum($E, "paddingLeft")		
    546 				- cssNum($E, "paddingRight")
    547 				- ($.curCSS($E[0], "borderLeftStyle", true) == "none" ? 0 : cssNum($E, "borderLeftWidth"))
    548 				- ($.curCSS($E[0], "borderRightStyle", true) == "none" ? 0 : cssNum($E, "borderRightWidth"))
    549 			;
    550 	};
    551 	var cssH = function (e, outerHeight) {
    552 		var $E;
    553 		if (isStr(e)) {
    554 			e = str(e);
    555 			$E = $Ps[e];
    556 		}
    557 		else
    558 			$E = $(e);
    559 
    560 		// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
    561 		if (outerHeight <= 0)
    562 			return 0;
    563 		else if (!(outerHeight>0))
    564 			outerHeight = (isStr(e)) ? getPaneSize(e) : $E.outerHeight();
    565 
    566 		if (!$.boxModel)
    567 			return outerHeight;
    568 
    569 		else // strip border and padding size from outerHeight to get CSS Height
    570 			return outerHeight
    571 				- cssNum($E, "paddingTop")
    572 				- cssNum($E, "paddingBottom")
    573 				- ($.curCSS($E[0], "borderTopStyle", true) == "none" ? 0 : cssNum($E, "borderTopWidth"))
    574 				- ($.curCSS($E[0], "borderBottomStyle", true) == "none" ? 0 : cssNum($E, "borderBottomWidth"))
    575 			;
    576 	};
    577 	var cssSize = function (pane, outerSize) {
    578 		if (c[pane].dir=="horz") // pane = north or south
    579 			return cssH(pane, outerSize);
    580 		else // pane = east or west
    581 			return cssW(pane, outerSize);
    582 	};
    583 
    584 	/**
    585 	 * getPaneSize
    586 	 *
    587 	 * Calculates the current 'size' (width or height) of a border-pane - optionally with 'pane spacing' added
    588 	 *
    589 	 * @returns Integer  Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
    590 	 */
    591 	var getPaneSize = function (pane, inclSpace) {
    592 		var 
    593 			$P	= $Ps[pane]
    594 		,	o	= options[pane]
    595 		,	s	= state[pane]
    596 		,	oSp	= (inclSpace ? o.spacing_open : 0)
    597 		,	cSp	= (inclSpace ? o.spacing_closed : 0)
    598 		;
    599 		if (!$P || s.isHidden)
    600 			return 0;
    601 		else if (s.isClosed || (s.isSliding && inclSpace))
    602 			return cSp;
    603 		else if (c[pane].dir == "horz")
    604 			return $P.outerHeight() + oSp;
    605 		else // dir == "vert"
    606 			return $P.outerWidth() + oSp;
    607 	};
    608 
    609 	var setPaneMinMaxSizes = function (pane) {
    610 		var 
    611 			d				= cDims
    612 		,	edge			= c[pane].edge
    613 		,	dir				= c[pane].dir
    614 		,	o				= options[pane]
    615 		,	s				= state[pane]
    616 		,	$P				= $Ps[pane]
    617 		,	$altPane		= $Ps[ altSide[pane] ]
    618 		,	paneSpacing		= o.spacing_open
    619 		,	altPaneSpacing	= options[ altSide[pane] ].spacing_open
    620 		,	altPaneSize		= (!$altPane ? 0 : (dir=="horz" ? $altPane.outerHeight() : $altPane.outerWidth()))
    621 		,	containerSize	= (dir=="horz" ? d.innerHeight : d.innerWidth)
    622 		//	limitSize prevents this pane from 'overlapping' opposite pane - even if opposite pane is currently closed
    623 		,	limitSize		= containerSize - paneSpacing - altPaneSize - altPaneSpacing
    624 		,	minSize			= s.minSize || 0
    625 		,	maxSize			= Math.min(s.maxSize || 9999, limitSize)
    626 		,	minPos, maxPos	// used to set resizing limits
    627 		;
    628 		switch (pane) {
    629 			case "north":	minPos = d.offsetTop + minSize;
    630 							maxPos = d.offsetTop + maxSize;
    631 							break;
    632 			case "west":	minPos = d.offsetLeft + minSize;
    633 							maxPos = d.offsetLeft + maxSize;
    634 							break;
    635 			case "south":	minPos = d.offsetTop + d.innerHeight - maxSize;
    636 							maxPos = d.offsetTop + d.innerHeight - minSize;
    637 							break;
    638 			case "east":	minPos = d.offsetLeft + d.innerWidth - maxSize;
    639 							maxPos = d.offsetLeft + d.innerWidth - minSize;
    640 							break;
    641 		}
    642 		// save data to pane-state
    643 		$.extend(s, { minSize: minSize, maxSize: maxSize, minPosition: minPos, maxPosition: maxPos });
    644 	};
    645 
    646 	/**
    647 	 * getPaneDims
    648 	 *
    649 	 * Returns data for setting the size/position of center pane. Date is also used to set Height for east/west panes
    650 	 *
    651 	 * @returns JSON  Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
    652 	 */
    653 	var getPaneDims = function () {
    654 		var d = {
    655 			top:	getPaneSize("north", true) // true = include 'spacing' value for p
    656 		,	bottom:	getPaneSize("south", true)
    657 		,	left:	getPaneSize("west", true)
    658 		,	right:	getPaneSize("east", true)
    659 		,	width:	0
    660 		,	height:	0
    661 		};
    662 
    663 		with (d) {
    664 			width 	= cDims.innerWidth - left - right;
    665 			height 	= cDims.innerHeight - bottom - top;
    666 			// now add the 'container border/padding' to get final positions - relative to the container
    667 			top		+= cDims.top;
    668 			bottom	+= cDims.bottom;
    669 			left	+= cDims.left;
    670 			right	+= cDims.right;
    671 		}
    672 
    673 		return d;
    674 	};
    675 
    676 
    677 	/**
    678 	 * getElemDims
    679 	 *
    680 	 * Returns data for setting size of an element (container or a pane).
    681 	 *
    682 	 * @callers  create(), onWindowResize() for container, plus others for pane
    683 	 * @returns JSON  Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
    684 	 */
    685 	var getElemDims = function ($E) {
    686 		var
    687 			d = {} // dimensions hash
    688 		,	e, b, p // edge, border, padding
    689 		;
    690 
    691 		$.each("Left,Right,Top,Bottom".split(","), function () {
    692 			e = str(this);
    693 			b = d["border" +e] = cssNum($E, "border"+e+"Width");
    694 			p = d["padding"+e] = cssNum($E, "padding"+e);
    695 			d["offset" +e] = b + p; // total offset of content from outer edge
    696 			// if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
    697 			if ($E == $Container)
    698 				d[e.toLowerCase()] = ($.boxModel ? p : 0); 
    699 		});
    700 
    701 		d.innerWidth  = d.outerWidth  = $E.outerWidth();
    702 		d.innerHeight = d.outerHeight = $E.outerHeight();
    703 		if ($.boxModel) {
    704 			d.innerWidth  -= (d.offsetLeft + d.offsetRight);
    705 			d.innerHeight -= (d.offsetTop  + d.offsetBottom);
    706 		}
    707 
    708 		return d;
    709 	};
    710 
    711 
    712 	var setTimer = function (pane, action, fn, ms) {
    713 		var
    714 			Layout = window.layout = window.layout || {}
    715 		,	Timers = Layout.timers = Layout.timers || {}
    716 		,	name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
    717 		;
    718 		if (Timers[name]) return; // timer already set!
    719 		else Timers[name] = setTimeout(fn, ms);
    720 	};
    721 
    722 	var clearTimer = function (pane, action) {
    723 		var
    724 			Layout = window.layout = window.layout || {}
    725 		,	Timers = Layout.timers = Layout.timers || {}
    726 		,	name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
    727 		;
    728 		if (Timers[name]) {
    729 			clearTimeout( Timers[name] );
    730 			delete Timers[name];
    731 			return true;
    732 		}
    733 		else
    734 			return false;
    735 	};
    736 
    737 
    738 /*
    739  * ###########################
    740  *   INITIALIZATION METHODS
    741  * ###########################
    742  */
    743 
    744 	/**
    745 	 * create
    746 	 *
    747 	 * Initialize the layout - called automatically whenever an instance of layout is created
    748 	 *
    749 	 * @callers  NEVER explicity called
    750 	 * @returns  An object pointer to the instance created
    751 	 */
    752 	var create = function () {
    753 		// initialize config/options
    754 		initOptions();
    755 
    756 		// initialize all objects
    757 		initContainer();	// set CSS as needed and init state.container dimensions
    758 		initPanes();		// size & position all panes
    759 		initHandles();		// create and position all resize bars & togglers buttons
    760 		initResizable();	// activate resizing on all panes where resizable=true
    761 		sizeContent("all");	// AFTER panes & handles have been initialized, size 'content' divs
    762 
    763 		if (options.scrollToBookmarkOnLoad)
    764 			with (self.location) if (hash) replace( hash ); // scrollTo Bookmark
    765 
    766 		// bind hotkey function - keyDown - if required
    767 		initHotkeys();
    768 
    769 		// bind resizeAll() for 'this layout instance' to window.resize event
    770 		$(window).resize(function () {
    771 			var timerID = "timerLayout_"+state.id;
    772 			if (window[timerID]) clearTimeout(window[timerID]);
    773 			window[timerID] = null;
    774 			if (true || $.browser.msie) // use a delay for IE because the resize event fires repeatly
    775 				window[timerID] = setTimeout(resizeAll, 100);
    776 			else // most other browsers have a built-in delay before firing the resize event
    777 				resizeAll(); // resize all layout elements NOW!
    778 		});
    779 	};
    780 
    781 	/**
    782 	 * initContainer
    783 	 *
    784 	 * Validate and initialize container CSS and events
    785 	 *
    786 	 * @callers  create()
    787 	 */
    788 	var initContainer = function () {
    789 		try { // format html/body if this is a full page layout
    790 			if ($Container[0].tagName == "BODY") {
    791 				$("html").css({
    792 					height:		"100%"
    793 				,	overflow:	"hidden"
    794 				});
    795 				$("body").css({
    796 					position:	"relative"
    797 				,	height:		"100%"
    798 				,	overflow:	"hidden"
    799 				,	margin:		0
    800 				,	padding:	0		// TODO: test whether body-padding could be handled?
    801 				,	border:		"none"	// a body-border creates problems because it cannot be measured!
    802 				});
    803 			}
    804 			else { // set required CSS - overflow and position
    805 				var
    806 					CSS	= { overflow: "hidden" } // make sure container will not 'scroll'
    807 				,	p	= $Container.css("position")
    808 				,	h	= $Container.css("height")
    809 				;
    810 				// if this is a NESTED layout, then outer-pane ALREADY has position and height
    811 				if (!$Container.hasClass("ui-layout-pane")) {
    812 					if (!p || "fixed,absolute,relative".indexOf(p) < 0)
    813 						CSS.position = "relative"; // container MUST have a 'position'
    814 					if (!h || h=="auto")
    815 						CSS.height = "100%"; // container MUST have a 'height'
    816 				}
    817 				$Container.css( CSS );
    818 			}
    819 		} catch (ex) {}
    820 
    821 		// get layout-container dimensions (updated when necessary)
    822 		cDims = state.container = getElemDims( $Container ); // update data-pointer too
    823 	};
    824 
    825 	/**
    826 	 * initHotkeys
    827 	 *
    828 	 * Bind layout hotkeys - if options enabled
    829 	 *
    830 	 * @callers  create()
    831 	 */
    832 	var initHotkeys = function () {
    833 		// bind keyDown to capture hotkeys, if option enabled for ANY pane
    834 		$.each(c.borderPanes.split(","), function (i,pane) {
    835 			var o = options[pane];
    836 			if (o.enableCursorHotkey || o.customHotkey) {
    837 				$(document).keydown( keyDown ); // only need to bind this ONCE
    838 				return false; // BREAK - binding was done
    839 			}
    840 		});
    841 	};
    842 
    843 	/**
    844 	 * initOptions
    845 	 *
    846 	 * Build final CONFIG and OPTIONS data
    847 	 *
    848 	 * @callers  create()
    849 	 */
    850 	var initOptions = function () {
    851 		// simplify logic by making sure passed 'opts' var has basic keys
    852 		opts = transformData( opts );
    853 
    854 		// update default effects, if case user passed key
    855 		if (opts.effects) {
    856 			$.extend( effects, opts.effects );
    857 			delete opts.effects;
    858 		}
    859 
    860 		// see if any 'global options' were specified
    861 		$.each("name,scrollToBookmarkOnLoad".split(","), function (idx,key) {
    862 			if (opts[key] !== undefined)
    863 				options[key] = opts[key];
    864 			else if (opts.defaults[key] !== undefined) {
    865 				options[key] = opts.defaults[key];
    866 				delete opts.defaults[key];
    867 			}
    868 		});
    869 
    870 		// remove any 'defaults' that MUST be set 'per-pane'
    871 		$.each("paneSelector,resizerCursor,customHotkey".split(","),
    872 			function (idx,key) { delete opts.defaults[key]; } // is OK if key does not exist
    873 		);
    874 
    875 		// now update options.defaults
    876 		$.extend( options.defaults, opts.defaults );
    877 		// make sure required sub-keys exist
    878 		//if (typeof options.defaults.fxSettings != "object") options.defaults.fxSettings = {};
    879 
    880 		// merge all config & options for the 'center' pane
    881 		c.center = $.extend( true, {}, c.defaults, c.center );
    882 		$.extend( options.center, opts.center );
    883 		// Most 'default options' do not apply to 'center', so add only those that DO
    884 		var o_Center = $.extend( true, {}, options.defaults, opts.defaults, options.center ); // TEMP data
    885 		$.each("paneClass,contentSelector,contentIgnoreSelector,applyDefaultStyles,showOverflowOnHover".split(","),
    886 			function (idx,key) { options.center[key] = o_Center[key]; }
    887 		);
    888 
    889 		var defs = options.defaults;
    890 
    891 		// create a COMPLETE set of options for EACH border-pane
    892 		$.each(c.borderPanes.split(","), function(i,pane) {
    893 			// apply 'pane-defaults' to CONFIG.PANE
    894 			c[pane] = $.extend( true, {}, c.defaults, c[pane] );
    895 			// apply 'pane-defaults' +  user-options to OPTIONS.PANE
    896 			o = options[pane] = $.extend( true, {}, options.defaults, options[pane], opts.defaults, opts[pane] );
    897 
    898 			// make sure we have base-classes
    899 			if (!o.paneClass)		o.paneClass		= defaults.paneClass;
    900 			if (!o.resizerClass)	o.resizerClass	= defaults.resizerClass;
    901 			if (!o.togglerClass)	o.togglerClass	= defaults.togglerClass;
    902 
    903 			// create FINAL fx options for each pane, ie: options.PANE.fxName/fxSpeed/fxSettings[_open|_close]
    904 			$.each(["_open","_close",""], function (i,n) { 
    905 				var
    906 					sName		= "fxName"+n
    907 				,	sSpeed		= "fxSpeed"+n
    908 				,	sSettings	= "fxSettings"+n
    909 				;
    910 				// recalculate fxName according to specificity rules
    911 				o[sName] =
    912 					opts[pane][sName]		// opts.west.fxName_open
    913 				||	opts[pane].fxName		// opts.west.fxName
    914 				||	opts.defaults[sName]	// opts.defaults.fxName_open
    915 				||	opts.defaults.fxName	// opts.defaults.fxName
    916 				||	o[sName]				// options.west.fxName_open
    917 				||	o.fxName				// options.west.fxName
    918 				||	defs[sName]				// options.defaults.fxName_open
    919 				||	defs.fxName				// options.defaults.fxName
    920 				||	"none"
    921 				;
    922 				// validate fxName to be sure is a valid effect
    923 				var fxName = o[sName];
    924 				if (fxName == "none" || !$.effects || !$.effects[fxName] || (!effects[fxName] && !o[sSettings] && !o.fxSettings))
    925 					fxName = o[sName] = "none"; // effect not loaded, OR undefined FX AND fxSettings not passed
    926 				// set vars for effects subkeys to simplify logic
    927 				var
    928 					fx = effects[fxName]	|| {} // effects.slide
    929 				,	fx_all	= fx.all		|| {} // effects.slide.all
    930 				,	fx_pane	= fx[pane]		|| {} // effects.slide.west
    931 				;
    932 				// RECREATE the fxSettings[_open|_close] keys using specificity rules
    933 				o[sSettings] = $.extend(
    934 					{}
    935 				,	fx_all						// effects.slide.all
    936 				,	fx_pane						// effects.slide.west
    937 				,	defs.fxSettings || {}		// options.defaults.fxSettings
    938 				,	defs[sSettings] || {}		// options.defaults.fxSettings_open
    939 				,	o.fxSettings				// options.west.fxSettings
    940 				,	o[sSettings]				// options.west.fxSettings_open
    941 				,	opts.defaults.fxSettings	// opts.defaults.fxSettings
    942 				,	opts.defaults[sSettings] || {} // opts.defaults.fxSettings_open
    943 				,	opts[pane].fxSettings		// opts.west.fxSettings
    944 				,	opts[pane][sSettings] || {}	// opts.west.fxSettings_open
    945 				);
    946 				// recalculate fxSpeed according to specificity rules
    947 				o[sSpeed] =
    948 					opts[pane][sSpeed]		// opts.west.fxSpeed_open
    949 				||	opts[pane].fxSpeed		// opts.west.fxSpeed (pane-default)
    950 				||	opts.defaults[sSpeed]	// opts.defaults.fxSpeed_open
    951 				||	opts.defaults.fxSpeed	// opts.defaults.fxSpeed
    952 				||	o[sSpeed]				// options.west.fxSpeed_open
    953 				||	o[sSettings].duration	// options.west.fxSettings_open.duration
    954 				||	o.fxSpeed				// options.west.fxSpeed
    955 				||	o.fxSettings.duration	// options.west.fxSettings.duration
    956 				||	defs.fxSpeed			// options.defaults.fxSpeed
    957 				||	defs.fxSettings.duration// options.defaults.fxSettings.duration
    958 				||	fx_pane.duration		// effects.slide.west.duration
    959 				||	fx_all.duration			// effects.slide.all.duration
    960 				||	"normal"				// DEFAULT
    961 				;
    962 				// DEBUG: if (pane=="east") debugData( $.extend({}, {speed: o[sSpeed], fxSettings_duration: o[sSettings].duration}, o[sSettings]), pane+"."+sName+" = "+fxName );
    963 			});
    964 		});
    965 	};
    966 
    967 	/**
    968 	 * initPanes
    969 	 *
    970 	 * Initialize module objects, styling, size and position for all panes
    971 	 *
    972 	 * @callers  create()
    973 	 */
    974 	var initPanes = function () {
    975 		// NOTE: do north & south FIRST so we can measure their height - do center LAST
    976 		$.each(c.allPanes.split(","), function() {
    977 			var 
    978 				pane	= str(this)
    979 			,	o		= options[pane]
    980 			,	s		= state[pane]
    981 			,	fx		= s.fx
    982 			,	dir		= c[pane].dir
    983 			//	if o.size is not > 0, then we will use MEASURE the pane and use that as it's 'size'
    984 			,	size	= o.size=="auto" || isNaN(o.size) ? 0 : o.size
    985 			,	minSize	= o.minSize || 1
    986 			,	maxSize	= o.maxSize || 9999
    987 			,	spacing	= o.spacing_open || 0
    988 			,	sel		= o.paneSelector
    989 			,	isIE6	= ($.browser.msie && $.browser.version < 7)
    990 			,	CSS		= {}
    991 			,	$P, $C
    992 			;
    993 			$Cs[pane] = false; // init
    994 
    995 			if (sel.substr(0,1)==="#") // ID selector
    996 				// NOTE: elements selected 'by ID' DO NOT have to be 'children'
    997 				$P = $Ps[pane] = $Container.find(sel+":first");
    998 			else { // class or other selector
    999 				$P = $Ps[pane] = $Container.children(sel+":first");
   1000 				// look for the pane nested inside a 'form' element
   1001 				if (!$P.length) $P = $Ps[pane] = $Container.children("form:first").children(sel+":first");
   1002 			}
   1003 
   1004 			if (!$P.length) {
   1005 				$Ps[pane] = false; // logic
   1006 				return true; // SKIP to next
   1007 			}
   1008 
   1009 			// add basic classes & attributes
   1010 			$P
   1011 				.attr("pane", pane) // add pane-identifier
   1012 				.addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
   1013 			;
   1014 
   1015 			// init pane-logic vars, etc.
   1016 			if (pane != "center") {
   1017 				s.isClosed  = false; // true = pane is closed
   1018 				s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
   1019 				s.isResizing= false; // true = pane is in process of being resized
   1020 				s.isHidden	= false; // true = pane is hidden - no spacing, resizer or toggler is visible!
   1021 				s.noRoom	= false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
   1022 				// create special keys for internal use
   1023 				c[pane].pins = [];   // used to track and sync 'pin-buttons' for border-panes
   1024 			}
   1025 
   1026 			CSS = $.extend({ visibility: "visible", display: "block" }, c.defaults.cssReq, c[pane].cssReq );
   1027 			if (o.applyDefaultStyles) $.extend( CSS, c.defaults.cssDef, c[pane].cssDef ); // cosmetic defaults
   1028 			$P.css(CSS); // add base-css BEFORE 'measuring' to calc size & position
   1029 			CSS = {};	// reset var
   1030 
   1031 			// set css-position to account for container borders & padding
   1032 			switch (pane) {
   1033 				case "north": 	CSS.top 	= cDims.top;
   1034 								CSS.left 	= cDims.left;
   1035 								CSS.right	= cDims.right;
   1036 								break;
   1037 				case "south": 	CSS.bottom	= cDims.bottom;
   1038 								CSS.left 	= cDims.left;
   1039 								CSS.right 	= cDims.right;
   1040 								break;
   1041 				case "west": 	CSS.left 	= cDims.left; // top, bottom & height set by sizeMidPanes()
   1042 								break;
   1043 				case "east": 	CSS.right 	= cDims.right; // ditto
   1044 								break;
   1045 				case "center":	// top, left, width & height set by sizeMidPanes()
   1046 			}
   1047 
   1048 			if (dir == "horz") { // north or south pane
   1049 				if (size === 0 || size == "auto") {
   1050 					$P.css({ height: "auto" });
   1051 					size = $P.outerHeight();
   1052 				}
   1053 				size = max(size, minSize);
   1054 				size = min(size, maxSize);
   1055 				size = min(size, cDims.innerHeight - spacing);
   1056 				CSS.height = max(1, cssH(pane, size));
   1057 				s.size = size; // update state
   1058 				// make sure minSize is sufficient to avoid errors
   1059 				s.maxSize = maxSize; // init value
   1060 				s.minSize = max(minSize, size - CSS.height + 1); // = pane.outerHeight when css.height = 1px
   1061 				// handle IE6
   1062 				//if (isIE6) CSS.width = cssW($P, cDims.innerWidth);
   1063 				$P.css(CSS); // apply size & position
   1064 			}
   1065 			else if (dir == "vert") { // east or west pane
   1066 				if (size === 0 || size == "auto") {
   1067 					$P.css({ width: "auto", float: "left" }); // float = FORCE pane to auto-size
   1068 					size = $P.outerWidth();
   1069 					$P.css({ float: "none" }); // RESET
   1070 				}
   1071 				size = max(size, minSize);
   1072 				size = min(size, maxSize);
   1073 				size = min(size, cDims.innerWidth - spacing);
   1074 				CSS.width = max(1, cssW(pane, size));
   1075 				s.size = size; // update state
   1076 				s.maxSize = maxSize; // init value
   1077 				// make sure minSize is sufficient to avoid errors
   1078 				s.minSize = max(minSize, size - CSS.width + 1); // = pane.outerWidth when css.width = 1px
   1079 				$P.css(CSS); // apply size - top, bottom & height set by sizeMidPanes
   1080 				sizeMidPanes(pane, null, true); // true = onInit
   1081 			}
   1082 			else if (pane == "center") {
   1083 				$P.css(CSS); // top, left, width & height set by sizeMidPanes...
   1084 				sizeMidPanes("center", null, true); // true = onInit
   1085 			}
   1086 
   1087 			// close or hide the pane if specified in settings
   1088 			if (o.initClosed && o.closable) {
   1089 				$P.hide().addClass("closed");
   1090 				s.isClosed = true;
   1091 			}
   1092 			else if (o.initHidden || o.initClosed) {
   1093 				hide(pane, true); // will be completely invisible - no resizer or spacing
   1094 				s.isHidden = true;
   1095 			}
   1096 			else
   1097 				$P.addClass("open");
   1098 
   1099 			// check option for auto-handling of pop-ups & drop-downs
   1100 			if (o.showOverflowOnHover)
   1101 				$P.hover( allowOverflow, resetOverflow );
   1102 
   1103 			/*
   1104 			 *	see if this pane has a 'content element' that we need to auto-size
   1105 			 */
   1106 			if (o.contentSelector) {
   1107 				$C = $Cs[pane] = $P.children(o.contentSelector+":first"); // match 1-element only
   1108 				if (!$C.length) {
   1109 					$Cs[pane] = false;
   1110 					return true; // SKIP to next
   1111 				}
   1112 				$C.css( c.content.cssReq );
   1113 				if (o.applyDefaultStyles) $C.css( c.content.cssDef ); // cosmetic defaults
   1114 				// NO PANE-SCROLLING when there is a content-div
   1115 				$P.css({ overflow: "hidden" });
   1116 			}
   1117 		});
   1118 	};
   1119 
   1120 	/**
   1121 	 * initHandles
   1122 	 *
   1123 	 * Initialize module objects, styling, size and position for all resize bars and toggler buttons
   1124 	 *
   1125 	 * @callers  create()
   1126 	 */
   1127 	var initHandles = function () {
   1128 		// create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
   1129 		$.each(c.borderPanes.split(","), function() {
   1130 			var 
   1131 				pane	= str(this)
   1132 			,	o		= options[pane]
   1133 			,	s		= state[pane]
   1134 			,	rClass	= o.resizerClass
   1135 			,	tClass	= o.togglerClass
   1136 			,	$P		= $Ps[pane]
   1137 			;
   1138 			$Rs[pane] = false; // INIT
   1139 			$Ts[pane] = false;
   1140 
   1141 			if (!$P || (!o.closable && !o.resizable)) return; // pane does not exist - skip
   1142 
   1143 			var 
   1144 				edge	= c[pane].edge
   1145 			,	isOpen	= $P.is(":visible")
   1146 			,	spacing	= (isOpen ? o.spacing_open : o.spacing_closed)
   1147 			,	_pane	= "-"+ pane // used for classNames
   1148 			,	_state	= (isOpen ? "-open" : "-closed") // used for classNames
   1149 			,	$R, $T
   1150 			;
   1151 			// INIT RESIZER BAR
   1152 			$R = $Rs[pane] = $("<span></span>");
   1153 	
   1154 			if (isOpen && o.resizable)
   1155 				; // this is handled by initResizable
   1156 			else if (!isOpen && o.slidable)
   1157 				$R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
   1158 	
   1159 			$R
   1160 				// if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
   1161 				.attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
   1162 				.attr("resizer", pane) // so we can read this from the resizer
   1163 				.css(c.resizers.cssReq) // add base/required styles
   1164 				// POSITION of resizer bar - allow for container border & padding
   1165 				.css(edge, cDims[edge] + getPaneSize(pane))
   1166 				// ADD CLASSNAMES - eg: class="resizer resizer-west resizer-open"
   1167 				.addClass( rClass +" "+ rClass+_pane +" "+ rClass+_state +" "+ rClass+_pane+_state )
   1168 				.appendTo($Container) // append DIV to container
   1169 			;
   1170 			 // ADD VISUAL STYLES
   1171 			if (o.applyDefaultStyles)
   1172 				$R.css(c.resizers.cssDef);
   1173 
   1174 			if (o.closable) {
   1175 				// INIT COLLAPSER BUTTON
   1176 				$T = $Ts[pane] = $("<div></div>");
   1177 				$T
   1178 					// if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-toggler"
   1179 					.attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
   1180 					.css(c.togglers.cssReq) // add base/required styles
   1181 					.attr("title", (isOpen ? o.togglerTip_open : o.togglerTip_closed))
   1182 					.click(function(evt){ toggle(pane); evt.stopPropagation(); })
   1183 					.mouseover(function(evt){ evt.stopPropagation(); }) // prevent resizer event
   1184 					// ADD CLASSNAMES - eg: class="toggler toggler-west toggler-west-open"
   1185 					.addClass( tClass +" "+ tClass+_pane +" "+ tClass+_state +" "+ tClass+_pane+_state )
   1186 					.appendTo($R) // append SPAN to resizer DIV
   1187 				;
   1188 
   1189 				// ADD INNER-SPANS TO TOGGLER
   1190 				if (o.togglerContent_open) // ui-layout-open
   1191 					$("<span>"+ o.togglerContent_open +"</span>")
   1192 						.addClass("content content-open")
   1193 						.css("display", s.isClosed ? "none" : "block")
   1194 						.appendTo( $T )
   1195 					;
   1196 				if (o.togglerContent_closed) // ui-layout-closed
   1197 					$("<span>"+ o.togglerContent_closed +"</span>")
   1198 						.addClass("content content-closed")
   1199 						.css("display", s.isClosed ? "block" : "none")
   1200 						.appendTo( $T )
   1201 					;
   1202 
   1203 				 // ADD BASIC VISUAL STYLES
   1204 				if (o.applyDefaultStyles)
   1205 					$T.css(c.togglers.cssDef);
   1206 
   1207 				if (!isOpen) bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
   1208 			}
   1209 
   1210 		});
   1211 
   1212 		// SET ALL HANDLE SIZES & LENGTHS
   1213 		sizeHandles("all", true); // true = onInit
   1214 	};
   1215 
   1216 	/**
   1217 	 * initResizable
   1218 	 *
   1219 	 * Add resize-bars to all panes that specify it in options
   1220 	 *
   1221 	 * @dependancies  $.fn.resizable - will abort if not found
   1222 	 * @callers  create()
   1223 	 */
   1224 	var initResizable = function () {
   1225 		var
   1226 			draggingAvailable = (typeof $.fn.draggable == "function")
   1227 		,	minPosition, maxPosition, edge // set in start()
   1228 		;
   1229 
   1230 		$.each(c.borderPanes.split(","), function() {
   1231 			var 
   1232 				pane	= str(this)
   1233 			,	o		= options[pane]
   1234 			,	s		= state[pane]
   1235 			;
   1236 			if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
   1237 				o.resizable = false;
   1238 				return true; // skip to next
   1239 			}
   1240 
   1241 			var 
   1242 				rClass				= o.resizerClass
   1243 			//	'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
   1244 			,	dragClass			= rClass+"-drag"			// resizer-drag
   1245 			,	dragPaneClass		= rClass+"-"+pane+"-drag"	// resizer-north-drag
   1246 			//	'dragging' class is applied to the CLONED resizer-bar while it is being dragged
   1247 			,	draggingClass		= rClass+"-dragging"		// resizer-dragging
   1248 			,	draggingPaneClass	= rClass+"-"+pane+"-dragging" // resizer-north-dragging
   1249 			,	draggingClassSet	= false 					// logic var
   1250 			,	$P 					= $Ps[pane]
   1251 			,	$R					= $Rs[pane]
   1252 			;
   1253 
   1254 			if (!s.isClosed)
   1255 				$R
   1256 					.attr("title", o.resizerTip)
   1257 					.css("cursor", o.resizerCursor) // n-resize, s-resize, etc
   1258 				;
   1259 
   1260 			$R.draggable({
   1261 				containment:	$Container[0] // limit resizing to layout container
   1262 			,	axis:			(c[pane].dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
   1263 			,	delay:			200
   1264 			,	distance:		1
   1265 			//	basic format for helper - style it using class: .ui-draggable-dragging
   1266 			,	helper:			"clone"
   1267 			,	opacity:		o.resizerDragOpacity
   1268 			//,	iframeFix:		o.draggableIframeFix // TODO: consider using when bug is fixed
   1269 			,	zIndex:			c.zIndex.resizing
   1270 
   1271 			,	start: function (e, ui) {
   1272 					// onresize_start callback - will CANCEL hide if returns false
   1273 					// TODO: CONFIRM that dragging can be cancelled like this???
   1274 					if (false === execUserCallback(pane, o.onresize_start)) return false;
   1275 
   1276 					s.isResizing = true; // prevent pane from closing while resizing
   1277 					clearTimer(pane, "closeSlider"); // just in case already triggered
   1278 
   1279 					$R.addClass( dragClass +" "+ dragPaneClass ); // add drag classes
   1280 					draggingClassSet = false; // reset logic var - see drag()
   1281 
   1282 					// SET RESIZING LIMITS - used in drag()
   1283 					var resizerWidth = (pane=="east" || pane=="south" ? o.spacing_open : 0);
   1284 					setPaneMinMaxSizes(pane); // update pane-state
   1285 					s.minPosition -= resizerWidth;
   1286 					s.maxPosition -= resizerWidth;
   1287 					edge = (c[pane].dir=="horz" ? "top" : "left");
   1288 
   1289 					// MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
   1290 					$(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).each(function() {					
   1291 						$('<div class="ui-layout-mask"/>')
   1292 							.css({
   1293 								background:	"#fff"
   1294 							,	opacity:	"0.001"
   1295 							,	zIndex:		9
   1296 							,	position:	"absolute"
   1297 							,	width:		this.offsetWidth+"px"
   1298 							,	height:		this.offsetHeight+"px"
   1299 							})
   1300 							.css($(this).offset()) // top & left
   1301 							.appendTo(this.parentNode) // put div INSIDE pane to avoid zIndex issues
   1302 						;
   1303 					});
   1304 				}
   1305 
   1306 			,	drag: function (e, ui) {
   1307 					if (!draggingClassSet) { // can only add classes after clone has been added to the DOM
   1308 						$(".ui-draggable-dragging")
   1309 							.addClass( draggingClass +" "+ draggingPaneClass ) // add dragging classes
   1310 							.children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
   1311 						;
   1312 						draggingClassSet = true;
   1313 						// draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
   1314 						if (s.isSliding) $Ps[pane].css("zIndex", c.zIndex.sliding);
   1315 					}
   1316 					// CONTAIN RESIZER-BAR TO RESIZING LIMITS
   1317 					if		(ui.position[edge] < s.minPosition) ui.position[edge] = s.minPosition;
   1318 					else if (ui.position[edge] > s.maxPosition) ui.position[edge] = s.maxPosition;
   1319 				}
   1320 
   1321 			,	stop: function (e, ui) {
   1322 					var 
   1323 						dragPos	= ui.position
   1324 					,	resizerPos
   1325 					,	newSize
   1326 					;
   1327 					$R.removeClass( dragClass +" "+ dragPaneClass ); // remove drag classes
   1328 	
   1329 					switch (pane) {
   1330 						case "north":	resizerPos = dragPos.top; break;
   1331 						case "west":	resizerPos = dragPos.left; break;
   1332 						case "south":	resizerPos = cDims.outerHeight - dragPos.top - $R.outerHeight(); break;
   1333 						case "east":	resizerPos = cDims.outerWidth - dragPos.left - $R.outerWidth(); break;
   1334 					}
   1335 					// remove container margin from resizer position to get the pane size
   1336 					newSize = resizerPos - cDims[ c[pane].edge ];
   1337 
   1338 					sizePane(pane, newSize);
   1339 
   1340 					// UN-MASK PANES MASKED IN drag.start
   1341 					$("div.ui-layout-mask").remove(); // Remove iframe masks	
   1342 
   1343 					s.isResizing = false;
   1344 				}
   1345 
   1346 			});
   1347 		});
   1348 	};
   1349 
   1350 
   1351 
   1352 /*
   1353  * ###########################
   1354  *       ACTION METHODS
   1355  * ###########################
   1356  */
   1357 
   1358 	/**
   1359 	 * hide / show
   1360 	 *
   1361 	 * Completely 'hides' a pane, including its spacing - as if it does not exist
   1362 	 * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
   1363 	 *
   1364 	 * @param String  pane   The pane being hidden, ie: north, south, east, or west
   1365 	 */
   1366 	var hide = function (pane, onInit) {
   1367 		var
   1368 			o	= options[pane]
   1369 		,	s	= state[pane]
   1370 		,	$P	= $Ps[pane]
   1371 		,	$R	= $Rs[pane]
   1372 		;
   1373 		if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
   1374 
   1375 		// onhide_start callback - will CANCEL hide if returns false
   1376 		if (false === execUserCallback(pane, o.onhide_start)) return;
   1377 
   1378 		s.isSliding = false; // just in case
   1379 
   1380 		// now hide the elements
   1381 		if ($R) $R.hide(); // hide resizer-bar
   1382 		if (onInit || s.isClosed) {
   1383 			s.isClosed = true; // to trigger open-animation on show()
   1384 			s.isHidden  = true;
   1385 			$P.hide(); // no animation when loading page
   1386 			sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
   1387 			execUserCallback(pane, o.onhide_end || o.onhide);
   1388 		}
   1389 		else {
   1390 			s.isHiding = true; // used by onclose
   1391 			close(pane, false); // adjust all panes to fit
   1392 			//s.isHidden  = true; - will be set by close - if not cancelled
   1393 		}
   1394 	};
   1395 
   1396 	var show = function (pane, openPane) {
   1397 		var
   1398 			o	= options[pane]
   1399 		,	s	= state[pane]
   1400 		,	$P	= $Ps[pane]
   1401 		,	$R	= $Rs[pane]
   1402 		;
   1403 		if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
   1404 
   1405 		// onhide_start callback - will CANCEL hide if returns false
   1406 		if (false === execUserCallback(pane, o.onshow_start)) return;
   1407 
   1408 		s.isSliding = false; // just in case
   1409 		s.isShowing = true; // used by onopen/onclose
   1410 		//s.isHidden  = false; - will be set by open/close - if not cancelled
   1411 
   1412 		// now show the elements
   1413 		if ($R && o.spacing_open > 0) $R.show();
   1414 		if (openPane === false)
   1415 			close(pane, true); // true = force
   1416 		else
   1417 			open(pane); // adjust all panes to fit
   1418 	};
   1419 
   1420 
   1421 	/**
   1422 	 * toggle
   1423 	 *
   1424 	 * Toggles a pane open/closed by calling either open or close
   1425 	 *
   1426 	 * @param String  pane   The pane being toggled, ie: north, south, east, or west
   1427 	 */
   1428 	var toggle = function (pane) {
   1429 		var s = state[pane];
   1430 		if (s.isHidden)
   1431 			show(pane); // will call 'open' after unhiding it
   1432 		else if (s.isClosed)
   1433 			open(pane);
   1434 		else
   1435 			close(pane);
   1436 	};
   1437 
   1438 	/**
   1439 	 * close
   1440 	 *
   1441 	 * Close the specified pane (animation optional), and resize all other panes as needed
   1442 	 *
   1443 	 * @param String  pane   The pane being closed, ie: north, south, east, or west
   1444 	 */
   1445 	var close = function (pane, force, noAnimation) {
   1446 		var 
   1447 			$P		= $Ps[pane]
   1448 		,	$R		= $Rs[pane]
   1449 		,	$T		= $Ts[pane]
   1450 		,	o		= options[pane]
   1451 		,	s		= state[pane]
   1452 		,	doFX	= !noAnimation && !s.isClosed && (o.fxName_close != "none")
   1453 		,	edge	= c[pane].edge
   1454 		,	rClass	= o.resizerClass
   1455 		,	tClass	= o.togglerClass
   1456 		,	_pane	= "-"+ pane // used for classNames
   1457 		,	_open	= "-open"
   1458 		,	_sliding= "-sliding"
   1459 		,	_closed	= "-closed"
   1460 		// 	transfer logic vars to temp vars
   1461 		,	isShowing = s.isShowing
   1462 		,	isHiding = s.isHiding
   1463 		;
   1464 		// now clear the logic vars
   1465 		delete s.isShowing;
   1466 		delete s.isHiding;
   1467 
   1468 		if (!$P || (!o.resizable && !o.closable)) return; // invalid request
   1469 		else if (!force && s.isClosed && !isShowing) return; // already closed
   1470 
   1471 		if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
   1472 			setFlowCallback("close", pane, force); // set a callback for this action, if possible
   1473 			return; // ABORT 
   1474 		}
   1475 
   1476 		// onclose_start callback - will CANCEL hide if returns false
   1477 		// SKIP if just 'showing' a hidden pane as 'closed'
   1478 		if (!isShowing && false === execUserCallback(pane, o.onclose_start)) return;
   1479 
   1480 		// SET flow-control flags
   1481 		c[pane].isMoving = true;
   1482 		c.isLayoutBusy = true;
   1483 
   1484 		s.isClosed = true;
   1485 		// update isHidden BEFORE sizing panes
   1486 		if (isHiding) s.isHidden = true;
   1487 		else if (isShowing) s.isHidden = false;
   1488 
   1489 		// sync any 'pin buttons'
   1490 		syncPinBtns(pane, false);
   1491 
   1492 		// resize panes adjacent to this one
   1493 		if (!s.isSliding) sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
   1494 
   1495 		// if this pane has a resizer bar, move it now
   1496 		if ($R) {
   1497 			$R
   1498 				.css(edge, cDims[edge]) // move the resizer bar
   1499 				.removeClass( rClass+_open +" "+ rClass+_pane+_open )
   1500 				.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
   1501 				.addClass( rClass+_closed +" "+ rClass+_pane+_closed )
   1502 			;
   1503 			// DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent
   1504 			if (o.resizable)
   1505 				$R
   1506 					.draggable("disable")
   1507 					.css("cursor", "default")
   1508 					.attr("title","")
   1509 				;
   1510 			// if pane has a toggler button, adjust that too
   1511 			if ($T) {
   1512 				$T
   1513 					.removeClass( tClass+_open +" "+ tClass+_pane+_open )
   1514 					.addClass( tClass+_closed +" "+ tClass+_pane+_closed )
   1515 					.attr("title", o.togglerTip_closed) // may be blank
   1516 				;
   1517 			}
   1518 			sizeHandles(); // resize 'length' and position togglers for adjacent panes
   1519 		}
   1520 
   1521 		// ANIMATE 'CLOSE' - if no animation, then was ALREADY shown above
   1522 		if (doFX) {
   1523 			lockPaneForFX(pane, true); // need to set left/top so animation will work
   1524 			$P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
   1525 				lockPaneForFX(pane, false); // undo
   1526 				if (!s.isClosed) return; // pane was opened before animation finished!
   1527 				close_2();
   1528 			});
   1529 		}
   1530 		else {
   1531 			$P.hide(); // just hide pane NOW
   1532 			close_2();
   1533 		}
   1534 
   1535 		// SUBROUTINE
   1536 		function close_2 () {
   1537 			bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
   1538 
   1539 			// onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
   1540 			if (!isShowing)	execUserCallback(pane, o.onclose_end || o.onclose);
   1541 			// onhide OR onshow callback
   1542 			if (isShowing)	execUserCallback(pane, o.onshow_end || o.onshow);
   1543 			if (isHiding)	execUserCallback(pane, o.onhide_end || o.onhide);
   1544 
   1545 			// internal flow-control callback
   1546 			execFlowCallback(pane);
   1547 		}
   1548 	};
   1549 
   1550 	/**
   1551 	 * open
   1552 	 *
   1553 	 * Open the specified pane (animation optional), and resize all other panes as needed
   1554 	 *
   1555 	 * @param String  pane   The pane being opened, ie: north, south, east, or west
   1556 	 */
   1557 	var open = function (pane, slide, noAnimation) {
   1558 		var 
   1559 			$P		= $Ps[pane]
   1560 		,	$R		= $Rs[pane]
   1561 		,	$T		= $Ts[pane]
   1562 		,	o		= options[pane]
   1563 		,	s		= state[pane]
   1564 		,	doFX	= !noAnimation && s.isClosed && (o.fxName_open != "none")
   1565 		,	edge	= c[pane].edge
   1566 		,	rClass	= o.resizerClass
   1567 		,	tClass	= o.togglerClass
   1568 		,	_pane	= "-"+ pane // used for classNames
   1569 		,	_open	= "-open"
   1570 		,	_closed	= "-closed"
   1571 		,	_sliding= "-sliding"
   1572 		// 	transfer logic var to temp var
   1573 		,	isShowing = s.isShowing
   1574 		;
   1575 		// now clear the logic var
   1576 		delete s.isShowing;
   1577 
   1578 		if (!$P || (!o.resizable && !o.closable)) return; // invalid request
   1579 		else if (!s.isClosed && !s.isSliding) return; // already open
   1580 
   1581 		// pane can ALSO be unhidden by just calling show(), so handle this scenario
   1582 		if (s.isHidden && !isShowing) {
   1583 			show(pane, true);
   1584 			return;
   1585 		}
   1586 
   1587 		if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
   1588 			setFlowCallback("open", pane, slide); // set a callback for this action, if possible
   1589 			return; // ABORT
   1590 		}
   1591 
   1592 		// onopen_start callback - will CANCEL hide if returns false
   1593 		if (false === execUserCallback(pane, o.onopen_start)) return;
   1594 
   1595 		// SET flow-control flags
   1596 		c[pane].isMoving = true;
   1597 		c.isLayoutBusy = true;
   1598 
   1599 		// 'PIN PANE' - stop sliding
   1600 		if (s.isSliding && !slide) // !slide = 'open pane normally' - NOT sliding
   1601 			bindStopSlidingEvents(pane, false); // will set isSliding=false
   1602 
   1603 		s.isClosed = false;
   1604 		// update isHidden BEFORE sizing panes
   1605 		if (isShowing) s.isHidden = false;
   1606 
   1607 		// Container size may have changed - shrink the pane if now 'too big'
   1608 		setPaneMinMaxSizes(pane); // update pane-state
   1609 		if (s.size > s.maxSize) // pane is too big! resize it before opening
   1610 			$P.css( c[pane].sizeType, max(1, cssSize(pane, s.maxSize)) );
   1611 
   1612 		bindStartSlidingEvent(pane, false); // remove trigger event from resizer-bar
   1613 
   1614 		if (doFX) { // ANIMATE
   1615 			lockPaneForFX(pane, true); // need to set left/top so animation will work
   1616 			$P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
   1617 				lockPaneForFX(pane, false); // undo
   1618 				if (s.isClosed) return; // pane was closed before animation finished!
   1619 				open_2(); // continue
   1620 			});
   1621 		}
   1622 		else {// no animation
   1623 			$P.show();	// just show pane and...
   1624 			open_2();	// continue
   1625 		}
   1626 
   1627 		// SUBROUTINE
   1628 		function open_2 () {
   1629 			// NOTE: if isSliding, then other panes are NOT 'resized'
   1630 			if (!s.isSliding) // resize all panes adjacent to this one
   1631 				sizeMidPanes(c[pane].dir=="vert" ? "center" : "all");
   1632 
   1633 			// if this pane has a toggler, move it now
   1634 			if ($R) {
   1635 				$R
   1636 					.css(edge, cDims[edge] + getPaneSize(pane)) // move the toggler
   1637 					.removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
   1638 					.addClass( rClass+_open +" "+ rClass+_pane+_open )
   1639 					.addClass( !s.isSliding ? "" : rClass+_sliding +" "+ rClass+_pane+_sliding )
   1640 				;
   1641 				if (o.resizable)
   1642 					$R
   1643 						.draggable("enable")
   1644 						.css("cursor", o.resizerCursor)
   1645 						.attr("title", o.resizerTip)
   1646 					;
   1647 				else
   1648 					$R.css("cursor", "default"); // n-resize, s-resize, etc
   1649 				// if pane also has a toggler button, adjust that too
   1650 				if ($T) {
   1651 					$T
   1652 						.removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
   1653 						.addClass( tClass+_open +" "+ tClass+_pane+_open )
   1654 						.attr("title", o.togglerTip_open) // may be blank
   1655 					;
   1656 				}
   1657 				sizeHandles("all"); // resize resizer & toggler sizes for all panes
   1658 			}
   1659 
   1660 			// resize content every time pane opens - to be sure
   1661 			sizeContent(pane);
   1662 
   1663 			// sync any 'pin buttons'
   1664 			syncPinBtns(pane, !s.isSliding);
   1665 
   1666 			// onopen callback
   1667 			execUserCallback(pane, o.onopen_end || o.onopen);
   1668 
   1669 			// onshow callback
   1670 			if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
   1671 
   1672 			// internal flow-control callback
   1673 			execFlowCallback(pane);
   1674 		}
   1675 	};
   1676 	
   1677 
   1678 	/**
   1679 	 * lockPaneForFX
   1680 	 *
   1681 	 * Must set left/top on East/South panes so animation will work properly
   1682 	 *
   1683 	 * @param String  pane  The pane to lock, 'east' or 'south' - any other is ignored!
   1684 	 * @param Boolean  doLock  true = set left/top, false = remove
   1685 	 */
   1686 	var lockPaneForFX = function (pane, doLock) {
   1687 		var $P = $Ps[pane];
   1688 		if (doLock) {
   1689 			$P.css({ zIndex: c.zIndex.animation }); // overlay all elements during animation
   1690 			if (pane=="south")
   1691 				$P.css({ top: cDims.top + cDims.innerHeight - $P.outerHeight() });
   1692 			else if (pane=="east")
   1693 				$P.css({ left: cDims.left + cDims.innerWidth - $P.outerWidth() });
   1694 		}
   1695 		else {
   1696 			if (!state[pane].isSliding) $P.css({ zIndex: c.zIndex.pane_normal });
   1697 			if (pane=="south")
   1698 				$P.css({ top: "auto" });
   1699 			else if (pane=="east")
   1700 				$P.css({ left: "auto" });
   1701 		}
   1702 	};
   1703 
   1704 
   1705 	/**
   1706 	 * bindStartSlidingEvent
   1707 	 *
   1708 	 * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
   1709 	 *
   1710 	 * @callers  open(), close()
   1711 	 * @param String  pane  The pane to enable/disable, 'north', 'south', etc.
   1712 	 * @param Boolean  enable  Enable or Disable sliding?
   1713 	 */
   1714 	var bindStartSlidingEvent = function (pane, enable) {
   1715 		var 
   1716 			o		= options[pane]
   1717 		,	$R		= $Rs[pane]
   1718 		,	trigger	= o.slideTrigger_open
   1719 		;
   1720 		if (!$R || !o.slidable) return;
   1721 		// make sure we have a valid event
   1722 		if (trigger != "click" && trigger != "dblclick" && trigger != "mouseover") trigger = "click";
   1723 		$R
   1724 			// add or remove trigger event
   1725 			[enable ? "bind" : "unbind"](trigger, slideOpen)
   1726 			// set the appropriate cursor & title/tip
   1727 			.css("cursor", (enable ? o.sliderCursor: "default"))
   1728 			.attr("title", (enable ? o.sliderTip : ""))
   1729 		;
   1730 	};
   1731 
   1732 	/**
   1733 	 * bindStopSlidingEvents
   1734 	 *
   1735 	 * Add or remove 'mouseout' events to 'slide close' when pane is 'sliding' open or closed
   1736 	 * Also increases zIndex when pane is sliding open
   1737 	 * See bindStartSlidingEvent for code to control 'slide open'
   1738 	 *
   1739 	 * @callers  slideOpen(), slideClosed()
   1740 	 * @param String  pane  The pane to process, 'north', 'south', etc.
   1741 	 * @param Boolean  isOpen  Is pane open or closed?
   1742 	 */
   1743 	var bindStopSlidingEvents = function (pane, enable) {
   1744 		var 
   1745 			o		= options[pane]
   1746 		,	s		= state[pane]
   1747 		,	trigger	= o.slideTrigger_close
   1748 		,	action	= (enable ? "bind" : "unbind") // can't make 'unbind' work! - see disabled code below
   1749 		,	$P		= $Ps[pane]
   1750 		,	$R		= $Rs[pane]
   1751 		;
   1752 
   1753 		s.isSliding = enable; // logic
   1754 		clearTimer(pane, "closeSlider"); // just in case
   1755 
   1756 		// raise z-index when sliding
   1757 		$P.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.pane_normal) });
   1758 		$R.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.resizer_normal) });
   1759 
   1760 		// make sure we have a valid event
   1761 		if (trigger != "click" && trigger != "mouseout") trigger = "mouseout";
   1762 
   1763 		// when trigger is 'mouseout', must cancel timer when mouse moves between 'pane' and 'resizer'
   1764 		if (enable) { // BIND trigger events
   1765 			$P.bind(trigger, slideClosed );
   1766 			$R.bind(trigger, slideClosed );
   1767 			if (trigger = "mouseout") {
   1768 				$P.bind("mouseover", cancelMouseOut );
   1769 				$R.bind("mouseover", cancelMouseOut );
   1770 			}
   1771 		}
   1772 		else { // UNBIND trigger events
   1773 			// TODO: why does unbind of a 'single function' not work reliably?
   1774 			//$P[action](trigger, slideClosed );
   1775 			$P.unbind(trigger);
   1776 			$R.unbind(trigger);
   1777 			if (trigger = "mouseout") {
   1778 				//$P[action]("mouseover", cancelMouseOut );
   1779 				$P.unbind("mouseover");
   1780 				$R.unbind("mouseover");
   1781 				clearTimer(pane, "closeSlider");
   1782 			}
   1783 		}
   1784 
   1785 		// SUBROUTINE for mouseout timer clearing
   1786 		function cancelMouseOut (evt) {
   1787 			clearTimer(pane, "closeSlider");
   1788 			evt.stopPropagation();
   1789 		}
   1790 	};
   1791 
   1792 	var slideOpen = function () {
   1793 		var pane = $(this).attr("resizer"); // attr added by initHandles
   1794 		if (state[pane].isClosed) { // skip if already open!
   1795 			bindStopSlidingEvents(pane, true); // pane is opening, so BIND trigger events to close it
   1796 			open(pane, true); // true = slide - ie, called from here!
   1797 		}
   1798 	};
   1799 
   1800 	var slideClosed = function () {
   1801 		var
   1802 			$E = $(this)
   1803 		,	pane = $E.attr("pane") || $E.attr("resizer")
   1804 		,	o = options[pane]
   1805 		,	s = state[pane]
   1806 		;
   1807 		if (s.isClosed || s.isResizing)
   1808 			return; // skip if already closed OR in process of resizing
   1809 		else if (o.slideTrigger_close == "click")
   1810 			close_NOW(); // close immediately onClick
   1811 		else // trigger = mouseout - use a delay
   1812 			setTimer(pane, "closeSlider", close_NOW, 300); // .3 sec delay
   1813 
   1814 		// SUBROUTINE for timed close
   1815 		function close_NOW () {
   1816 			bindStopSlidingEvents(pane, false); // pane is being closed, so UNBIND trigger events
   1817 			if (!s.isClosed) close(pane); // skip if already closed!
   1818 		}
   1819 	};
   1820 
   1821 
   1822 	/**
   1823 	 * sizePane
   1824 	 *
   1825 	 * @callers  initResizable.stop()
   1826 	 * @param String  pane   The pane being resized - usually west or east, but potentially north or south
   1827 	 * @param Integer  newSize  The new size for this pane - will be validated
   1828 	 */
   1829 	var sizePane = function (pane, size) {
   1830 		// TODO: accept "auto" as size, and size-to-fit pane content
   1831 		var 
   1832 			edge	= c[pane].edge
   1833 		,	dir		= c[pane].dir
   1834 		,	o		= options[pane]
   1835 		,	s		= state[pane]
   1836 		,	$P		= $Ps[pane]
   1837 		,	$R		= $Rs[pane]
   1838 		;
   1839 		// calculate 'current' min/max sizes
   1840 		setPaneMinMaxSizes(pane); // update pane-state
   1841 		// compare/update calculated min/max to user-options
   1842 		s.minSize = max(s.minSize, o.minSize);
   1843 		if (o.maxSize > 0) s.maxSize = min(s.maxSize, o.maxSize);
   1844 		// validate passed size
   1845 		size = max(size, s.minSize);
   1846 		size = min(size, s.maxSize);
   1847 		s.size = size; // update state
   1848 
   1849 		// move the resizer bar and resize the pane
   1850 		$R.css( edge, size + cDims[edge] );
   1851 		$P.css( c[pane].sizeType, max(1, cssSize(pane, size)) );
   1852 
   1853 		// resize all the adjacent panes, and adjust their toggler buttons
   1854 		if (!s.isSliding) sizeMidPanes(dir=="horz" ? "all" : "center");
   1855 		sizeHandles();
   1856 		sizeContent(pane);
   1857 		execUserCallback(pane, o.onresize_end || o.onresize);
   1858 	};
   1859 
   1860 	/**
   1861 	 * sizeMidPanes
   1862 	 *
   1863 	 * @callers  create(), open(), close(), onWindowResize()
   1864 	 */
   1865 	var sizeMidPanes = function (panes, overrideDims, onInit) {
   1866 		if (!panes || panes == "all") panes = "east,west,center";
   1867 
   1868 		var d = getPaneDims();
   1869 		if (overrideDims) $.extend( d, overrideDims );
   1870 
   1871 		$.each(panes.split(","), function() {
   1872 			if (!$Ps[this]) return; // NO PANE - skip
   1873 			var 
   1874 				pane	= str(this)
   1875 			,	o		= options[pane]
   1876 			,	s		= state[pane]
   1877 			,	$P		= $Ps[pane]
   1878 			,	$R		= $Rs[pane]
   1879 			,	hasRoom	= true
   1880 			,	CSS		= {}
   1881 			;
   1882 
   1883 			if (pane == "center") {
   1884 				d = getPaneDims(); // REFRESH Dims because may have just 'unhidden' East or West pane after a 'resize'
   1885 				CSS = $.extend( {}, d ); // COPY ALL of the paneDims
   1886 				CSS.width  = max(1, cssW(pane, CSS.width));
   1887 				CSS.height = max(1, cssH(pane, CSS.height));
   1888 				hasRoom = (CSS.width > 1 && CSS.height > 1);
   1889 				/*
   1890 				 * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
   1891 				 * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
   1892 				 */
   1893 				if ($.browser.msie && (!$.boxModel || $.browser.version < 7)) {
   1894 					if ($Ps.north) $Ps.north.css({ width: cssW($Ps.north, cDims.innerWidth) });
   1895 					if ($Ps.south) $Ps.south.css({ width: cssW($Ps.south, cDims.innerWidth) });
   1896 				}
   1897 			}
   1898 			else { // for east and west, set only the height
   1899 				CSS.top = d.top;
   1900 				CSS.bottom = d.bottom;
   1901 				CSS.height = max(1, cssH(pane, d.height));
   1902 				hasRoom = (CSS.height > 1);
   1903 			}
   1904 
   1905 			if (hasRoom) {
   1906 				$P.css(CSS);
   1907 				if (s.noRoom) {
   1908 					s.noRoom = false;
   1909 					if (s.isHidden) return;
   1910 					else show(pane, !s.isClosed);
   1911 					/* OLD CODE - keep until sure line above works right!
   1912 					if (!s.isClosed) $P.show(); // in case was previously hidden due to NOT hasRoom
   1913 					if ($R) $R.show();
   1914 					*/
   1915 				}
   1916 				if (!onInit) {
   1917 					sizeContent(pane);
   1918 					execUserCallback(pane, o.onresize_end || o.onresize);
   1919 				}
   1920 			}
   1921 			else if (!s.noRoom) { // no room for pane, so just hide it (if not already)
   1922 				s.noRoom = true; // update state
   1923 				if (s.isHidden) return;
   1924 				if (onInit) { // skip onhide callback and other logic onLoad
   1925 					$P.hide();
   1926 					if ($R) $R.hide();
   1927 				}
   1928 				else hide(pane);
   1929 			}
   1930 		});
   1931 	};
   1932 
   1933 
   1934 	var sizeContent = function (panes) {
   1935 		if (!panes || panes == "all") panes = c.allPanes;
   1936 
   1937 		$.each(panes.split(","), function() {
   1938 			if (!$Cs[this]) return; // NO CONTENT - skip
   1939 			var 
   1940 				pane	= str(this)
   1941 			,	ignore	= options[pane].contentIgnoreSelector
   1942 			,	$P		= $Ps[pane]
   1943 			,	$C		= $Cs[pane]
   1944 			,	e_C		= $C[0]		// DOM element
   1945 			,	height	= cssH($P);	// init to pane.innerHeight
   1946 			;
   1947 			$P.children().each(function() {
   1948 				if (this == e_C) return; // Content elem - skip
   1949 				var $E = $(this);
   1950 				if (!ignore || !$E.is(ignore))
   1951 					height -= $E.outerHeight();
   1952 			});
   1953 			if (height > 0)
   1954 				height = cssH($C, height);
   1955 			if (height < 1)
   1956 				$C.hide(); // no room for content!
   1957 			else
   1958 				$C.css({ height: height }).show();
   1959 		});
   1960 	};
   1961 
   1962 
   1963 	/**
   1964 	 * sizeHandles
   1965 	 *
   1966 	 * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
   1967 	 *
   1968 	 * @callers  initHandles(), open(), close(), resizeAll()
   1969 	 */
   1970 	var sizeHandles = function (panes, onInit) {
   1971 		if (!panes || panes == "all") panes = c.borderPanes;
   1972 
   1973 		$.each(panes.split(","), function() {
   1974 			var 
   1975 				pane	= str(this)
   1976 			,	o		= options[pane]
   1977 			,	s		= state[pane]
   1978 			,	$P		= $Ps[pane]
   1979 			,	$R		= $Rs[pane]
   1980 			,	$T		= $Ts[pane]
   1981 			;
   1982 			if (!$P || !$R || (!o.resizable && !o.closable)) return; // skip
   1983 
   1984 			var 
   1985 				dir			= c[pane].dir
   1986 			,	_state		= (s.isClosed ? "_closed" : "_open")
   1987 			,	spacing		= o["spacing"+ _state]
   1988 			,	togAlign	= o["togglerAlign"+ _state]
   1989 			,	togLen		= o["togglerLength"+ _state]
   1990 			,	paneLen
   1991 			,	offset
   1992 			,	CSS = {}
   1993 			;
   1994 			if (spacing == 0) {
   1995 				$R.hide();
   1996 				return;
   1997 			}
   1998 			else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
   1999 				$R.show(); // in case was previously hidden
   2000 
   2001 			// Resizer Bar is ALWAYS same width/height of pane it is attached to
   2002 			if (dir == "horz") { // north/south
   2003 				paneLen = $P.outerWidth();
   2004 				$R.css({
   2005 					width:	max(1, cssW($R, paneLen)) // account for borders & padding
   2006 				,	height:	max(1, cssH($R, spacing)) // ditto
   2007 				,	left:	cssNum($P, "left")
   2008 				});
   2009 			}
   2010 			else { // east/west
   2011 				paneLen = $P.outerHeight();
   2012 				$R.css({
   2013 					height:	max(1, cssH($R, paneLen)) // account for borders & padding
   2014 				,	width:	max(1, cssW($R, spacing)) // ditto
   2015 				,	top:	cDims.top + getPaneSize("north", true)
   2016 				//,	top:	cssNum($Ps["center"], "top")
   2017 				});
   2018 				
   2019 			}
   2020 
   2021 			if ($T) {
   2022 				if (togLen == 0 || (s.isSliding && o.hideTogglerOnSlide)) {
   2023 					$T.hide(); // always HIDE the toggler when 'sliding'
   2024 					return;
   2025 				}
   2026 				else
   2027 					$T.show(); // in case was previously hidden
   2028 
   2029 				if (!(togLen > 0) || togLen == "100%" || togLen > paneLen) {
   2030 					togLen = paneLen;
   2031 					offset = 0;
   2032 				}
   2033 				else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
   2034 					if (typeof togAlign == "string") {
   2035 						switch (togAlign) {
   2036 							case "top":
   2037 							case "left":	offset = 0;
   2038 											break;
   2039 							case "bottom":
   2040 							case "right":	offset = paneLen - togLen;
   2041 											break;
   2042 							case "middle":
   2043 							case "center":
   2044 							default:		offset = Math.floor((paneLen - togLen) / 2); // 'default' catches typos
   2045 						}
   2046 					}
   2047 					else { // togAlign = number
   2048 						var x = parseInt(togAlign); //
   2049 						if (togAlign >= 0) offset = x;
   2050 						else offset = paneLen - togLen + x; // NOTE: x is negative!
   2051 					}
   2052 				}
   2053 
   2054 				var
   2055 					$TC_o = (o.togglerContent_open   ? $T.children(".content-open") : false)
   2056 				,	$TC_c = (o.togglerContent_closed ? $T.children(".content-closed")   : false)
   2057 				,	$TC   = (s.isClosed ? $TC_c : $TC_o)
   2058 				;
   2059 				if ($TC_o) $TC_o.css("display", s.isClosed ? "none" : "block");
   2060 				if ($TC_c) $TC_c.css("display", s.isClosed ? "block" : "none");
   2061 
   2062 				if (dir == "horz") { // north/south
   2063 					var width = cssW($T, togLen);
   2064 					$T.css({
   2065 						width:	max(0, width)  // account for borders & padding
   2066 					,	height:	max(1, cssH($T, spacing)) // ditto
   2067 					,	left:	offset // TODO: VERIFY that toggler  positions correctly for ALL values
   2068 					});
   2069 					if ($TC) // CENTER the toggler content SPAN
   2070 						$TC.css("marginLeft", Math.floor((width-$TC.outerWidth())/2)); // could be negative
   2071 				}
   2072 				else { // east/west
   2073 					var height = cssH($T, togLen);
   2074 					$T.css({
   2075 						height:	max(0, height)  // account for borders & padding
   2076 					,	width:	max(1, cssW($T, spacing)) // ditto
   2077 					,	top:	offset // POSITION the toggler
   2078 					});
   2079 					if ($TC) // CENTER the toggler content SPAN
   2080 						$TC.css("marginTop", Math.floor((height-$TC.outerHeight())/2)); // could be negative
   2081 				}
   2082 
   2083 
   2084 			}
   2085 
   2086 			// DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
   2087 			if (onInit && o.initHidden) {
   2088 				$R.hide();
   2089 				if ($T) $T.hide();
   2090 			}
   2091 		});
   2092 	};
   2093 
   2094 
   2095 	/**
   2096 	 * resizeAll
   2097 	 *
   2098 	 * @callers  window.onresize(), callbacks or custom code
   2099 	 */
   2100 	var resizeAll = function () {
   2101 		var
   2102 			oldW	= cDims.innerWidth
   2103 		,	oldH	= cDims.innerHeight
   2104 		;
   2105 		cDims = state.container = getElemDims($Container); // UPDATE container dimensions
   2106 
   2107 		var
   2108 			checkH	= (cDims.innerHeight < oldH)
   2109 		,	checkW	= (cDims.innerWidth < oldW)
   2110 		,	s, dir
   2111 		;
   2112 
   2113 		if (checkH || checkW)
   2114 			// NOTE special order for sizing: S-N-E-W
   2115 			$.each(["south","north","east","west"], function(i,pane) {
   2116 				s = state[pane];
   2117 				dir = c[pane].dir;
   2118 				if (!s.isClosed && ((checkH && dir=="horz") || (checkW && dir=="vert"))) {
   2119 					setPaneMinMaxSizes(pane); // update pane-state
   2120 					// shrink pane if 'too big' to fit
   2121 					if (s.size > s.maxSize)
   2122 						sizePane(pane, s.maxSize);
   2123 				}
   2124 			});
   2125 
   2126 		sizeMidPanes("all");
   2127 		sizeHandles("all"); // reposition the toggler elements
   2128 	};
   2129 
   2130 
   2131 	/**
   2132 	 * keyDown
   2133 	 *
   2134 	 * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
   2135 	 *
   2136 	 * @callers  document.keydown()
   2137 	 */
   2138 	function keyDown (evt) {
   2139 		if (!evt) return true;
   2140 		var code = evt.keyCode;
   2141 		if (code < 33) return true; // ignore special keys: ENTER, TAB, etc
   2142 
   2143 		var
   2144 			PANE = {
   2145 				38: "north" // Up Cursor
   2146 			,	40: "south" // Down Cursor
   2147 			,	37: "west"  // Left Cursor
   2148 			,	39: "east"  // Right Cursor
   2149 			}
   2150 		,	isCursorKey = (code >= 37 && code <= 40)
   2151 		,	ALT = evt.altKey // no worky!
   2152 		,	SHIFT = evt.shiftKey
   2153 		,	CTRL = evt.ctrlKey
   2154 		,	pane = false
   2155 		,	s, o, k, m, el
   2156 		;
   2157 
   2158 		if (!CTRL && !SHIFT)
   2159 			return true; // no modifier key - abort
   2160 		else if (isCursorKey && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
   2161 			pane = PANE[code];
   2162 		else // check to see if this matches a custom-hotkey
   2163 			$.each(c.borderPanes.split(","), function(i,p) { // loop each pane to check its hotkey
   2164 				o = options[p];
   2165 				k = o.customHotkey;
   2166 				m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
   2167 				if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
   2168 					if (k && code == (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
   2169 						pane = p;
   2170 						return false; // BREAK
   2171 					}
   2172 				}
   2173 			});
   2174 
   2175 		if (!pane) return true; // no hotkey - abort
   2176 
   2177 		// validate pane
   2178 		o = options[pane]; // get pane options
   2179 		s = state[pane]; // get pane options
   2180 		if (!o.enableCursorHotkey || s.isHidden || !$Ps[pane]) return true;
   2181 
   2182 		// see if user is in a 'form field' because may be 'selecting text'!
   2183 		el = evt.target || evt.srcElement;
   2184 		if (el && SHIFT && isCursorKey && (el.tagName=="TEXTAREA" || (el.tagName=="INPUT" && (code==37 || code==39))))
   2185 			return true; // allow text-selection
   2186 
   2187 		// SYNTAX NOTES
   2188 		// use "returnValue=false" to abort keystroke but NOT abort function - can run another command afterwards
   2189 		// use "return false" to abort keystroke AND abort function
   2190 		toggle(pane);
   2191 		evt.stopPropagation();
   2192 		evt.returnValue = false; // CANCEL key
   2193 		return false;
   2194 	};
   2195 
   2196 
   2197 /*
   2198  * ###########################
   2199  *     UTILITY METHODS
   2200  *   called externally only
   2201  * ###########################
   2202  */
   2203 
   2204 	function allowOverflow (elem) {
   2205 		if (this && this.tagName) elem = this; // BOUND to element
   2206 		var $P;
   2207 		if (typeof elem=="string")
   2208 			$P = $Ps[elem];
   2209 		else {
   2210 			if ($(elem).attr("pane")) $P = $(elem);
   2211 			else $P = $(elem).parents("div[pane]:first");
   2212 		}
   2213 		if (!$P.length) return; // INVALID
   2214 
   2215 		var
   2216 			pane	= $P.attr("pane")
   2217 		,	s		= state[pane]
   2218 		;
   2219 
   2220 		// if pane is already raised, then reset it before doing it again!
   2221 		// this would happen if allowOverflow is attached to BOTH the pane and an element 
   2222 		if (s.cssSaved)
   2223 			resetOverflow(pane); // reset previous CSS before continuing
   2224 
   2225 		// if pane is raised by sliding or resizing, or it's closed, then abort
   2226 		if (s.isSliding || s.isResizing || s.isClosed) {
   2227 			s.cssSaved = false;
   2228 			return;
   2229 		}
   2230 
   2231 		var
   2232 			newCSS	= { zIndex: (c.zIndex.pane_normal + 1) }
   2233 		,	curCSS	= {}
   2234 		,	of		= $P.css("overflow")
   2235 		,	ofX		= $P.css("overflowX")
   2236 		,	ofY		= $P.css("overflowY")
   2237 		;
   2238 		// determine which, if any, overflow settings need to be changed
   2239 		if (of != "visible") {
   2240 			curCSS.overflow = of;
   2241 			newCSS.overflow = "visible";
   2242 		}
   2243 		if (ofX && ofX != "visible" && ofX != "auto") {
   2244 			curCSS.overflowX = ofX;
   2245 			newCSS.overflowX = "visible";
   2246 		}
   2247 		if (ofY && ofY != "visible" && ofY != "auto") {
   2248 			curCSS.overflowY = ofX;
   2249 			newCSS.overflowY = "visible";
   2250 		}
   2251 
   2252 		// save the current overflow settings - even if blank!
   2253 		s.cssSaved = curCSS;
   2254 
   2255 		// apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
   2256 		$P.css( newCSS );
   2257 
   2258 		// make sure the zIndex of all other panes is normal
   2259 		$.each(c.allPanes.split(","), function(i, p) {
   2260 			if (p != pane) resetOverflow(p);
   2261 		});
   2262 
   2263 	};
   2264 
   2265 	function resetOverflow (elem) {
   2266 		if (this && this.tagName) elem = this; // BOUND to element
   2267 		var $P;
   2268 		if (typeof elem=="string")
   2269 			$P = $Ps[elem];
   2270 		else {
   2271 			if ($(elem).hasClass("ui-layout-pane")) $P = $(elem);
   2272 			else $P = $(elem).parents("div[pane]:first");
   2273 		}
   2274 		if (!$P.length) return; // INVALID
   2275 
   2276 		var
   2277 			pane	= $P.attr("pane")
   2278 		,	s		= state[pane]
   2279 		,	CSS		= s.cssSaved || {}
   2280 		;
   2281 		// reset the zIndex
   2282 		if (!s.isSliding && !s.isResizing)
   2283 			$P.css("zIndex", c.zIndex.pane_normal);
   2284 
   2285 		// reset Overflow - if necessary
   2286 		$P.css( CSS );
   2287 
   2288 		// clear var
   2289 		s.cssSaved = false;
   2290 	};
   2291 
   2292 
   2293 	/**
   2294 	* getBtn
   2295 	*
   2296 	* Helper function to validate params received by addButton utilities
   2297 	*
   2298 	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .toggle-button"
   2299 	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
   2300 	* @returns  If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise 'false'
   2301 	*/
   2302 	function getBtn(selector, pane, action) {
   2303 		var
   2304 			$E = $(selector)
   2305 		,	err = "Error Adding Button \n\nInvalid "
   2306 		;
   2307 		if (!$E.length) // element not found
   2308 			alert(err+"selector: "+ selector);
   2309 		else if (c.borderPanes.indexOf(pane) == -1) // invalid 'pane' sepecified
   2310 			alert(err+"pane: "+ pane);
   2311 		else { // VALID
   2312 			var btn = options[pane].buttonClass +"-"+ action;
   2313 			$E.addClass( btn +" "+ btn +"-"+ pane );
   2314 			return $E;
   2315 		}
   2316 		return false;  // INVALID
   2317 	};
   2318 
   2319 
   2320 	/**
   2321 	* addToggleBtn
   2322 	*
   2323 	* Add a custom Toggler button for a pane
   2324 	*
   2325 	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .toggle-button"
   2326 	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
   2327 	*/
   2328 	function addToggleBtn (selector, pane) {
   2329 		var $E = getBtn(selector, pane, "toggle");
   2330 		if ($E)
   2331 			$E
   2332 				.attr("title", state[pane].isClosed ? "Open" : "Close")
   2333 				.click(function (evt) {
   2334 					toggle(pane);
   2335 					evt.stopPropagation();
   2336 				})
   2337 			;
   2338 	};
   2339 
   2340 	/**
   2341 	* addOpenBtn
   2342 	*
   2343 	* Add a custom Open button for a pane
   2344 	*
   2345 	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .open-button"
   2346 	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
   2347 	*/
   2348 	function addOpenBtn (selector, pane) {
   2349 		var $E = getBtn(selector, pane, "open");
   2350 		if ($E)
   2351 			$E
   2352 				.attr("title", "Open")
   2353 				.click(function (evt) {
   2354 					open(pane);
   2355 					evt.stopPropagation();
   2356 				})
   2357 			;
   2358 	};
   2359 
   2360 	/**
   2361 	* addCloseBtn
   2362 	*
   2363 	* Add a custom Close button for a pane
   2364 	*
   2365 	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .close-button"
   2366 	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
   2367 	*/
   2368 	function addCloseBtn (selector, pane) {
   2369 		var $E = getBtn(selector, pane, "close");
   2370 		if ($E)
   2371 			$E
   2372 				.attr("title", "Close")
   2373 				.click(function (evt) {
   2374 					close(pane);
   2375 					evt.stopPropagation();
   2376 				})
   2377 			;
   2378 	};
   2379 
   2380 	/**
   2381 	* addPinBtn
   2382 	*
   2383 	* Add a custom Pin button for a pane
   2384 	*
   2385 	* Four classes are added to the element, based on the paneClass for the associated pane...
   2386 	* Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
   2387 	*  - ui-layout-pane-pin
   2388 	*  - ui-layout-pane-west-pin
   2389 	*  - ui-layout-pane-pin-up
   2390 	*  - ui-layout-pane-west-pin-up
   2391 	*
   2392 	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .ui-layout-pin"
   2393 	* @param String   pane 		Name of the pane the pin is for: 'north', 'south', etc.
   2394 	*/
   2395 	function addPinBtn (selector, pane) {
   2396 		var $E = getBtn(selector, pane, "pin");
   2397 		if ($E) {
   2398 			var s = state[pane];
   2399 			$E.click(function (evt) {
   2400 				setPinState($(this), pane, (s.isSliding || s.isClosed));
   2401 				if (s.isSliding || s.isClosed) open( pane ); // change from sliding to open
   2402 				else close( pane ); // slide-closed
   2403 				evt.stopPropagation();
   2404 			});
   2405 			// add up/down pin attributes and classes
   2406 			setPinState ($E, pane, (!s.isClosed && !s.isSliding));
   2407 			// add this pin to the pane data so we can 'sync it' automatically
   2408 			// PANE.pins key is an array so we can store multiple pins for each pane
   2409 			c[pane].pins.push( selector ); // just save the selector string
   2410 		}
   2411 	};
   2412 
   2413 	/**
   2414 	* syncPinBtns
   2415 	*
   2416 	* INTERNAL function to sync 'pin buttons' when pane is opened or closed
   2417 	* Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
   2418 	*
   2419 	* @callers  open(), close()
   2420 	* @params  pane   These are the params returned to callbacks by layout()
   2421 	* @params  doPin  True means set the pin 'down', False means 'up'
   2422 	*/
   2423 	function syncPinBtns (pane, doPin) {
   2424 		$.each(c[pane].pins, function (i, selector) {
   2425 			setPinState($(selector), pane, doPin);
   2426 		});
   2427 	};
   2428 
   2429 	/**
   2430 	* setPinState
   2431 	*
   2432 	* Change the class of the pin button to make it look 'up' or 'down'
   2433 	*
   2434 	* @callers  addPinBtn(), syncPinBtns()
   2435 	* @param Element  $Pin		The pin-span element in a jQuery wrapper
   2436 	* @param Boolean  doPin		True = set the pin 'down', False = set it 'up'
   2437 	* @param String   pinClass	The root classname for pins - will add '-up' or '-down' suffix
   2438 	*/
   2439 	function setPinState ($Pin, pane, doPin) {
   2440 		var updown = $Pin.attr("pin");
   2441 		if (updown && doPin == (updown=="down")) return; // already in correct state
   2442 		var
   2443 			root	= options[pane].buttonClass
   2444 		,	class1	= root +"-pin"
   2445 		,	class2	= class1 +"-"+ pane
   2446 		,	UP1		= class1 + "-up"
   2447 		,	UP2		= class2 + "-up"
   2448 		,	DN1		= class1 + "-down"
   2449 		,	DN2		= class2 + "-down"
   2450 		;
   2451 		$Pin
   2452 			.attr("pin", doPin ? "down" : "up") // logic
   2453 			.attr("title", doPin ? "Un-Pin" : "Pin")
   2454 			.removeClass( doPin ? UP1 : DN1 ) 
   2455 			.removeClass( doPin ? UP2 : DN2 ) 
   2456 			.addClass( doPin ? DN1 : UP1 ) 
   2457 			.addClass( doPin ? DN2 : UP2 ) 
   2458 		;
   2459 	};
   2460 
   2461 
   2462 /*
   2463  * ###########################
   2464  * CREATE/RETURN BORDER-LAYOUT
   2465  * ###########################
   2466  */
   2467 
   2468 	// init global vars
   2469 	var 
   2470 		$Container = $(this).css({ overflow: "hidden" }) // Container elem
   2471 	,	$Ps		= {} // Panes x4	- set in initPanes()
   2472 	,	$Cs		= {} // Content x4	- set in initPanes()
   2473 	,	$Rs		= {} // Resizers x4	- set in initHandles()
   2474 	,	$Ts		= {} // Togglers x4	- set in initHandles()
   2475 	//	object aliases
   2476 	,	c		= config // alias for config hash
   2477 	,	cDims	= state.container // alias for easy access to 'container dimensions'
   2478 	;
   2479 
   2480 	// create the border layout NOW
   2481 	create();
   2482 
   2483 	// return object pointers to expose data & option Properties, and primary action Methods
   2484 	return {
   2485 		options:		options			// property - options hash
   2486 	,	state:			state			// property - dimensions hash
   2487 	,	panes:			$Ps				// property - object pointers for ALL panes: panes.north, panes.center
   2488 	,	toggle:			toggle			// method - pass a 'pane' ("north", "west", etc)
   2489 	,	open:			open			// method - ditto
   2490 	,	close:			close			// method - ditto
   2491 	,	hide:			hide			// method - ditto
   2492 	,	show:			show			// method - ditto
   2493 	,	resizeContent:	sizeContent		// method - ditto
   2494 	,	sizePane:		sizePane		// method - pass a 'pane' AND a 'size' in pixels
   2495 	,	resizeAll:		resizeAll		// method - no parameters
   2496 	,	addToggleBtn:	addToggleBtn	// utility - pass element selector and 'pane'
   2497 	,	addOpenBtn:		addOpenBtn		// utility - ditto
   2498 	,	addCloseBtn:	addCloseBtn		// utility - ditto
   2499 	,	addPinBtn:		addPinBtn		// utility - ditto
   2500 	,	allowOverflow:	allowOverflow	// utility - pass calling element
   2501 	,	resetOverflow:	resetOverflow	// utility - ditto
   2502 	,	cssWidth:		cssW
   2503 	,	cssHeight:		cssH
   2504 	};
   2505 
   2506 }
   2507 })( jQuery );