[Mapbender-commits] r6329 -
trunk/mapbender/http/extensions/jquery.layout.all-1.2.0
svn_mapbender at osgeo.org
svn_mapbender at osgeo.org
Fri Jun 18 05:17:01 EDT 2010
Author: christoph
Date: 2010-06-18 09:17:01 +0000 (Fri, 18 Jun 2010)
New Revision: 6329
Modified:
trunk/mapbender/http/extensions/jquery.layout.all-1.2.0/jquery.layout.js
Log:
Modified: trunk/mapbender/http/extensions/jquery.layout.all-1.2.0/jquery.layout.js
===================================================================
--- trunk/mapbender/http/extensions/jquery.layout.all-1.2.0/jquery.layout.js 2010-06-18 09:16:29 UTC (rev 6328)
+++ trunk/mapbender/http/extensions/jquery.layout.all-1.2.0/jquery.layout.js 2010-06-18 09:17:01 UTC (rev 6329)
@@ -1,7 +1,7 @@
/*
* jquery.layout 1.2.0
*
- * Copyright (c) 2008
+ * Copyright (c) 2008
* Fabrizio Balliano (http://www.fabrizioballiano.net)
* Kevin Dalman (http://allpro.net)
*
@@ -10,2534 +10,2504 @@
*
* $Date: 2008-12-27 02:17:22 +0100 (sab, 27 dic 2008) $
* $Rev: 203 $
- *
+ *
* NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars
*/
-(function($){
+(function($) {
- $.fn.layout = function(opts){
-
- /*
- * ###########################
- * WIDGET CONFIG & OPTIONS
- * ###########################
- */
- // DEFAULTS for options
- var prefix = "ui-layout-" // prefix for ALL selectors and classNames
-, defaults = { // misc default values
- paneClass: prefix + "pane" // ui-layout-pane
- ,
- resizerClass: prefix + "resizer" // ui-layout-resizer
- ,
- togglerClass: prefix + "toggler" // ui-layout-toggler
- ,
- togglerInnerClass: prefix + "" // ui-layout-open / ui-layout-closed
- ,
- buttonClass: prefix + "button" // ui-layout-button
- ,
- contentSelector: "." + prefix + "content"// ui-layout-content
- ,
- contentIgnoreSelector: "." + prefix + "ignore" // ui-layout-mask
- };
-
- // DEFAULT PANEL OPTIONS - CHANGE IF DESIRED
- var options = {
- name: "" // FUTURE REFERENCE - not used right now
- ,
- scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
- ,
- defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
- applyDefaultStyles: false // apply basic styles directly to resizers & buttons? If not, then stylesheet must handle it
- ,
- closable: true // pane can open & close
- ,
- resizable: true // when open, pane can be resized
- ,
- slidable: true // when closed, pane can 'slide' open over other panes - closes on mouse-out
- //, paneSelector: [ ] // MUST be pane-specific!
- ,
- contentSelector: defaults.contentSelector // INNER div/element to auto-size so only it scrolls, not the entire pane!
- ,
- contentIgnoreSelector: defaults.contentIgnoreSelector // elem(s) to 'ignore' when measuring 'content'
- ,
- paneClass: defaults.paneClass // border-Pane - default: 'ui-layout-pane'
- ,
- resizerClass: defaults.resizerClass // Resizer Bar - default: 'ui-layout-resizer'
- ,
- togglerClass: defaults.togglerClass // Toggler Button - default: 'ui-layout-toggler'
- ,
- buttonClass: defaults.buttonClass // CUSTOM Buttons - default: 'ui-layout-button-toggle/-open/-close/-pin'
- ,
- resizerDragOpacity: 1 // option for ui.draggable
- //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
- ,
- maskIframesOnResize: true // true = all iframes OR = iframe-selector(s) - adds masking-div during resizing/dragging
- //, size: 100 // inital size of pane - defaults are set 'per pane'
- ,
- minSize: 0 // when manually resizing a pane
- ,
- maxSize: 0 // ditto, 0 = no limit
- ,
- spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
- ,
- spacing_closed: 6 // ditto - when pane is 'closed'
- ,
- togglerLength_open: 50 // Length = WIDTH of toggler button on north/south edges - HEIGHT on east/west edges
- ,
- togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
- ,
- togglerAlign_open: "center" // top/left, bottom/right, center, OR...
- ,
- togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
- ,
- togglerTip_open: "Close" // Toggler tool-tip (title)
- ,
- togglerTip_closed: "Open" // ditto
- ,
- resizerTip: "Resize" // Resizer tool-tip (title)
- ,
- sliderTip: "Slide Open" // resizer-bar triggers 'sliding' when pane is closed
- ,
- sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
- ,
- slideTrigger_open: "click" // click, dblclick, mouseover
- ,
- slideTrigger_close: "mouseout" // click, mouseout
- ,
- hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
- ,
- togglerContent_open: "" // text or HTML to put INSIDE the toggler
- ,
- togglerContent_closed: "" // ditto
- ,
- showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
- ,
- enableCursorHotkey: true // enabled 'cursor' hotkeys
- //, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
- ,
- customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
- // NOTE: fxSss_open & fxSss_close options (eg: fxName_open) are auto-generated if not passed
- ,
- fxName: "slide" // ('none' or blank), slide, drop, scale
- ,
- fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
- ,
- fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
- ,
- initClosed: false // true = init pane as 'closed'
- ,
- initHidden: false // true = init pane as 'hidden' - no resizer or spacing
- /* callback options do not have to be set - listed here for reference only
- , onshow_start: "" // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
- , onshow_end: "" // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
- , onhide_start: "" // CALLBACK when pane STARTS to Close - BEFORE onclose_start
- , onhide_end: "" // CALLBACK when pane ENDS being Closed - AFTER onclose_end
- , onopen_start: "" // CALLBACK when pane STARTS to Open
- , onopen_end: "" // CALLBACK when pane ENDS being Opened
- , onclose_start: "" // CALLBACK when pane STARTS to Close
- , onclose_end: "" // CALLBACK when pane ENDS being Closed
- , onresize_start: "" // CALLBACK when pane STARTS to be ***MANUALLY*** Resized
- , onresize_end: "" // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
- */
- },
- north: {
- paneSelector: "." + prefix + "north" // default = .ui-layout-north
- ,
- size: "auto",
- resizerCursor: "n-resize"
- },
- south: {
- paneSelector: "." + prefix + "south" // default = .ui-layout-south
- ,
- size: "auto",
- resizerCursor: "s-resize"
- },
- east: {
- paneSelector: "." + prefix + "east" // default = .ui-layout-east
- ,
- size: 200,
- resizerCursor: "e-resize"
- },
- west: {
- paneSelector: "." + prefix + "west" // default = .ui-layout-west
- ,
- size: 200,
- resizerCursor: "w-resize"
- },
- center: {
- paneSelector: "." + prefix + "center" // default = .ui-layout-center
- }
-
- };
-
-
- var effects = { // LIST *PREDEFINED EFFECTS* HERE, even if effect has no settings
- slide: {
- all: {
- duration: "fast"
- } // eg: duration: 1000, easing: "easeOutBounce"
- ,
- north: {
- direction: "up"
- },
- south: {
- direction: "down"
- },
- east: {
- direction: "right"
- },
- west: {
- direction: "left"
- }
- },
- drop: {
- all: {
- duration: "slow"
- } // eg: duration: 1000, easing: "easeOutQuint"
- ,
- north: {
- direction: "up"
- },
- south: {
- direction: "down"
- },
- east: {
- direction: "right"
- },
- west: {
- direction: "left"
- }
- },
- scale: {
- all: {
- duration: "fast"
- }
- }
- };
-
-
- // STATIC, INTERNAL CONFIG - DO NOT CHANGE THIS!
- var config = {
- allPanes: "north,south,east,west,center",
- borderPanes: "north,south,east,west",
- zIndex: { // set z-index values here
- resizer_normal: 1 // normal z-index for resizer-bars
- ,
- pane_normal: 2 // normal z-index for panes
- ,
- mask: 4 // overlay div used to mask pane(s) during resizing
- ,
- sliding: 100 // applied to both the pane and its resizer when a pane is 'slid open'
- ,
- resizing: 10000 // applied to the CLONED resizer-bar when being 'dragged'
- ,
- animation: 10000 // applied to the pane when being animated - not applied to the resizer
- },
- resizers: {
- cssReq: {
- position: "absolute",
- padding: 0,
- margin: 0,
- fontSize: "1px",
- textAlign: "left" // to counter-act "center" alignment!
- ,
- overflow: "hidden" // keep toggler button from overflowing
- ,
- zIndex: 1
- },
- cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
- background: "#DDD",
- border: "none"
- }
- },
- togglers: {
- cssReq: {
- position: "absolute",
- display: "block",
- padding: 0,
- margin: 0,
- overflow: "hidden",
- textAlign: "center",
- fontSize: "1px",
- cursor: "pointer",
- zIndex: 1
- },
- cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
- background: "#AAA"
- }
- },
- content: {
- cssReq: {
- overflow: "auto"
- },
- cssDef: {}
- },
- defaults: { // defaults for ALL panes - overridden by 'per-pane settings' below
- cssReq: {
- position: "absolute",
- margin: 0,
- zIndex: 2
- },
- cssDef: {
- padding: "10px",
- background: "#FFF",
- border: "1px solid #BBB",
- overflow: "auto"
- }
- },
- north: {
- edge: "top",
- sizeType: "height",
- dir: "horz",
- cssReq: {
- top: 0,
- bottom: "auto",
- left: 0,
- right: 0,
- width: "auto"
- // height: DYNAMIC
- }
- },
- south: {
- edge: "bottom",
- sizeType: "height",
- dir: "horz",
- cssReq: {
- top: "auto",
- bottom: 0,
- left: 0,
- right: 0,
- width: "auto"
- // height: DYNAMIC
- }
- },
- east: {
- edge: "right",
- sizeType: "width",
- dir: "vert",
- cssReq: {
- left: "auto",
- right: 0,
- top: "auto" // DYNAMIC
- ,
- bottom: "auto" // DYNAMIC
- ,
- height: "auto"
- // width: DYNAMIC
- }
- },
- west: {
- edge: "left",
- sizeType: "width",
- dir: "vert",
- cssReq: {
- left: 0,
- right: "auto",
- top: "auto" // DYNAMIC
- ,
- bottom: "auto" // DYNAMIC
- ,
- height: "auto"
- // width: DYNAMIC
- }
- },
- center: {
- dir: "center",
- cssReq: {
- left: "auto" // DYNAMIC
- ,
- right: "auto" // DYNAMIC
- ,
- top: "auto" // DYNAMIC
- ,
- bottom: "auto" // DYNAMIC
- ,
- height: "auto",
- width: "auto"
- }
- }
- };
-
-
- // DYNAMIC DATA
- var state = {
- // generate random 'ID#' to identify layout - used to create global namespace for timers
- id: Math.floor(Math.random() * 10000),
- container: {},
- north: {},
- south: {},
- east: {},
- west: {},
- center: {}
- };
-
-
- var altEdge = {
- top: "bottom",
- bottom: "top",
- left: "right",
- right: "left"
- }, altSide = {
- north: "south",
- south: "north",
- east: "west",
- west: "east"
- };
-
-
- /*
- * ###########################
- * INTERNAL HELPER FUNCTIONS
- * ###########################
- */
- /**
- * isStr
- *
- * Returns true if passed param is EITHER a simple string OR a 'string object' - otherwise returns false
- */
- var isStr = function(o){
- if (typeof o == "string")
- return true;
- else
- if (typeof o == "object") {
- try {
- var match = o.constructor.toString().match(/string/i);
- return (match !== null);
- }
- catch (e) {
- }
- }
- return false;
- };
-
- /**
- * str
- *
- * Returns a simple string if the passed param is EITHER a simple string OR a 'string object',
- * else returns the original object
- */
- var str = function(o){
- if (typeof o == "string" || isStr(o))
- return $.trim(o); // trim converts 'String object' to a simple string
- else
- return o;
- };
-
- /**
- * min / max
- *
- * Alias for Math.min/.max to simplify coding
- */
- var min = function(x, y){
- return Math.min(x, y);
- };
- var max = function(x, y){
- return Math.max(x, y);
- };
-
- /**
- * transformData
- *
- * Processes the options passed in and transforms them into the format used by layout()
- * Missing keys are added, and converts the data if passed in 'flat-format' (no sub-keys)
- * In flat-format, pane-specific-settings are prefixed like: north__optName (2-underscores)
- * To update effects, options MUST use nested-keys format, with an effects key
- *
- * @callers initOptions()
- * @params JSON d Data/options passed by user - may be a single level or nested levels
- * @returns JSON Creates a data struture that perfectly matches 'options', ready to be imported
- */
- var transformData = function(d){
- var json = {
- defaults: {
- fxSettings: {}
- },
- north: {
- fxSettings: {}
- },
- south: {
- fxSettings: {}
- },
- east: {
- fxSettings: {}
- },
- west: {
- fxSettings: {}
- },
- center: {
- fxSettings: {}
- }
- };
- d = d ||
- {};
- if (d.effects || d.defaults || d.north || d.south || d.west || d.east || d.center)
- json = $.extend(json, d); // already in json format - add to base keys
- else
- // convert 'flat' to 'nest-keys' format - also handles 'empty' user-options
- $.each(d, function(key, val){
- a = key.split("__");
- json[a[1] ? a[0] : "defaults"][a[1] ? a[1] : a[0]] = val;
- });
- return json;
- };
-
- /**
- * setFlowCallback
- *
- * Set an INTERNAL callback to avoid simultaneous animation
- * Runs only if needed and only if all callbacks are not 'already set'!
- *
- * @param String action Either 'open' or 'close'
- * @pane String pane A valid border-pane name, eg 'west'
- * @pane Boolean param Extra param for callback (optional)
- */
- var setFlowCallback = function(action, pane, param){
- var cb = action + "," + pane + "," + (param ? 1 : 0), cP, cbPane;
- $.each(c.borderPanes.split(","), function(i, p){
- if (c[p].isMoving) {
- bindCallback(p); // TRY to bind a callback
- return false; // BREAK
- }
- });
-
- function bindCallback(p, test){
- cP = c[p];
- if (!cP.doCallback) {
- cP.doCallback = true;
- cP.callback = cb;
- }
- else { // try to 'chain' this callback
- cpPane = cP.callback.split(",")[1]; // 2nd param is 'pane'
- if (cpPane != p && cpPane != pane) // callback target NOT 'itself' and NOT 'this pane'
- bindCallback(cpPane, true); // RECURSE
- }
- }
- };
-
- /**
- * execFlowCallback
- *
- * RUN the INTERNAL callback for this pane - if one exists
- *
- * @param String action Either 'open' or 'close'
- * @pane String pane A valid border-pane name, eg 'west'
- * @pane Boolean param Extra param for callback (optional)
- */
- var execFlowCallback = function(pane){
- var cP = c[pane];
-
- // RESET flow-control flaGs
- c.isLayoutBusy = false;
- delete cP.isMoving;
- if (!cP.doCallback || !cP.callback)
- return;
-
- cP.doCallback = false; // RESET logic flag
- // EXECUTE the callback
- var cb = cP.callback.split(","), param = (cb[2] > 0 ? true : false);
- if (cb[0] == "open")
- open(cb[1], param);
- else
- if (cb[0] == "close")
- close(cb[1], param);
-
- if (!cP.doCallback)
- cP.callback = null; // RESET - unless callback above enabled it again!
- };
-
- /**
- * execUserCallback
- *
- * Executes a Callback function after a trigger event, like resize, open or close
- *
- * @param String pane This is passed only so we can pass the 'pane object' to the callback
- * @param String v_fn Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
- */
- var execUserCallback = function(pane, v_fn){
- if (!v_fn)
- return;
- var fn;
- try {
- if (typeof v_fn == "function")
- fn = v_fn;
- else
- if (typeof v_fn != "string")
- return;
- else
- if (v_fn.indexOf(",") > 0) {
- // function name cannot contain a comma, so must be a function name AND a 'name' parameter
- var args = v_fn.split(","), fn = eval(args[0]);
- if (typeof fn == "function" && args.length > 1)
- return fn(args[1]); // pass the argument parsed from 'list'
- }
- else // just the name of an external function?
- fn = eval(v_fn);
-
- if (typeof fn == "function")
- // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
- return fn(pane, $Ps[pane], $.extend({}, state[pane]), $.extend({}, options[pane]), options.name);
- }
- catch (ex) {
- }
- };
-
- /**
- * cssNum
- *
- * Returns the 'current CSS value' for an element - returns 0 if property does not exist
- *
- * @callers Called by many methods
- * @param jQuery $Elem Must pass a jQuery object - first element is processed
- * @param String property The name of the CSS property, eg: top, width, etc.
- * @returns Variant Usually is used to get an integer value for position (top, left) or size (height, width)
- */
- var cssNum = function($E, prop){
- var val = 0, hidden = false, visibility = "";
- if (!$.browser.msie) { // IE CAN read dimensions of 'hidden' elements - FF CANNOT
- if ($.curCSS($E[0], "display", true) == "none") {
- hidden = true;
- visibility = $.curCSS($E[0], "visibility", true); // SAVE current setting
- $E.css({
- display: "block",
- visibility: "hidden"
- }); // show element 'invisibly' so we can measure it
- }
- }
-
- val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
-
- if (hidden) { // WAS hidden, so put back the way it was
- $E.css({
- display: "none"
- });
- if (visibility && visibility != "hidden")
- $E.css({
- visibility: visibility
- }); // reset 'visibility'
- }
-
- return val;
- };
-
- /**
- * cssW / cssH / cssSize
- *
- * Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
- *
- * @callers initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
- * @param Variant elem Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
- * @param Integer outerWidth/outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
- * @returns Integer Returns the innerHeight of the elem by subtracting padding and borders
- *
- * @TODO May need to add additional logic to handle more browser/doctype variations?
- */
- var cssW = function(e, outerWidth){
- var $E;
- if (isStr(e)) {
- e = str(e);
- $E = $Ps[e];
- }
- else
- $E = $(e);
-
- // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
- if (outerWidth <= 0)
- return 0;
- else
- if (!(outerWidth > 0))
- outerWidth = isStr(e) ? getPaneSize(e) : $E.outerWidth();
-
- if (!$.boxModel)
- return outerWidth;
-
- else // strip border and padding size from outerWidth to get CSS Width
- return outerWidth -
- cssNum($E, "paddingLeft") -
- cssNum($E, "paddingRight") -
- ($.curCSS($E[0], "borderLeftStyle", true) == "none" ? 0 : cssNum($E, "borderLeftWidth")) -
- ($.curCSS($E[0], "borderRightStyle", true) == "none" ? 0 : cssNum($E, "borderRightWidth"));
- };
- var cssH = function(e, outerHeight){
- var $E;
- if (isStr(e)) {
- e = str(e);
- $E = $Ps[e];
- }
- else
- $E = $(e);
-
- // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
- if (outerHeight <= 0)
- return 0;
- else
- if (!(outerHeight > 0))
- outerHeight = (isStr(e)) ? getPaneSize(e) : $E.outerHeight();
-
- if (!$.boxModel)
- return outerHeight;
-
- else // strip border and padding size from outerHeight to get CSS Height
- return outerHeight -
- cssNum($E, "paddingTop") -
- cssNum($E, "paddingBottom") -
- ($.curCSS($E[0], "borderTopStyle", true) == "none" ? 0 : cssNum($E, "borderTopWidth")) -
- ($.curCSS($E[0], "borderBottomStyle", true) == "none" ? 0 : cssNum($E, "borderBottomWidth"));
- };
- var cssSize = function(pane, outerSize){
- if (c[pane].dir == "horz") // pane = north or south
- return cssH(pane, outerSize);
- else // pane = east or west
- return cssW(pane, outerSize);
- };
-
- /**
- * getPaneSize
- *
- * Calculates the current 'size' (width or height) of a border-pane - optionally with 'pane spacing' added
- *
- * @returns Integer Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
- */
- var getPaneSize = function(pane, inclSpace){
- var $P = $Ps[pane], o = options[pane], s = state[pane], oSp = (inclSpace ? o.spacing_open : 0), cSp = (inclSpace ? o.spacing_closed : 0);
- if (!$P || s.isHidden)
- return 0;
- else
- if (s.isClosed || (s.isSliding && inclSpace))
- return cSp;
- else
- if (c[pane].dir == "horz")
- return $P.outerHeight() + oSp;
- else // dir == "vert"
- return $P.outerWidth() + oSp;
- };
-
- var setPaneMinMaxSizes = function(pane){
- var d = cDims, edge = c[pane].edge, dir = c[pane].dir, o = options[pane], s = state[pane], $P = $Ps[pane], $altPane = $Ps[altSide[pane]], paneSpacing = o.spacing_open, altPaneSpacing = options[altSide[pane]].spacing_open, altPaneSize = (!$altPane ? 0 : (dir == "horz" ? $altPane.outerHeight() : $altPane.outerWidth())), containerSize = (dir == "horz" ? d.innerHeight : d.innerWidth) // limitSize prevents this pane from 'overlapping' opposite pane - even if opposite pane is currently closed
- , limitSize = containerSize - paneSpacing - altPaneSize - altPaneSpacing, minSize = s.minSize || 0, maxSize = Math.min(s.maxSize || 9999, limitSize), minPos, maxPos // used to set resizing limits
-;
- switch (pane) {
- case "north":
- minPos = d.offsetTop + minSize;
- maxPos = d.offsetTop + maxSize;
- break;
- case "west":
- minPos = d.offsetLeft + minSize;
- maxPos = d.offsetLeft + maxSize;
- break;
- case "south":
- minPos = d.offsetTop + d.innerHeight - maxSize;
- maxPos = d.offsetTop + d.innerHeight - minSize;
- break;
- case "east":
- minPos = d.offsetLeft + d.innerWidth - maxSize;
- maxPos = d.offsetLeft + d.innerWidth - minSize;
- break;
- }
- // save data to pane-state
- $.extend(s, {
- minSize: minSize,
- maxSize: maxSize,
- minPosition: minPos,
- maxPosition: maxPos
- });
- };
-
- /**
- * getPaneDims
- *
- * Returns data for setting the size/position of center pane. Date is also used to set Height for east/west panes
- *
- * @returns JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
- */
- var getPaneDims = function(){
- var d = {
- top: getPaneSize("north", true) // true = include 'spacing' value for p
- ,
- bottom: getPaneSize("south", true),
- left: getPaneSize("west", true),
- right: getPaneSize("east", true),
- width: 0,
- height: 0
- };
-
- with (d) {
- width = cDims.innerWidth - left - right;
- height = cDims.innerHeight - bottom - top;
- // now add the 'container border/padding' to get final positions - relative to the container
- top += cDims.top;
- bottom += cDims.bottom;
- left += cDims.left;
- right += cDims.right;
- }
-
- return d;
- };
-
-
- /**
- * getElemDims
- *
- * Returns data for setting size of an element (container or a pane).
- *
- * @callers create(), onWindowResize() for container, plus others for pane
- * @returns JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
- */
- var getElemDims = function($E){
- var d = {} // dimensions hash
-, e, b, p // edge, border, padding
-;
-
- $.each("Left,Right,Top,Bottom".split(","), function(){
- e = str(this);
- b = d["border" + e] = cssNum($E, "border" + e + "Width");
- p = d["padding" + e] = cssNum($E, "padding" + e);
- d["offset" + e] = b + p; // total offset of content from outer edge
- // if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
- if ($E == $Container)
- d[e.toLowerCase()] = ($.boxModel ? p : 0);
- });
-
- d.innerWidth = d.outerWidth = $E.outerWidth();
- d.innerHeight = d.outerHeight = $E.outerHeight();
- if ($.boxModel) {
- d.innerWidth -= (d.offsetLeft + d.offsetRight);
- d.innerHeight -= (d.offsetTop + d.offsetBottom);
- }
-
- return d;
- };
-
-
- var setTimer = function(pane, action, fn, ms){
- var Layout = window.layout = window.layout ||
- {}, Timers = Layout.timers = Layout.timers ||
- {}, name = "layout_" + state.id + "_" + pane + "_" + action // UNIQUE NAME for every layout-pane-action
-;
- if (Timers[name])
- return; // timer already set!
- else
- Timers[name] = setTimeout(fn, ms);
- };
-
- var clearTimer = function(pane, action){
- var Layout = window.layout = window.layout ||
- {}, Timers = Layout.timers = Layout.timers ||
- {}, name = "layout_" + state.id + "_" + pane + "_" + action // UNIQUE NAME for every layout-pane-action
-;
- if (Timers[name]) {
- clearTimeout(Timers[name]);
- delete Timers[name];
- return true;
- }
- else
- return false;
- };
-
-
- /*
- * ###########################
- * INITIALIZATION METHODS
- * ###########################
- */
- /**
- * create
- *
- * Initialize the layout - called automatically whenever an instance of layout is created
- *
- * @callers NEVER explicity called
- * @returns An object pointer to the instance created
- */
- var create = function(){
- // initialize config/options
- initOptions();
-
- // initialize all objects
- initContainer(); // set CSS as needed and init state.container dimensions
- initPanes(); // size & position all panes
- initHandles(); // create and position all resize bars & togglers buttons
- initResizable(); // activate resizing on all panes where resizable=true
- sizeContent("all"); // AFTER panes & handles have been initialized, size 'content' divs
- if (options.scrollToBookmarkOnLoad)
- with (self.location)
-if (hash)
- replace(hash); // scrollTo Bookmark
- // bind hotkey function - keyDown - if required
- initHotkeys();
-
- // bind resizeAll() for 'this layout instance' to window.resize event
- $(window).resize(function(){
- var timerID = "timerLayout_" + state.id;
- if (window[timerID])
- clearTimeout(window[timerID]);
- window[timerID] = null;
- if (true || $.browser.msie) // use a delay for IE because the resize event fires repeatly
- window[timerID] = setTimeout(resizeAll, 100);
- else // most other browsers have a built-in delay before firing the resize event
- resizeAll(); // resize all layout elements NOW!
- });
- };
-
- /**
- * initContainer
- *
- * Validate and initialize container CSS and events
- *
- * @callers create()
- */
- var initContainer = function(){
- try { // format html/body if this is a full page layout
- if ($Container[0].tagName == "BODY") {
- $("html").css({
- height: "100%",
- overflow: "hidden"
- });
- $("body").css({
- position: "relative",
- height: "100%",
- overflow: "hidden",
- margin: 0,
- padding: 0 // TODO: test whether body-padding could be handled?
- ,
- border: "none" // a body-border creates problems because it cannot be measured!
- });
- }
- else { // set required CSS - overflow and position
- var CSS = {
- overflow: "hidden"
- } // make sure container will not 'scroll'
-, p = $Container.css("position"), h = $Container.css("height");
- // if this is a NESTED layout, then outer-pane ALREADY has position and height
- if (!$Container.hasClass("ui-layout-pane")) {
- if (!p || "fixed,absolute,relative".indexOf(p) < 0)
- CSS.position = "relative"; // container MUST have a 'position'
- if (!h || h == "auto")
- CSS.height = "100%"; // container MUST have a 'height'
- }
- $Container.css(CSS);
- }
- }
- catch (ex) {
- }
-
- // get layout-container dimensions (updated when necessary)
- cDims = state.container = getElemDims($Container); // update data-pointer too
- };
-
- /**
- * initHotkeys
- *
- * Bind layout hotkeys - if options enabled
- *
- * @callers create()
- */
- var initHotkeys = function(){
- // bind keyDown to capture hotkeys, if option enabled for ANY pane
- $.each(c.borderPanes.split(","), function(i, pane){
- var o = options[pane];
- if (o.enableCursorHotkey || o.customHotkey) {
- $(document).keydown(keyDown); // only need to bind this ONCE
- return false; // BREAK - binding was done
- }
- });
- };
-
- /**
- * initOptions
- *
- * Build final CONFIG and OPTIONS data
- *
- * @callers create()
- */
- var initOptions = function(){
- // simplify logic by making sure passed 'opts' var has basic keys
- opts = transformData(opts);
-
- // update default effects, if case user passed key
- if (opts.effects) {
- $.extend(effects, opts.effects);
- delete opts.effects;
- }
-
- // see if any 'global options' were specified
- $.each("name,scrollToBookmarkOnLoad".split(","), function(idx, key){
- if (opts[key] !== undefined)
- options[key] = opts[key];
- else
- if (opts.defaults[key] !== undefined) {
- options[key] = opts.defaults[key];
- delete opts.defaults[key];
- }
- });
-
- // remove any 'defaults' that MUST be set 'per-pane'
- $.each("paneSelector,resizerCursor,customHotkey".split(","), function(idx, key){
- delete opts.defaults[key];
- } // is OK if key does not exist
-);
-
- // now update options.defaults
- $.extend(options.defaults, opts.defaults);
- // make sure required sub-keys exist
- //if (typeof options.defaults.fxSettings != "object") options.defaults.fxSettings = {};
-
- // merge all config & options for the 'center' pane
- c.center = $.extend(true, {}, c.defaults, c.center);
- $.extend(options.center, opts.center);
- // Most 'default options' do not apply to 'center', so add only those that DO
- var o_Center = $.extend(true, {}, options.defaults, opts.defaults, options.center); // TEMP data
- $.each("paneClass,contentSelector,contentIgnoreSelector,applyDefaultStyles,showOverflowOnHover".split(","), function(idx, key){
- options.center[key] = o_Center[key];
- });
-
- var defs = options.defaults;
-
- // create a COMPLETE set of options for EACH border-pane
- $.each(c.borderPanes.split(","), function(i, pane){
- // apply 'pane-defaults' to CONFIG.PANE
- c[pane] = $.extend(true, {}, c.defaults, c[pane]);
- // apply 'pane-defaults' + user-options to OPTIONS.PANE
- o = options[pane] = $.extend(true, {}, options.defaults, options[pane], opts.defaults, opts[pane]);
-
- // make sure we have base-classes
- if (!o.paneClass)
- o.paneClass = defaults.paneClass;
- if (!o.resizerClass)
- o.resizerClass = defaults.resizerClass;
- if (!o.togglerClass)
- o.togglerClass = defaults.togglerClass;
-
- // create FINAL fx options for each pane, ie: options.PANE.fxName/fxSpeed/fxSettings[_open|_close]
- $.each(["_open", "_close", ""], function(i, n){
- var sName = "fxName" + n, sSpeed = "fxSpeed" + n, sSettings = "fxSettings" + n;
- // recalculate fxName according to specificity rules
- o[sName] = opts[pane][sName] // opts.west.fxName_open
- ||
- opts[pane].fxName // opts.west.fxName
- ||
- opts.defaults[sName] // opts.defaults.fxName_open
- ||
- opts.defaults.fxName // opts.defaults.fxName
- ||
- o[sName] // options.west.fxName_open
- ||
- o.fxName // options.west.fxName
- ||
- defs[sName] // options.defaults.fxName_open
- ||
- defs.fxName // options.defaults.fxName
- ||
- "none";
- // validate fxName to be sure is a valid effect
- var fxName = o[sName];
- if (fxName == "none" || !$.effects || !$.effects[fxName] || (!effects[fxName] && !o[sSettings] && !o.fxSettings))
- fxName = o[sName] = "none"; // effect not loaded, OR undefined FX AND fxSettings not passed
- // set vars for effects subkeys to simplify logic
- var fx = effects[fxName] ||
- {} // effects.slide
-, fx_all = fx.all ||
- {} // effects.slide.all
-, fx_pane = fx[pane] ||
- {} // effects.slide.west
-;
- // RECREATE the fxSettings[_open|_close] keys using specificity rules
- o[sSettings] = $.extend({}, fx_all // effects.slide.all
-, fx_pane // effects.slide.west
-, defs.fxSettings ||
- {} // options.defaults.fxSettings
-, defs[sSettings] ||
- {} // options.defaults.fxSettings_open
-, o.fxSettings // options.west.fxSettings
-, o[sSettings] // options.west.fxSettings_open
-, opts.defaults.fxSettings // opts.defaults.fxSettings
-, opts.defaults[sSettings] ||
- {} // opts.defaults.fxSettings_open
-, opts[pane].fxSettings // opts.west.fxSettings
-, opts[pane][sSettings] ||
- {} // opts.west.fxSettings_open
-);
- // recalculate fxSpeed according to specificity rules
- o[sSpeed] = opts[pane][sSpeed] // opts.west.fxSpeed_open
- ||
- opts[pane].fxSpeed // opts.west.fxSpeed (pane-default)
- ||
- opts.defaults[sSpeed] // opts.defaults.fxSpeed_open
- ||
- opts.defaults.fxSpeed // opts.defaults.fxSpeed
- ||
- o[sSpeed] // options.west.fxSpeed_open
- ||
- o[sSettings].duration // options.west.fxSettings_open.duration
- ||
- o.fxSpeed // options.west.fxSpeed
- ||
- o.fxSettings.duration // options.west.fxSettings.duration
- ||
- defs.fxSpeed // options.defaults.fxSpeed
- ||
- defs.fxSettings.duration// options.defaults.fxSettings.duration
- ||
- fx_pane.duration // effects.slide.west.duration
- ||
- fx_all.duration // effects.slide.all.duration
- ||
- "normal" // DEFAULT
-;
- // DEBUG: if (pane=="east") debugData( $.extend({}, {speed: o[sSpeed], fxSettings_duration: o[sSettings].duration}, o[sSettings]), pane+"."+sName+" = "+fxName );
- });
- });
- };
-
- /**
- * initPanes
- *
- * Initialize module objects, styling, size and position for all panes
- *
- * @callers create()
- */
- var initPanes = function(){
- // NOTE: do north & south FIRST so we can measure their height - do center LAST
- $.each(c.allPanes.split(","), function(){
- var pane = str(this), o = options[pane], s = state[pane], fx = s.fx, dir = c[pane].dir // if o.size is not > 0, then we will use MEASURE the pane and use that as it's 'size'
- , size = o.size == "auto" || isNaN(o.size) ? 0 : o.size, minSize = o.minSize || 1, maxSize = o.maxSize || 9999, spacing = o.spacing_open || 0, sel = o.paneSelector, isIE6 = ($.browser.msie && $.browser.version < 7), CSS = {}, $P, $C;
- $Cs[pane] = false; // init
- if (sel.substr(0, 1) === "#") // ID selector
- // NOTE: elements selected 'by ID' DO NOT have to be 'children'
- $P = $Ps[pane] = $Container.find(sel + ":first");
- else { // class or other selector
- $P = $Ps[pane] = $Container.children(sel + ":first");
- // look for the pane nested inside a 'form' element
- if (!$P.length)
- $P = $Ps[pane] = $Container.children("form:first").children(sel + ":first");
- }
-
- if (!$P.length) {
- $Ps[pane] = false; // logic
- return true; // SKIP to next
- }
-
- // add basic classes & attributes
- $P.attr("pane", pane) // add pane-identifier
-.addClass(o.paneClass + " " + o.paneClass + "-" + pane) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
-;
-
- // init pane-logic vars, etc.
- if (pane != "center") {
- s.isClosed = false; // true = pane is closed
- s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
- s.isResizing = false; // true = pane is in process of being resized
- s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
- s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
- // create special keys for internal use
- c[pane].pins = []; // used to track and sync 'pin-buttons' for border-panes
- }
-
- CSS = $.extend({
- visibility: "visible",
- display: "block"
- }, c.defaults.cssReq, c[pane].cssReq);
- if (o.applyDefaultStyles)
- $.extend(CSS, c.defaults.cssDef, c[pane].cssDef); // cosmetic defaults
- $P.css(CSS); // add base-css BEFORE 'measuring' to calc size & position
- CSS = {}; // reset var
- // set css-position to account for container borders & padding
- switch (pane) {
- case "north":
- CSS.top = cDims.top;
- CSS.left = cDims.left;
- CSS.right = cDims.right;
- break;
- case "south":
- CSS.bottom = cDims.bottom;
- CSS.left = cDims.left;
- CSS.right = cDims.right;
- break;
- case "west":
- CSS.left = cDims.left; // top, bottom & height set by sizeMidPanes()
- break;
- case "east":
- CSS.right = cDims.right; // ditto
- break;
- case "center": // top, left, width & height set by sizeMidPanes()
- }
-
- if (dir == "horz") { // north or south pane
- if (size === 0 || size == "auto") {
- $P.css({
- height: "auto"
- });
- size = $P.outerHeight();
- }
- size = max(size, minSize);
- size = min(size, maxSize);
- size = min(size, cDims.innerHeight - spacing);
- CSS.height = max(1, cssH(pane, size));
- s.size = size; // update state
- // make sure minSize is sufficient to avoid errors
- s.maxSize = maxSize; // init value
- s.minSize = max(minSize, size - CSS.height + 1); // = pane.outerHeight when css.height = 1px
- // handle IE6
- //if (isIE6) CSS.width = cssW($P, cDims.innerWidth);
- $P.css(CSS); // apply size & position
- }
- else
- if (dir == "vert") { // east or west pane
- if (size === 0 || size == "auto") {
- $P.css({
- width: "auto",
- float: "left"
- }); // float = FORCE pane to auto-size
- size = $P.outerWidth();
- $P.css({
- float: "none"
- }); // RESET
- }
- size = max(size, minSize);
- size = min(size, maxSize);
- size = min(size, cDims.innerWidth - spacing);
- CSS.width = max(1, cssW(pane, size));
- s.size = size; // update state
- s.maxSize = maxSize; // init value
- // make sure minSize is sufficient to avoid errors
- s.minSize = max(minSize, size - CSS.width + 1); // = pane.outerWidth when css.width = 1px
- $P.css(CSS); // apply size - top, bottom & height set by sizeMidPanes
- sizeMidPanes(pane, null, true); // true = onInit
- }
- else
- if (pane == "center") {
- $P.css(CSS); // top, left, width & height set by sizeMidPanes...
- sizeMidPanes("center", null, true); // true = onInit
- }
-
- // close or hide the pane if specified in settings
- if (o.initClosed && o.closable) {
- $P.hide().addClass("closed");
- s.isClosed = true;
- }
- else
- if (o.initHidden || o.initClosed) {
- hide(pane, true); // will be completely invisible - no resizer or spacing
- s.isHidden = true;
- }
- else
- $P.addClass("open");
-
- // check option for auto-handling of pop-ups & drop-downs
- if (o.showOverflowOnHover)
- $P.hover(allowOverflow, resetOverflow);
-
- /*
- * see if this pane has a 'content element' that we need to auto-size
- */
- if (o.contentSelector) {
- $C = $Cs[pane] = $P.children(o.contentSelector + ":first"); // match 1-element only
- if (!$C.length) {
- $Cs[pane] = false;
- return true; // SKIP to next
- }
- $C.css(c.content.cssReq);
- if (o.applyDefaultStyles)
- $C.css(c.content.cssDef); // cosmetic defaults
- // NO PANE-SCROLLING when there is a content-div
- $P.css({
- overflow: "hidden"
- });
- }
- });
- };
-
- /**
- * initHandles
- *
- * Initialize module objects, styling, size and position for all resize bars and toggler buttons
- *
- * @callers create()
- */
- var initHandles = function(){
- // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
- $.each(c.borderPanes.split(","), function(){
- var pane = str(this), o = options[pane], s = state[pane], rClass = o.resizerClass, tClass = o.togglerClass, $P = $Ps[pane];
- $Rs[pane] = false; // INIT
- $Ts[pane] = false;
-
- if (!$P || (!o.closable && !o.resizable))
- return; // pane does not exist - skip
- var edge = c[pane].edge, isOpen = $P.is(":visible"), spacing = (isOpen ? o.spacing_open : o.spacing_closed), _pane = "-" + pane // used for classNames
-, _state = (isOpen ? "-open" : "-closed") // used for classNames
-, $R, $T;
- // INIT RESIZER BAR
- $R = $Rs[pane] = $("<span></span>");
-
- if (isOpen && o.resizable)
- ; // this is handled by initResizable
- else
- if (!isOpen && o.slidable)
- $R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
-
- $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
- .attr("id", (o.paneSelector.substr(0, 1) == "#" ? o.paneSelector.substr(1) + "-resizer" : "")).attr("resizer", pane) // so we can read this from the resizer
-.css(c.resizers.cssReq) // add base/required styles
- // POSITION of resizer bar - allow for container border & padding
- .css(edge, cDims[edge] + getPaneSize(pane)) // ADD CLASSNAMES - eg: class="resizer resizer-west resizer-open"
- .addClass(rClass + " " + rClass + _pane + " " + rClass + _state + " " + rClass + _pane + _state).appendTo($Container) // append DIV to container
-;
- // ADD VISUAL STYLES
- if (o.applyDefaultStyles)
- $R.css(c.resizers.cssDef);
-
- if (o.closable) {
- // INIT COLLAPSER BUTTON
- $T = $Ts[pane] = $("<div></div>");
- $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-toggler"
- .attr("id", (o.paneSelector.substr(0, 1) == "#" ? o.paneSelector.substr(1) + "-toggler" : "")).css(c.togglers.cssReq) // add base/required styles
-.attr("title", (isOpen ? o.togglerTip_open : o.togglerTip_closed)).click(function(evt){
- toggle(pane);
- evt.stopPropagation();
- }).mouseover(function(evt){
- evt.stopPropagation();
- }) // prevent resizer event
- // ADD CLASSNAMES - eg: class="toggler toggler-west toggler-west-open"
- .addClass(tClass + " " + tClass + _pane + " " + tClass + _state + " " + tClass + _pane + _state).appendTo($R) // append SPAN to resizer DIV
-;
-
- // ADD INNER-SPANS TO TOGGLER
- if (o.togglerContent_open) // ui-layout-open
- $("<span>" + o.togglerContent_open + "</span>").addClass("content content-open").css("display", s.isClosed ? "none" : "block").appendTo($T);
- if (o.togglerContent_closed) // ui-layout-closed
- $("<span>" + o.togglerContent_closed + "</span>").addClass("content content-closed").css("display", s.isClosed ? "block" : "none").appendTo($T);
-
- // ADD BASIC VISUAL STYLES
- if (o.applyDefaultStyles)
- $T.css(c.togglers.cssDef);
-
- if (!isOpen)
- bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
- }
-
- });
-
- // SET ALL HANDLE SIZES & LENGTHS
- sizeHandles("all", true); // true = onInit
- };
-
- /**
- * initResizable
- *
- * Add resize-bars to all panes that specify it in options
- *
- * @dependancies $.fn.resizable - will abort if not found
- * @callers create()
- */
- var initResizable = function(){
- var draggingAvailable = (typeof $.fn.draggable == "function"), minPosition, maxPosition, edge // set in start()
-;
-
- $.each(c.borderPanes.split(","), function(){
- var pane = str(this), o = options[pane], s = state[pane];
- if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
- o.resizable = false;
- return true; // skip to next
- }
-
- var rClass = o.resizerClass // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
- , dragClass = rClass + "-drag" // resizer-drag
-, dragPaneClass = rClass + "-" + pane + "-drag" // resizer-north-drag
- // 'dragging' class is applied to the CLONED resizer-bar while it is being dragged
- , draggingClass = rClass + "-dragging" // resizer-dragging
-, draggingPaneClass = rClass + "-" + pane + "-dragging" // resizer-north-dragging
-, draggingClassSet = false // logic var
-, $P = $Ps[pane], $R = $Rs[pane];
-
- if (!s.isClosed)
- $R.attr("title", o.resizerTip).css("cursor", o.resizerCursor) // n-resize, s-resize, etc
-;
-
- $R.draggable({
- containment: $Container[0] // limit resizing to layout container
- ,
- axis: (c[pane].dir == "horz" ? "y" : "x") // limit resizing to horz or vert axis
- ,
- delay: 200,
- distance: 1 // basic format for helper - style it using class: .ui-draggable-dragging
- ,
- helper: "clone",
- opacity: o.resizerDragOpacity //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
- ,
- zIndex: c.zIndex.resizing,
- start: function(e, ui){
- // onresize_start callback - will CANCEL hide if returns false
- // TODO: CONFIRM that dragging can be cancelled like this???
- if (false === execUserCallback(pane, o.onresize_start))
- return false;
-
- s.isResizing = true; // prevent pane from closing while resizing
- clearTimer(pane, "closeSlider"); // just in case already triggered
- $R.addClass(dragClass + " " + dragPaneClass); // add drag classes
- draggingClassSet = false; // reset logic var - see drag()
- // SET RESIZING LIMITS - used in drag()
- var resizerWidth = (pane == "east" || pane == "south" ? o.spacing_open : 0);
- setPaneMinMaxSizes(pane); // update pane-state
- s.minPosition -= resizerWidth;
- s.maxPosition -= resizerWidth;
- edge = (c[pane].dir == "horz" ? "top" : "left");
-
- // MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
- $(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).each(function(){
- $('<div class="ui-layout-mask"/>').css({
- background: "#fff",
- opacity: "0.001",
- zIndex: 9,
- position: "absolute",
- width: this.offsetWidth + "px",
- height: this.offsetHeight + "px"
- }).css($(this).offset()) // top & left
-.appendTo(this.parentNode) // put div INSIDE pane to avoid zIndex issues
-;
- });
- },
- drag: function(e, ui){
- if (!draggingClassSet) { // can only add classes after clone has been added to the DOM
- $(".ui-draggable-dragging").addClass(draggingClass + " " + draggingPaneClass) // add dragging classes
-.children().css("visibility", "hidden") // hide toggler inside dragged resizer-bar
-;
- draggingClassSet = true;
- // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
- if (s.isSliding)
- $Ps[pane].css("zIndex", c.zIndex.sliding);
- }
- // CONTAIN RESIZER-BAR TO RESIZING LIMITS
- if (ui.position[edge] < s.minPosition)
- ui.position[edge] = s.minPosition;
- else
- if (ui.position[edge] > s.maxPosition)
- ui.position[edge] = s.maxPosition;
- },
- stop: function(e, ui){
- var dragPos = ui.position, resizerPos, newSize;
- $R.removeClass(dragClass + " " + dragPaneClass); // remove drag classes
- switch (pane) {
- case "north":
- resizerPos = dragPos.top;
- break;
- case "west":
- resizerPos = dragPos.left;
- break;
- case "south":
- resizerPos = cDims.outerHeight - dragPos.top - $R.outerHeight();
- break;
- case "east":
- resizerPos = cDims.outerWidth - dragPos.left - $R.outerWidth();
- break;
- }
- // remove container margin from resizer position to get the pane size
- newSize = resizerPos - cDims[c[pane].edge];
-
- sizePane(pane, newSize);
-
- // UN-MASK PANES MASKED IN drag.start
- $("div.ui-layout-mask").remove(); // Remove iframe masks
- s.isResizing = false;
- }
-
- });
- });
- };
-
-
-
- /*
- * ###########################
- * ACTION METHODS
- * ###########################
- */
- /**
- * hide / show
- *
- * Completely 'hides' a pane, including its spacing - as if it does not exist
- * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
- *
- * @param String pane The pane being hidden, ie: north, south, east, or west
- */
- var hide = function(pane, onInit){
- var o = options[pane], s = state[pane], $P = $Ps[pane], $R = $Rs[pane];
- if (!$P || s.isHidden)
- return; // pane does not exist OR is already hidden
- // onhide_start callback - will CANCEL hide if returns false
- if (false === execUserCallback(pane, o.onhide_start))
- return;
-
- s.isSliding = false; // just in case
- // now hide the elements
- if ($R)
- $R.hide(); // hide resizer-bar
- if (onInit || s.isClosed) {
- s.isClosed = true; // to trigger open-animation on show()
- s.isHidden = true;
- $P.hide(); // no animation when loading page
- sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
- execUserCallback(pane, o.onhide_end || o.onhide);
- }
- else {
- s.isHiding = true; // used by onclose
- close(pane, false); // adjust all panes to fit
- //s.isHidden = true; - will be set by close - if not cancelled
- }
- };
-
- var show = function(pane, openPane){
- var o = options[pane], s = state[pane], $P = $Ps[pane], $R = $Rs[pane];
- if (!$P || !s.isHidden)
- return; // pane does not exist OR is not hidden
- // onhide_start callback - will CANCEL hide if returns false
- if (false === execUserCallback(pane, o.onshow_start))
- return;
-
- s.isSliding = false; // just in case
- s.isShowing = true; // used by onopen/onclose
- //s.isHidden = false; - will be set by open/close - if not cancelled
-
- // now show the elements
- if ($R && o.spacing_open > 0)
- $R.show();
- if (openPane === false)
- close(pane, true); // true = force
- else
- open(pane); // adjust all panes to fit
- };
-
-
- /**
- * toggle
- *
- * Toggles a pane open/closed by calling either open or close
- *
- * @param String pane The pane being toggled, ie: north, south, east, or west
- */
- var toggle = function(pane){
- var s = state[pane];
- if (s.isHidden)
- show(pane); // will call 'open' after unhiding it
- else
- if (s.isClosed)
- open(pane);
- else
- close(pane);
- };
-
- /**
- * close
- *
- * Close the specified pane (animation optional), and resize all other panes as needed
- *
- * @param String pane The pane being closed, ie: north, south, east, or west
- */
- var close = function(pane, force, noAnimation){
- var $P = $Ps[pane], $R = $Rs[pane], $T = $Ts[pane], o = options[pane], s = state[pane], doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none"), edge = c[pane].edge, rClass = o.resizerClass, tClass = o.togglerClass, _pane = "-" + pane // used for classNames
-, _open = "-open", _sliding = "-sliding", _closed = "-closed" // transfer logic vars to temp vars
- , isShowing = s.isShowing, isHiding = s.isHiding;
- // now clear the logic vars
- delete s.isShowing;
- delete s.isHiding;
-
- if (!$P || (!o.resizable && !o.closable))
- return; // invalid request
- else
- if (!force && s.isClosed && !isShowing)
- return; // already closed
- if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
- setFlowCallback("close", pane, force); // set a callback for this action, if possible
- return; // ABORT
- }
-
- // onclose_start callback - will CANCEL hide if returns false
- // SKIP if just 'showing' a hidden pane as 'closed'
- if (!isShowing && false === execUserCallback(pane, o.onclose_start))
- return;
-
- // SET flow-control flags
- c[pane].isMoving = true;
- c.isLayoutBusy = true;
-
- s.isClosed = true;
- // update isHidden BEFORE sizing panes
- if (isHiding)
- s.isHidden = true;
- else
- if (isShowing)
- s.isHidden = false;
-
- // sync any 'pin buttons'
- syncPinBtns(pane, false);
-
- // resize panes adjacent to this one
- if (!s.isSliding)
- sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
-
- // if this pane has a resizer bar, move it now
- if ($R) {
- $R.css(edge, cDims[edge]) // move the resizer bar
-.removeClass(rClass + _open + " " + rClass + _pane + _open).removeClass(rClass + _sliding + " " + rClass + _pane + _sliding).addClass(rClass + _closed + " " + rClass + _pane + _closed);
- // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent
- if (o.resizable)
- $R.draggable("disable").css("cursor", "default").attr("title", "");
- // if pane has a toggler button, adjust that too
- if ($T) {
- $T.removeClass(tClass + _open + " " + tClass + _pane + _open).addClass(tClass + _closed + " " + tClass + _pane + _closed).attr("title", o.togglerTip_closed) // may be blank
-;
- }
- sizeHandles(); // resize 'length' and position togglers for adjacent panes
- }
-
- // ANIMATE 'CLOSE' - if no animation, then was ALREADY shown above
- if (doFX) {
- lockPaneForFX(pane, true); // need to set left/top so animation will work
- $P.hide(o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function(){
- lockPaneForFX(pane, false); // undo
- if (!s.isClosed)
- return; // pane was opened before animation finished!
- close_2();
- });
- }
- else {
- $P.hide(); // just hide pane NOW
- close_2();
- }
-
- // SUBROUTINE
- function close_2(){
- bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
- // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
- if (!isShowing)
- execUserCallback(pane, o.onclose_end || o.onclose);
- // onhide OR onshow callback
- if (isShowing)
- execUserCallback(pane, o.onshow_end || o.onshow);
- if (isHiding)
- execUserCallback(pane, o.onhide_end || o.onhide);
-
- // internal flow-control callback
- execFlowCallback(pane);
- }
- };
-
- /**
- * open
- *
- * Open the specified pane (animation optional), and resize all other panes as needed
- *
- * @param String pane The pane being opened, ie: north, south, east, or west
- */
- var open = function(pane, slide, noAnimation){
- var $P = $Ps[pane], $R = $Rs[pane], $T = $Ts[pane], o = options[pane], s = state[pane], doFX = !noAnimation && s.isClosed && (o.fxName_open != "none"), edge = c[pane].edge, rClass = o.resizerClass, tClass = o.togglerClass, _pane = "-" + pane // used for classNames
-, _open = "-open", _closed = "-closed", _sliding = "-sliding" // transfer logic var to temp var
- , isShowing = s.isShowing;
- // now clear the logic var
- delete s.isShowing;
-
- if (!$P || (!o.resizable && !o.closable))
- return; // invalid request
- else
- if (!s.isClosed && !s.isSliding)
- return; // already open
- // pane can ALSO be unhidden by just calling show(), so handle this scenario
- if (s.isHidden && !isShowing) {
- show(pane, true);
- return;
- }
-
- if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
- setFlowCallback("open", pane, slide); // set a callback for this action, if possible
- return; // ABORT
- }
-
- // onopen_start callback - will CANCEL hide if returns false
- if (false === execUserCallback(pane, o.onopen_start))
- return;
-
- // SET flow-control flags
- c[pane].isMoving = true;
- c.isLayoutBusy = true;
-
- // 'PIN PANE' - stop sliding
- if (s.isSliding && !slide) // !slide = 'open pane normally' - NOT sliding
- bindStopSlidingEvents(pane, false); // will set isSliding=false
- s.isClosed = false;
- // update isHidden BEFORE sizing panes
- if (isShowing)
- s.isHidden = false;
-
- // Container size may have changed - shrink the pane if now 'too big'
- setPaneMinMaxSizes(pane); // update pane-state
- if (s.size > s.maxSize) // pane is too big! resize it before opening
- $P.css(c[pane].sizeType, max(1, cssSize(pane, s.maxSize)));
-
- bindStartSlidingEvent(pane, false); // remove trigger event from resizer-bar
- if (doFX) { // ANIMATE
- lockPaneForFX(pane, true); // need to set left/top so animation will work
- $P.show(o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function(){
- lockPaneForFX(pane, false); // undo
- if (s.isClosed)
- return; // pane was closed before animation finished!
- open_2(); // continue
- });
- }
- else {// no animation
- $P.show(); // just show pane and...
- open_2(); // continue
- }
-
- // SUBROUTINE
- function open_2(){
- // NOTE: if isSliding, then other panes are NOT 'resized'
- if (!s.isSliding) // resize all panes adjacent to this one
- sizeMidPanes(c[pane].dir == "vert" ? "center" : "all");
-
- // if this pane has a toggler, move it now
- if ($R) {
- $R.css(edge, cDims[edge] + getPaneSize(pane)) // move the toggler
-.removeClass(rClass + _closed + " " + rClass + _pane + _closed).addClass(rClass + _open + " " + rClass + _pane + _open).addClass(!s.isSliding ? "" : rClass + _sliding + " " + rClass + _pane + _sliding);
- if (o.resizable)
- $R.draggable("enable").css("cursor", o.resizerCursor).attr("title", o.resizerTip);
- else
- $R.css("cursor", "default"); // n-resize, s-resize, etc
- // if pane also has a toggler button, adjust that too
- if ($T) {
- $T.removeClass(tClass + _closed + " " + tClass + _pane + _closed).addClass(tClass + _open + " " + tClass + _pane + _open).attr("title", o.togglerTip_open) // may be blank
-;
- }
- sizeHandles("all"); // resize resizer & toggler sizes for all panes
- }
-
- // resize content every time pane opens - to be sure
- sizeContent(pane);
-
- // sync any 'pin buttons'
- syncPinBtns(pane, !s.isSliding);
-
- // onopen callback
- execUserCallback(pane, o.onopen_end || o.onopen);
-
- // onshow callback
- if (isShowing)
- execUserCallback(pane, o.onshow_end || o.onshow);
-
- // internal flow-control callback
- execFlowCallback(pane);
- }
- };
-
-
- /**
- * lockPaneForFX
- *
- * Must set left/top on East/South panes so animation will work properly
- *
- * @param String pane The pane to lock, 'east' or 'south' - any other is ignored!
- * @param Boolean doLock true = set left/top, false = remove
- */
- var lockPaneForFX = function(pane, doLock){
- var $P = $Ps[pane];
- if (doLock) {
- $P.css({
- zIndex: c.zIndex.animation
- }); // overlay all elements during animation
- if (pane == "south")
- $P.css({
- top: cDims.top + cDims.innerHeight - $P.outerHeight()
- });
- else
- if (pane == "east")
- $P.css({
- left: cDims.left + cDims.innerWidth - $P.outerWidth()
- });
- }
- else {
- if (!state[pane].isSliding)
- $P.css({
- zIndex: c.zIndex.pane_normal
- });
- if (pane == "south")
- $P.css({
- top: "auto"
- });
- else
- if (pane == "east")
- $P.css({
- left: "auto"
- });
- }
- };
-
-
- /**
- * bindStartSlidingEvent
- *
- * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
- *
- * @callers open(), close()
- * @param String pane The pane to enable/disable, 'north', 'south', etc.
- * @param Boolean enable Enable or Disable sliding?
- */
- var bindStartSlidingEvent = function(pane, enable){
- var o = options[pane], $R = $Rs[pane], trigger = o.slideTrigger_open;
- if (!$R || !o.slidable)
- return;
- // make sure we have a valid event
- if (trigger != "click" && trigger != "dblclick" && trigger != "mouseover")
- trigger = "click";
- $R // add or remove trigger event
- [enable ? "bind" : "unbind"](trigger, slideOpen) // set the appropriate cursor & title/tip
- .css("cursor", (enable ? o.sliderCursor : "default")).attr("title", (enable ? o.sliderTip : ""));
- };
-
- /**
- * bindStopSlidingEvents
- *
- * Add or remove 'mouseout' events to 'slide close' when pane is 'sliding' open or closed
- * Also increases zIndex when pane is sliding open
- * See bindStartSlidingEvent for code to control 'slide open'
- *
- * @callers slideOpen(), slideClosed()
- * @param String pane The pane to process, 'north', 'south', etc.
- * @param Boolean isOpen Is pane open or closed?
- */
- var bindStopSlidingEvents = function(pane, enable){
- var o = options[pane], s = state[pane], trigger = o.slideTrigger_close, action = (enable ? "bind" : "unbind") // can't make 'unbind' work! - see disabled code below
-, $P = $Ps[pane], $R = $Rs[pane];
-
- s.isSliding = enable; // logic
- clearTimer(pane, "closeSlider"); // just in case
- // raise z-index when sliding
- $P.css({
- zIndex: (enable ? c.zIndex.sliding : c.zIndex.pane_normal)
- });
- $R.css({
- zIndex: (enable ? c.zIndex.sliding : c.zIndex.resizer_normal)
- });
-
- // make sure we have a valid event
- if (trigger != "click" && trigger != "mouseout")
- trigger = "mouseout";
-
- // when trigger is 'mouseout', must cancel timer when mouse moves between 'pane' and 'resizer'
- if (enable) { // BIND trigger events
- $P.bind(trigger, slideClosed);
- $R.bind(trigger, slideClosed);
- if (trigger = "mouseout") {
- $P.bind("mouseover", cancelMouseOut);
- $R.bind("mouseover", cancelMouseOut);
- }
- }
- else { // UNBIND trigger events
- // TODO: why does unbind of a 'single function' not work reliably?
- //$P[action](trigger, slideClosed );
- $P.unbind(trigger);
- $R.unbind(trigger);
- if (trigger = "mouseout") {
- //$P[action]("mouseover", cancelMouseOut );
- $P.unbind("mouseover");
- $R.unbind("mouseover");
- clearTimer(pane, "closeSlider");
- }
- }
-
- // SUBROUTINE for mouseout timer clearing
- function cancelMouseOut(evt){
- clearTimer(pane, "closeSlider");
- evt.stopPropagation();
- }
- };
-
- var slideOpen = function(){
- var pane = $(this).attr("resizer"); // attr added by initHandles
- if (state[pane].isClosed) { // skip if already open!
- bindStopSlidingEvents(pane, true); // pane is opening, so BIND trigger events to close it
- open(pane, true); // true = slide - ie, called from here!
- }
- };
-
- var slideClosed = function(){
- var $E = $(this), pane = $E.attr("pane") || $E.attr("resizer"), o = options[pane], s = state[pane];
- if (s.isClosed || s.isResizing)
- return; // skip if already closed OR in process of resizing
- else
- if (o.slideTrigger_close == "click")
- close_NOW(); // close immediately onClick
- else // trigger = mouseout - use a delay
- setTimer(pane, "closeSlider", close_NOW, 300); // .3 sec delay
- // SUBROUTINE for timed close
- function close_NOW(){
- bindStopSlidingEvents(pane, false); // pane is being closed, so UNBIND trigger events
- if (!s.isClosed)
- close(pane); // skip if already closed!
- }
- };
-
-
- /**
- * sizePane
- *
- * @callers initResizable.stop()
- * @param String pane The pane being resized - usually west or east, but potentially north or south
- * @param Integer newSize The new size for this pane - will be validated
- */
- var sizePane = function(pane, size){
- // TODO: accept "auto" as size, and size-to-fit pane content
- var edge = c[pane].edge, dir = c[pane].dir, o = options[pane], s = state[pane], $P = $Ps[pane], $R = $Rs[pane];
- // calculate 'current' min/max sizes
- setPaneMinMaxSizes(pane); // update pane-state
- // compare/update calculated min/max to user-options
- s.minSize = max(s.minSize, o.minSize);
- if (o.maxSize > 0)
- s.maxSize = min(s.maxSize, o.maxSize);
- // validate passed size
- size = max(size, s.minSize);
- size = min(size, s.maxSize);
- s.size = size; // update state
- // move the resizer bar and resize the pane
- $R.css(edge, size + cDims[edge]);
- $P.css(c[pane].sizeType, max(1, cssSize(pane, size)));
-
- // resize all the adjacent panes, and adjust their toggler buttons
- if (!s.isSliding)
- sizeMidPanes(dir == "horz" ? "all" : "center");
- sizeHandles();
- sizeContent(pane);
- execUserCallback(pane, o.onresize_end || o.onresize);
- };
-
- /**
- * sizeMidPanes
- *
- * @callers create(), open(), close(), onWindowResize()
- */
- var sizeMidPanes = function(panes, overrideDims, onInit){
- if (!panes || panes == "all")
- panes = "east,west,center";
-
- var d = getPaneDims();
- if (overrideDims)
- $.extend(d, overrideDims);
-
- $.each(panes.split(","), function(){
- if (!$Ps[this])
- return; // NO PANE - skip
- var pane = str(this), o = options[pane], s = state[pane], $P = $Ps[pane], $R = $Rs[pane], hasRoom = true, CSS = {};
-
- if (pane == "center") {
- d = getPaneDims(); // REFRESH Dims because may have just 'unhidden' East or West pane after a 'resize'
- CSS = $.extend({}, d); // COPY ALL of the paneDims
- CSS.width = max(1, cssW(pane, CSS.width));
- CSS.height = max(1, cssH(pane, CSS.height));
- hasRoom = (CSS.width > 1 && CSS.height > 1);
- /*
- * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
- * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
- */
- if ($.browser.msie && (!$.boxModel || $.browser.version < 7)) {
- if ($Ps.north)
- $Ps.north.css({
- width: cssW($Ps.north, cDims.innerWidth)
- });
- if ($Ps.south)
- $Ps.south.css({
- width: cssW($Ps.south, cDims.innerWidth)
- });
- }
- }
- else { // for east and west, set only the height
- CSS.top = d.top;
- CSS.bottom = d.bottom;
- CSS.height = max(1, cssH(pane, d.height));
- hasRoom = (CSS.height > 1);
- }
-
- if (hasRoom) {
- $P.css(CSS);
- if (s.noRoom) {
- s.noRoom = false;
- if (s.isHidden)
- return;
- else
- show(pane, !s.isClosed);
- /* OLD CODE - keep until sure line above works right!
- if (!s.isClosed) $P.show(); // in case was previously hidden due to NOT hasRoom
- if ($R) $R.show();
- */
- }
- if (!onInit) {
- sizeContent(pane);
- execUserCallback(pane, o.onresize_end || o.onresize);
- }
- }
- else
- if (!s.noRoom) { // no room for pane, so just hide it (if not already)
- s.noRoom = true; // update state
- if (s.isHidden)
- return;
- if (onInit) { // skip onhide callback and other logic onLoad
- $P.hide();
- if ($R)
- $R.hide();
- }
- else
- hide(pane);
- }
- });
- };
-
-
- var sizeContent = function(panes){
- if (!panes || panes == "all")
- panes = c.allPanes;
-
- $.each(panes.split(","), function(){
- if (!$Cs[this])
- return; // NO CONTENT - skip
- var pane = str(this), ignore = options[pane].contentIgnoreSelector, $P = $Ps[pane], $C = $Cs[pane], e_C = $C[0] // DOM element
-, height = cssH($P); // init to pane.innerHeight
- ;
- $P.children().each(function(){
- if (this == e_C)
- return; // Content elem - skip
- var $E = $(this);
- if (!ignore || !$E.is(ignore))
- height -= $E.outerHeight();
- });
- if (height > 0)
- height = cssH($C, height);
- if (height < 1)
- $C.hide(); // no room for content!
- else
- $C.css({
- height: height
- }).show();
- });
- };
-
-
- /**
- * sizeHandles
- *
- * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
- *
- * @callers initHandles(), open(), close(), resizeAll()
- */
- var sizeHandles = function(panes, onInit){
- if (!panes || panes == "all")
- panes = c.borderPanes;
-
- $.each(panes.split(","), function(){
- var pane = str(this), o = options[pane], s = state[pane], $P = $Ps[pane], $R = $Rs[pane], $T = $Ts[pane];
- if (!$P || !$R || (!o.resizable && !o.closable))
- return; // skip
- var dir = c[pane].dir, _state = (s.isClosed ? "_closed" : "_open"), spacing = o["spacing" + _state], togAlign = o["togglerAlign" + _state], togLen = o["togglerLength" + _state], paneLen, offset, CSS = {};
- if (spacing == 0) {
- $R.hide();
- return;
- }
- else
- if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
- $R.show(); // in case was previously hidden
- // Resizer Bar is ALWAYS same width/height of pane it is attached to
- if (dir == "horz") { // north/south
- paneLen = $P.outerWidth();
- $R.css({
- width: max(1, cssW($R, paneLen)) // account for borders & padding
- ,
- height: max(1, cssH($R, spacing)) // ditto
- ,
- left: cssNum($P, "left")
- });
- }
- else { // east/west
- paneLen = $P.outerHeight();
- $R.css({
- height: max(1, cssH($R, paneLen)) // account for borders & padding
- ,
- width: max(1, cssW($R, spacing)) // ditto
- ,
- top: cDims.top + getPaneSize("north", true)
- //, top: cssNum($Ps["center"], "top")
- });
-
- }
-
- if ($T) {
- if (togLen == 0 || (s.isSliding && o.hideTogglerOnSlide)) {
- $T.hide(); // always HIDE the toggler when 'sliding'
- return;
- }
- else
- $T.show(); // in case was previously hidden
- if (!(togLen > 0) || togLen == "100%" || togLen > paneLen) {
- togLen = paneLen;
- offset = 0;
- }
- else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
- if (typeof togAlign == "string") {
- switch (togAlign) {
- case "top":
- case "left":
- offset = 0;
- break;
- case "bottom":
- case "right":
- offset = paneLen - togLen;
- break;
- case "middle":
- case "center":
- default:
- offset = Math.floor((paneLen - togLen) / 2); // 'default' catches typos
- }
- }
- else { // togAlign = number
- var x = parseInt(togAlign); //
- if (togAlign >= 0)
- offset = x;
- else
- offset = paneLen - togLen + x; // NOTE: x is negative!
- }
- }
-
- var $TC_o = (o.togglerContent_open ? $T.children(".content-open") : false), $TC_c = (o.togglerContent_closed ? $T.children(".content-closed") : false), $TC = (s.isClosed ? $TC_c : $TC_o);
- if ($TC_o)
- $TC_o.css("display", s.isClosed ? "none" : "block");
- if ($TC_c)
- $TC_c.css("display", s.isClosed ? "block" : "none");
-
- if (dir == "horz") { // north/south
- var width = cssW($T, togLen);
- $T.css({
- width: max(0, width) // account for borders & padding
- ,
- height: max(1, cssH($T, spacing)) // ditto
- ,
- left: offset // TODO: VERIFY that toggler positions correctly for ALL values
- });
- if ($TC) // CENTER the toggler content SPAN
- $TC.css("marginLeft", Math.floor((width - $TC.outerWidth()) / 2)); // could be negative
- }
- else { // east/west
- var height = cssH($T, togLen);
- $T.css({
- height: max(0, height) // account for borders & padding
- ,
- width: max(1, cssW($T, spacing)) // ditto
- ,
- top: offset // POSITION the toggler
- });
- if ($TC) // CENTER the toggler content SPAN
- $TC.css("marginTop", Math.floor((height - $TC.outerHeight()) / 2)); // could be negative
- }
-
-
- }
-
- // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
- if (onInit && o.initHidden) {
- $R.hide();
- if ($T)
- $T.hide();
- }
- });
- };
-
-
- /**
- * resizeAll
- *
- * @callers window.onresize(), callbacks or custom code
- */
- var resizeAll = function(){
-
- console.log("RESIZE!");
- console.log($Container);
- var oldW = cDims.innerWidth, oldH = cDims.innerHeight;
- cDims = state.container = getElemDims($Container); // UPDATE container dimensions
- var checkH = (cDims.innerHeight < oldH), checkW = (cDims.innerWidth < oldW), s, dir;
-
- if (checkH || checkW)
- // NOTE special order for sizing: S-N-E-W
- $.each(["south", "north", "east", "west"], function(i, pane){
- console.log(this);
- s = state[pane];
- dir = c[pane].dir;
- if (!s.isClosed && ((checkH && dir == "horz") || (checkW && dir == "vert"))) {
- setPaneMinMaxSizes(pane); // update pane-state
- // shrink pane if 'too big' to fit
- if (s.size > s.maxSize)
- console.log("sizePane: " + pane);
- sizePane(pane, s.maxSize);
- }
- });
-
- sizeMidPanes("all");
- sizeHandles("all"); // reposition the toggler elements
- };
-
-
- /**
- * keyDown
- *
- * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
- *
- * @callers document.keydown()
- */
- function keyDown(evt){
- if (!evt)
- return true;
- var code = evt.keyCode;
- if (code < 33)
- return true; // ignore special keys: ENTER, TAB, etc
- var PANE = {
- 38: "north" // Up Cursor
- ,
- 40: "south" // Down Cursor
- ,
- 37: "west" // Left Cursor
- ,
- 39: "east" // Right Cursor
- }, isCursorKey = (code >= 37 && code <= 40), ALT = evt.altKey // no worky!
-, SHIFT = evt.shiftKey, CTRL = evt.ctrlKey, pane = false, s, o, k, m, el;
-
- if (!CTRL && !SHIFT)
- return true; // no modifier key - abort
- else
- if (isCursorKey && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
- pane = PANE[code];
- else // check to see if this matches a custom-hotkey
- $.each(c.borderPanes.split(","), function(i, p){ // loop each pane to check its hotkey
- o = options[p];
- k = o.customHotkey;
- m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
- if ((SHIFT && m == "SHIFT") || (CTRL && m == "CTRL") || (CTRL && SHIFT)) { // Modifier matches
- if (k && code == (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
- pane = p;
- return false; // BREAK
- }
- }
- });
-
- if (!pane)
- return true; // no hotkey - abort
- // validate pane
- o = options[pane]; // get pane options
- s = state[pane]; // get pane options
- if (!o.enableCursorHotkey || s.isHidden || !$Ps[pane])
- return true;
-
- // see if user is in a 'form field' because may be 'selecting text'!
- el = evt.target || evt.srcElement;
- if (el && SHIFT && isCursorKey && (el.tagName == "TEXTAREA" || (el.tagName == "INPUT" && (code == 37 || code == 39))))
- return true; // allow text-selection
- // SYNTAX NOTES
- // use "returnValue=false" to abort keystroke but NOT abort function - can run another command afterwards
- // use "return false" to abort keystroke AND abort function
- toggle(pane);
- evt.stopPropagation();
- evt.returnValue = false; // CANCEL key
- return false;
- };
-
-
- /*
- * ###########################
- * UTILITY METHODS
- * called externally only
- * ###########################
- */
- function allowOverflow(elem){
- if (this && this.tagName)
- elem = this; // BOUND to element
- var $P;
- if (typeof elem == "string")
- $P = $Ps[elem];
- else {
- if ($(elem).attr("pane"))
- $P = $(elem);
- else
- $P = $(elem).parents("div[pane]:first");
- }
- if (!$P.length)
- return; // INVALID
- var pane = $P.attr("pane"), s = state[pane];
-
- // if pane is already raised, then reset it before doing it again!
- // this would happen if allowOverflow is attached to BOTH the pane and an element
- if (s.cssSaved)
- resetOverflow(pane); // reset previous CSS before continuing
- // if pane is raised by sliding or resizing, or it's closed, then abort
- if (s.isSliding || s.isResizing || s.isClosed) {
- s.cssSaved = false;
- return;
- }
-
- var newCSS = {
- zIndex: (c.zIndex.pane_normal + 1)
- }, curCSS = {}, of = $P.css("overflow"), ofX = $P.css("overflowX"), ofY = $P.css("overflowY");
- // determine which, if any, overflow settings need to be changed
- if (of != "visible") {
- curCSS.overflow = of;
- newCSS.overflow = "visible";
- }
- if (ofX && ofX != "visible" && ofX != "auto") {
- curCSS.overflowX = ofX;
- newCSS.overflowX = "visible";
- }
- if (ofY && ofY != "visible" && ofY != "auto") {
- curCSS.overflowY = ofX;
- newCSS.overflowY = "visible";
- }
-
- // save the current overflow settings - even if blank!
- s.cssSaved = curCSS;
-
- // apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
- $P.css(newCSS);
-
- // make sure the zIndex of all other panes is normal
- $.each(c.allPanes.split(","), function(i, p){
- if (p != pane)
- resetOverflow(p);
- });
-
- };
-
- function resetOverflow(elem){
- if (this && this.tagName)
- elem = this; // BOUND to element
- var $P;
- if (typeof elem == "string")
- $P = $Ps[elem];
- else {
- if ($(elem).hasClass("ui-layout-pane"))
- $P = $(elem);
- else
- $P = $(elem).parents("div[pane]:first");
- }
- if (!$P.length)
- return; // INVALID
- var pane = $P.attr("pane"), s = state[pane], CSS = s.cssSaved ||
- {};
- // reset the zIndex
- if (!s.isSliding && !s.isResizing)
- $P.css("zIndex", c.zIndex.pane_normal);
-
- // reset Overflow - if necessary
- $P.css(CSS);
-
- // clear var
- s.cssSaved = false;
- };
-
-
- /**
- * getBtn
- *
- * Helper function to validate params received by addButton utilities
- *
- * @param String selector jQuery selector for button, eg: ".ui-layout-north .toggle-button"
- * @param String pane Name of the pane the button is for: 'north', 'south', etc.
- * @returns If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise 'false'
- */
- function getBtn(selector, pane, action){
- var $E = $(selector), err = "Error Adding Button \n\nInvalid ";
- if (!$E.length) // element not found
- alert(err + "selector: " + selector);
- else
- if (c.borderPanes.indexOf(pane) == -1) // invalid 'pane' sepecified
- alert(err + "pane: " + pane);
- else { // VALID
- var btn = options[pane].buttonClass + "-" + action;
- $E.addClass(btn + " " + btn + "-" + pane);
- return $E;
- }
- return false; // INVALID
- };
-
-
- /**
- * addToggleBtn
- *
- * Add a custom Toggler button for a pane
- *
- * @param String selector jQuery selector for button, eg: ".ui-layout-north .toggle-button"
- * @param String pane Name of the pane the button is for: 'north', 'south', etc.
- */
- function addToggleBtn(selector, pane){
- var $E = getBtn(selector, pane, "toggle");
- if ($E)
- $E.attr("title", state[pane].isClosed ? "Open" : "Close").click(function(evt){
- toggle(pane);
- evt.stopPropagation();
- });
- };
-
- /**
- * addOpenBtn
- *
- * Add a custom Open button for a pane
- *
- * @param String selector jQuery selector for button, eg: ".ui-layout-north .open-button"
- * @param String pane Name of the pane the button is for: 'north', 'south', etc.
- */
- function addOpenBtn(selector, pane){
- var $E = getBtn(selector, pane, "open");
- if ($E)
- $E.attr("title", "Open").click(function(evt){
- open(pane);
- evt.stopPropagation();
- });
- };
-
- /**
- * addCloseBtn
- *
- * Add a custom Close button for a pane
- *
- * @param String selector jQuery selector for button, eg: ".ui-layout-north .close-button"
- * @param String pane Name of the pane the button is for: 'north', 'south', etc.
- */
- function addCloseBtn(selector, pane){
- var $E = getBtn(selector, pane, "close");
- if ($E)
- $E.attr("title", "Close").click(function(evt){
- close(pane);
- evt.stopPropagation();
- });
- };
-
- /**
- * addPinBtn
- *
- * Add a custom Pin button for a pane
- *
- * Four classes are added to the element, based on the paneClass for the associated pane...
- * Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
- * - ui-layout-pane-pin
- * - ui-layout-pane-west-pin
- * - ui-layout-pane-pin-up
- * - ui-layout-pane-west-pin-up
- *
- * @param String selector jQuery selector for button, eg: ".ui-layout-north .ui-layout-pin"
- * @param String pane Name of the pane the pin is for: 'north', 'south', etc.
- */
- function addPinBtn(selector, pane){
- var $E = getBtn(selector, pane, "pin");
- if ($E) {
- var s = state[pane];
- $E.click(function(evt){
- setPinState($(this), pane, (s.isSliding || s.isClosed));
- if (s.isSliding || s.isClosed)
- open(pane); // change from sliding to open
- else
- close(pane); // slide-closed
- evt.stopPropagation();
- });
- // add up/down pin attributes and classes
- setPinState($E, pane, (!s.isClosed && !s.isSliding));
- // add this pin to the pane data so we can 'sync it' automatically
- // PANE.pins key is an array so we can store multiple pins for each pane
- c[pane].pins.push(selector); // just save the selector string
- }
- };
-
- /**
- * syncPinBtns
- *
- * INTERNAL function to sync 'pin buttons' when pane is opened or closed
- * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
- *
- * @callers open(), close()
- * @params pane These are the params returned to callbacks by layout()
- * @params doPin True means set the pin 'down', False means 'up'
- */
- function syncPinBtns(pane, doPin){
- $.each(c[pane].pins, function(i, selector){
- setPinState($(selector), pane, doPin);
- });
- };
-
- /**
- * setPinState
- *
- * Change the class of the pin button to make it look 'up' or 'down'
- *
- * @callers addPinBtn(), syncPinBtns()
- * @param Element $Pin The pin-span element in a jQuery wrapper
- * @param Boolean doPin True = set the pin 'down', False = set it 'up'
- * @param String pinClass The root classname for pins - will add '-up' or '-down' suffix
- */
- function setPinState($Pin, pane, doPin){
- var updown = $Pin.attr("pin");
- if (updown && doPin == (updown == "down"))
- return; // already in correct state
- var root = options[pane].buttonClass, class1 = root + "-pin", class2 = class1 + "-" + pane, UP1 = class1 + "-up", UP2 = class2 + "-up", DN1 = class1 + "-down", DN2 = class2 + "-down";
- $Pin.attr("pin", doPin ? "down" : "up") // logic
-.attr("title", doPin ? "Un-Pin" : "Pin").removeClass(doPin ? UP1 : DN1).removeClass(doPin ? UP2 : DN2).addClass(doPin ? DN1 : UP1).addClass(doPin ? DN2 : UP2);
- };
-
-
- /*
- * ###########################
- * CREATE/RETURN BORDER-LAYOUT
- * ###########################
- */
- // init global vars
- var $Container = $(this).css({
- overflow: "hidden"
- }) // Container elem
-, $Ps = {} // Panes x4 - set in initPanes()
-, $Cs = {} // Content x4 - set in initPanes()
-, $Rs = {} // Resizers x4 - set in initHandles()
-, $Ts = {} // Togglers x4 - set in initHandles()
- // object aliases
- , c = config // alias for config hash
-, cDims = state.container // alias for easy access to 'container dimensions'
-;
-
- // create the border layout NOW
- create();
-
- // return object pointers to expose data & option Properties, and primary action Methods
- return {
- options: options // property - options hash
- ,
- state: state // property - dimensions hash
- ,
- panes: $Ps // property - object pointers for ALL panes: panes.north, panes.center
- ,
- toggle: toggle // method - pass a 'pane' ("north", "west", etc)
- ,
- open: open // method - ditto
- ,
- close: close // method - ditto
- ,
- hide: hide // method - ditto
- ,
- show: show // method - ditto
- ,
- resizeContent: sizeContent // method - ditto
- ,
- sizePane: sizePane // method - pass a 'pane' AND a 'size' in pixels
- ,
- resizeAll: resizeAll // method - no parameters
- ,
- addToggleBtn: addToggleBtn // utility - pass element selector and 'pane'
- ,
- addOpenBtn: addOpenBtn // utility - ditto
- ,
- addCloseBtn: addCloseBtn // utility - ditto
- ,
- addPinBtn: addPinBtn // utility - ditto
- ,
- allowOverflow: allowOverflow // utility - pass calling element
- ,
- resetOverflow: resetOverflow // utility - ditto
- ,
- cssWidth: cssW,
- cssHeight: cssH
- };
-
- }
-})(jQuery);
+$.fn.layout = function (opts) {
+
+/*
+ * ###########################
+ * WIDGET CONFIG & OPTIONS
+ * ###########################
+ */
+
+ // DEFAULTS for options
+ var
+ prefix = "ui-layout-" // prefix for ALL selectors and classNames
+ , defaults = { // misc default values
+ paneClass: prefix+"pane" // ui-layout-pane
+ , resizerClass: prefix+"resizer" // ui-layout-resizer
+ , togglerClass: prefix+"toggler" // ui-layout-toggler
+ , togglerInnerClass: prefix+"" // ui-layout-open / ui-layout-closed
+ , buttonClass: prefix+"button" // ui-layout-button
+ , contentSelector: "."+prefix+"content"// ui-layout-content
+ , contentIgnoreSelector: "."+prefix+"ignore" // ui-layout-mask
+ }
+ ;
+
+ // DEFAULT PANEL OPTIONS - CHANGE IF DESIRED
+ var options = {
+ name: "" // FUTURE REFERENCE - not used right now
+ , scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
+ , defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
+ applyDefaultStyles: false // apply basic styles directly to resizers & buttons? If not, then stylesheet must handle it
+ , closable: true // pane can open & close
+ , resizable: true // when open, pane can be resized
+ , slidable: true // when closed, pane can 'slide' open over other panes - closes on mouse-out
+ //, paneSelector: [ ] // MUST be pane-specific!
+ , contentSelector: defaults.contentSelector // INNER div/element to auto-size so only it scrolls, not the entire pane!
+ , contentIgnoreSelector: defaults.contentIgnoreSelector // elem(s) to 'ignore' when measuring 'content'
+ , paneClass: defaults.paneClass // border-Pane - default: 'ui-layout-pane'
+ , resizerClass: defaults.resizerClass // Resizer Bar - default: 'ui-layout-resizer'
+ , togglerClass: defaults.togglerClass // Toggler Button - default: 'ui-layout-toggler'
+ , buttonClass: defaults.buttonClass // CUSTOM Buttons - default: 'ui-layout-button-toggle/-open/-close/-pin'
+ , resizerDragOpacity: 1 // option for ui.draggable
+ //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
+ , maskIframesOnResize: true // true = all iframes OR = iframe-selector(s) - adds masking-div during resizing/dragging
+ //, size: 100 // inital size of pane - defaults are set 'per pane'
+ , minSize: 0 // when manually resizing a pane
+ , maxSize: 0 // ditto, 0 = no limit
+ , spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
+ , spacing_closed: 6 // ditto - when pane is 'closed'
+ , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south edges - HEIGHT on east/west edges
+ , togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
+ , togglerAlign_open: "center" // top/left, bottom/right, center, OR...
+ , togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
+ , togglerTip_open: "Close" // Toggler tool-tip (title)
+ , togglerTip_closed: "Open" // ditto
+ , resizerTip: "Resize" // Resizer tool-tip (title)
+ , sliderTip: "Slide Open" // resizer-bar triggers 'sliding' when pane is closed
+ , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
+ , slideTrigger_open: "click" // click, dblclick, mouseover
+ , slideTrigger_close: "mouseout" // click, mouseout
+ , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
+ , togglerContent_open: "" // text or HTML to put INSIDE the toggler
+ , togglerContent_closed: "" // ditto
+ , showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
+ , enableCursorHotkey: true // enabled 'cursor' hotkeys
+ //, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
+ , customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
+ // NOTE: fxSss_open & fxSss_close options (eg: fxName_open) are auto-generated if not passed
+ , fxName: "slide" // ('none' or blank), slide, drop, scale
+ , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
+ , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
+ , initClosed: false // true = init pane as 'closed'
+ , initHidden: false // true = init pane as 'hidden' - no resizer or spacing
+
+ /* callback options do not have to be set - listed here for reference only
+ , onshow_start: "" // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
+ , onshow_end: "" // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
+ , onhide_start: "" // CALLBACK when pane STARTS to Close - BEFORE onclose_start
+ , onhide_end: "" // CALLBACK when pane ENDS being Closed - AFTER onclose_end
+ , onopen_start: "" // CALLBACK when pane STARTS to Open
+ , onopen_end: "" // CALLBACK when pane ENDS being Opened
+ , onclose_start: "" // CALLBACK when pane STARTS to Close
+ , onclose_end: "" // CALLBACK when pane ENDS being Closed
+ , onresize_start: "" // CALLBACK when pane STARTS to be ***MANUALLY*** Resized
+ , onresize_end: "" // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
+ */
+ }
+ , north: {
+ paneSelector: "."+prefix+"north" // default = .ui-layout-north
+ , size: "auto"
+ , resizerCursor: "n-resize"
+ }
+ , south: {
+ paneSelector: "."+prefix+"south" // default = .ui-layout-south
+ , size: "auto"
+ , resizerCursor: "s-resize"
+ }
+ , east: {
+ paneSelector: "."+prefix+"east" // default = .ui-layout-east
+ , size: 200
+ , resizerCursor: "e-resize"
+ }
+ , west: {
+ paneSelector: "."+prefix+"west" // default = .ui-layout-west
+ , size: 200
+ , resizerCursor: "w-resize"
+ }
+ , center: {
+ paneSelector: "."+prefix+"center" // default = .ui-layout-center
+ }
+
+ };
+
+
+ var effects = { // LIST *PREDEFINED EFFECTS* HERE, even if effect has no settings
+ slide: {
+ all: { duration: "fast" } // eg: duration: 1000, easing: "easeOutBounce"
+ , north: { direction: "up" }
+ , south: { direction: "down" }
+ , east: { direction: "right"}
+ , west: { direction: "left" }
+ }
+ , drop: {
+ all: { duration: "slow" } // eg: duration: 1000, easing: "easeOutQuint"
+ , north: { direction: "up" }
+ , south: { direction: "down" }
+ , east: { direction: "right"}
+ , west: { direction: "left" }
+ }
+ , scale: {
+ all: { duration: "fast" }
+ }
+ };
+
+
+ // STATIC, INTERNAL CONFIG - DO NOT CHANGE THIS!
+ var config = {
+ allPanes: "north,south,east,west,center"
+ , borderPanes: "north,south,east,west"
+ , zIndex: { // set z-index values here
+ resizer_normal: 1 // normal z-index for resizer-bars
+ , pane_normal: 2 // normal z-index for panes
+ , mask: 4 // overlay div used to mask pane(s) during resizing
+ , sliding: 100 // applied to both the pane and its resizer when a pane is 'slid open'
+ , resizing: 10000 // applied to the CLONED resizer-bar when being 'dragged'
+ , animation: 10000 // applied to the pane when being animated - not applied to the resizer
+ }
+ , resizers: {
+ cssReq: {
+ position: "absolute"
+ , padding: 0
+ , margin: 0
+ , fontSize: "1px"
+ , textAlign: "left" // to counter-act "center" alignment!
+ , overflow: "hidden" // keep toggler button from overflowing
+ , zIndex: 1
+ }
+ , cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
+ background: "#DDD"
+ , border: "none"
+ }
+ }
+ , togglers: {
+ cssReq: {
+ position: "absolute"
+ , display: "block"
+ , padding: 0
+ , margin: 0
+ , overflow: "hidden"
+ , textAlign: "center"
+ , fontSize: "1px"
+ , cursor: "pointer"
+ , zIndex: 1
+ }
+ , cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
+ background: "#AAA"
+ }
+ }
+ , content: {
+ cssReq: {
+ overflow: "auto"
+ }
+ , cssDef: {}
+ }
+ , defaults: { // defaults for ALL panes - overridden by 'per-pane settings' below
+ cssReq: {
+ position: "absolute"
+ , margin: 0
+ , zIndex: 2
+ }
+ , cssDef: {
+ padding: "10px"
+ , background: "#FFF"
+ , border: "1px solid #BBB"
+ , overflow: "auto"
+ }
+ }
+ , north: {
+ edge: "top"
+ , sizeType: "height"
+ , dir: "horz"
+ , cssReq: {
+ top: 0
+ , bottom: "auto"
+ , left: 0
+ , right: 0
+ , width: "auto"
+ // height: DYNAMIC
+ }
+ }
+ , south: {
+ edge: "bottom"
+ , sizeType: "height"
+ , dir: "horz"
+ , cssReq: {
+ top: "auto"
+ , bottom: 0
+ , left: 0
+ , right: 0
+ , width: "auto"
+ // height: DYNAMIC
+ }
+ }
+ , east: {
+ edge: "right"
+ , sizeType: "width"
+ , dir: "vert"
+ , cssReq: {
+ left: "auto"
+ , right: 0
+ , top: "auto" // DYNAMIC
+ , bottom: "auto" // DYNAMIC
+ , height: "auto"
+ // width: DYNAMIC
+ }
+ }
+ , west: {
+ edge: "left"
+ , sizeType: "width"
+ , dir: "vert"
+ , cssReq: {
+ left: 0
+ , right: "auto"
+ , top: "auto" // DYNAMIC
+ , bottom: "auto" // DYNAMIC
+ , height: "auto"
+ // width: DYNAMIC
+ }
+ }
+ , center: {
+ dir: "center"
+ , cssReq: {
+ left: "auto" // DYNAMIC
+ , right: "auto" // DYNAMIC
+ , top: "auto" // DYNAMIC
+ , bottom: "auto" // DYNAMIC
+ , height: "auto"
+ , width: "auto"
+ }
+ }
+ };
+
+
+ // DYNAMIC DATA
+ var state = {
+ // generate random 'ID#' to identify layout - used to create global namespace for timers
+ id: Math.floor(Math.random() * 10000)
+ , container: {}
+ , north: {}
+ , south: {}
+ , east: {}
+ , west: {}
+ , center: {}
+ };
+
+
+ var
+ altEdge = {
+ top: "bottom"
+ , bottom: "top"
+ , left: "right"
+ , right: "left"
+ }
+ , altSide = {
+ north: "south"
+ , south: "north"
+ , east: "west"
+ , west: "east"
+ }
+ ;
+
+
+/*
+ * ###########################
+ * INTERNAL HELPER FUNCTIONS
+ * ###########################
+ */
+
+ /**
+ * isStr
+ *
+ * Returns true if passed param is EITHER a simple string OR a 'string object' - otherwise returns false
+ */
+ var isStr = function (o) {
+ if (typeof o == "string")
+ return true;
+ else if (typeof o == "object") {
+ try {
+ var match = o.constructor.toString().match(/string/i);
+ return (match !== null);
+ } catch (e) {}
+ }
+ return false;
+ };
+
+ /**
+ * str
+ *
+ * Returns a simple string if the passed param is EITHER a simple string OR a 'string object',
+ * else returns the original object
+ */
+ var str = function (o) {
+ if (typeof o == "string" || isStr(o)) return $.trim(o); // trim converts 'String object' to a simple string
+ else return o;
+ };
+
+ /**
+ * min / max
+ *
+ * Alias for Math.min/.max to simplify coding
+ */
+ var min = function (x,y) { return Math.min(x,y); };
+ var max = function (x,y) { return Math.max(x,y); };
+
+ /**
+ * transformData
+ *
+ * Processes the options passed in and transforms them into the format used by layout()
+ * Missing keys are added, and converts the data if passed in 'flat-format' (no sub-keys)
+ * In flat-format, pane-specific-settings are prefixed like: north__optName (2-underscores)
+ * To update effects, options MUST use nested-keys format, with an effects key
+ *
+ * @callers initOptions()
+ * @params JSON d Data/options passed by user - may be a single level or nested levels
+ * @returns JSON Creates a data struture that perfectly matches 'options', ready to be imported
+ */
+ var transformData = function (d) {
+ var json = { defaults:{fxSettings:{}}, north:{fxSettings:{}}, south:{fxSettings:{}}, east:{fxSettings:{}}, west:{fxSettings:{}}, center:{fxSettings:{}} };
+ d = d || {};
+ if (d.effects || d.defaults || d.north || d.south || d.west || d.east || d.center)
+ json = $.extend( json, d ); // already in json format - add to base keys
+ else
+ // convert 'flat' to 'nest-keys' format - also handles 'empty' user-options
+ $.each( d, function (key,val) {
+ a = key.split("__");
+ json[ a[1] ? a[0] : "defaults" ][ a[1] ? a[1] : a[0] ] = val;
+ });
+ return json;
+ };
+
+ /**
+ * setFlowCallback
+ *
+ * Set an INTERNAL callback to avoid simultaneous animation
+ * Runs only if needed and only if all callbacks are not 'already set'!
+ *
+ * @param String action Either 'open' or 'close'
+ * @pane String pane A valid border-pane name, eg 'west'
+ * @pane Boolean param Extra param for callback (optional)
+ */
+ var setFlowCallback = function (action, pane, param) {
+ var
+ cb = action +","+ pane +","+ (param ? 1 : 0)
+ , cP, cbPane
+ ;
+ $.each(c.borderPanes.split(","), function (i,p) {
+ if (c[p].isMoving) {
+ bindCallback(p); // TRY to bind a callback
+ return false; // BREAK
+ }
+ });
+
+ function bindCallback (p, test) {
+ cP = c[p];
+ if (!cP.doCallback) {
+ cP.doCallback = true;
+ cP.callback = cb;
+ }
+ else { // try to 'chain' this callback
+ cpPane = cP.callback.split(",")[1]; // 2nd param is 'pane'
+ if (cpPane != p && cpPane != pane) // callback target NOT 'itself' and NOT 'this pane'
+ bindCallback (cpPane, true); // RECURSE
+ }
+ }
+ };
+
+ /**
+ * execFlowCallback
+ *
+ * RUN the INTERNAL callback for this pane - if one exists
+ *
+ * @param String action Either 'open' or 'close'
+ * @pane String pane A valid border-pane name, eg 'west'
+ * @pane Boolean param Extra param for callback (optional)
+ */
+ var execFlowCallback = function (pane) {
+ var cP = c[pane];
+
+ // RESET flow-control flaGs
+ c.isLayoutBusy = false;
+ delete cP.isMoving;
+ if (!cP.doCallback || !cP.callback) return;
+
+ cP.doCallback = false; // RESET logic flag
+
+ // EXECUTE the callback
+ var
+ cb = cP.callback.split(",")
+ , param = (cb[2] > 0 ? true : false)
+ ;
+ if (cb[0] == "open")
+ open( cb[1], param );
+ else if (cb[0] == "close")
+ close( cb[1], param );
+
+ if (!cP.doCallback) cP.callback = null; // RESET - unless callback above enabled it again!
+ };
+
+ /**
+ * execUserCallback
+ *
+ * Executes a Callback function after a trigger event, like resize, open or close
+ *
+ * @param String pane This is passed only so we can pass the 'pane object' to the callback
+ * @param String v_fn Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
+ */
+ var execUserCallback = function (pane, v_fn) {
+ if (!v_fn) return;
+ var fn;
+ try {
+ if (typeof v_fn == "function")
+ fn = v_fn;
+ else if (typeof v_fn != "string")
+ return;
+ else if (v_fn.indexOf(",") > 0) {
+ // function name cannot contain a comma, so must be a function name AND a 'name' parameter
+ var
+ args = v_fn.split(",")
+ , fn = eval(args[0])
+ ;
+ if (typeof fn=="function" && args.length > 1)
+ return fn(args[1]); // pass the argument parsed from 'list'
+ }
+ else // just the name of an external function?
+ fn = eval(v_fn);
+
+ if (typeof fn=="function")
+ // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
+ return fn( pane, $Ps[pane], $.extend({},state[pane]), $.extend({},options[pane]), options.name );
+ }
+ catch (ex) {}
+ };
+
+ /**
+ * cssNum
+ *
+ * Returns the 'current CSS value' for an element - returns 0 if property does not exist
+ *
+ * @callers Called by many methods
+ * @param jQuery $Elem Must pass a jQuery object - first element is processed
+ * @param String property The name of the CSS property, eg: top, width, etc.
+ * @returns Variant Usually is used to get an integer value for position (top, left) or size (height, width)
+ */
+ var cssNum = function ($E, prop) {
+ var
+ val = 0
+ , hidden = false
+ , visibility = ""
+ ;
+ if (!$.browser.msie) { // IE CAN read dimensions of 'hidden' elements - FF CANNOT
+ if ($.curCSS($E[0], "display", true) == "none") {
+ hidden = true;
+ visibility = $.curCSS($E[0], "visibility", true); // SAVE current setting
+ $E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so we can measure it
+ }
+ }
+
+ val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
+
+ if (hidden) { // WAS hidden, so put back the way it was
+ $E.css({ display: "none" });
+ if (visibility && visibility != "hidden")
+ $E.css({ visibility: visibility }); // reset 'visibility'
+ }
+
+ return val;
+ };
+
+ /**
+ * cssW / cssH / cssSize
+ *
+ * Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
+ *
+ * @callers initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
+ * @param Variant elem Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
+ * @param Integer outerWidth/outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
+ * @returns Integer Returns the innerHeight of the elem by subtracting padding and borders
+ *
+ * @TODO May need to add additional logic to handle more browser/doctype variations?
+ */
+ var cssW = function (e, outerWidth) {
+ var $E;
+ if (isStr(e)) {
+ e = str(e);
+ $E = $Ps[e];
+ }
+ else
+ $E = $(e);
+
+ // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
+ if (outerWidth <= 0)
+ return 0;
+ else if (!(outerWidth>0))
+ outerWidth = isStr(e) ? getPaneSize(e) : $E.outerWidth();
+
+ if (!$.boxModel)
+ return outerWidth;
+
+ else // strip border and padding size from outerWidth to get CSS Width
+ return outerWidth
+ - cssNum($E, "paddingLeft")
+ - cssNum($E, "paddingRight")
+ - ($.curCSS($E[0], "borderLeftStyle", true) == "none" ? 0 : cssNum($E, "borderLeftWidth"))
+ - ($.curCSS($E[0], "borderRightStyle", true) == "none" ? 0 : cssNum($E, "borderRightWidth"))
+ ;
+ };
+ var cssH = function (e, outerHeight) {
+ var $E;
+ if (isStr(e)) {
+ e = str(e);
+ $E = $Ps[e];
+ }
+ else
+ $E = $(e);
+
+ // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
+ if (outerHeight <= 0)
+ return 0;
+ else if (!(outerHeight>0))
+ outerHeight = (isStr(e)) ? getPaneSize(e) : $E.outerHeight();
+
+ if (!$.boxModel)
+ return outerHeight;
+
+ else // strip border and padding size from outerHeight to get CSS Height
+ return outerHeight
+ - cssNum($E, "paddingTop")
+ - cssNum($E, "paddingBottom")
+ - ($.curCSS($E[0], "borderTopStyle", true) == "none" ? 0 : cssNum($E, "borderTopWidth"))
+ - ($.curCSS($E[0], "borderBottomStyle", true) == "none" ? 0 : cssNum($E, "borderBottomWidth"))
+ ;
+ };
+ var cssSize = function (pane, outerSize) {
+ if (c[pane].dir=="horz") // pane = north or south
+ return cssH(pane, outerSize);
+ else // pane = east or west
+ return cssW(pane, outerSize);
+ };
+
+ /**
+ * getPaneSize
+ *
+ * Calculates the current 'size' (width or height) of a border-pane - optionally with 'pane spacing' added
+ *
+ * @returns Integer Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
+ */
+ var getPaneSize = function (pane, inclSpace) {
+ var
+ $P = $Ps[pane]
+ , o = options[pane]
+ , s = state[pane]
+ , oSp = (inclSpace ? o.spacing_open : 0)
+ , cSp = (inclSpace ? o.spacing_closed : 0)
+ ;
+ if (!$P || s.isHidden)
+ return 0;
+ else if (s.isClosed || (s.isSliding && inclSpace))
+ return cSp;
+ else if (c[pane].dir == "horz")
+ return $P.outerHeight() + oSp;
+ else // dir == "vert"
+ return $P.outerWidth() + oSp;
+ };
+
+ var setPaneMinMaxSizes = function (pane) {
+ var
+ d = cDims
+ , edge = c[pane].edge
+ , dir = c[pane].dir
+ , o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $altPane = $Ps[ altSide[pane] ]
+ , paneSpacing = o.spacing_open
+ , altPaneSpacing = options[ altSide[pane] ].spacing_open
+ , altPaneSize = (!$altPane ? 0 : (dir=="horz" ? $altPane.outerHeight() : $altPane.outerWidth()))
+ , containerSize = (dir=="horz" ? d.innerHeight : d.innerWidth)
+ // limitSize prevents this pane from 'overlapping' opposite pane - even if opposite pane is currently closed
+ , limitSize = containerSize - paneSpacing - altPaneSize - altPaneSpacing
+ , minSize = s.minSize || 0
+ , maxSize = Math.min(s.maxSize || 9999, limitSize)
+ , minPos, maxPos // used to set resizing limits
+ ;
+ switch (pane) {
+ case "north": minPos = d.offsetTop + minSize;
+ maxPos = d.offsetTop + maxSize;
+ break;
+ case "west": minPos = d.offsetLeft + minSize;
+ maxPos = d.offsetLeft + maxSize;
+ break;
+ case "south": minPos = d.offsetTop + d.innerHeight - maxSize;
+ maxPos = d.offsetTop + d.innerHeight - minSize;
+ break;
+ case "east": minPos = d.offsetLeft + d.innerWidth - maxSize;
+ maxPos = d.offsetLeft + d.innerWidth - minSize;
+ break;
+ }
+ // save data to pane-state
+ $.extend(s, { minSize: minSize, maxSize: maxSize, minPosition: minPos, maxPosition: maxPos });
+ };
+
+ /**
+ * getPaneDims
+ *
+ * Returns data for setting the size/position of center pane. Date is also used to set Height for east/west panes
+ *
+ * @returns JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
+ */
+ var getPaneDims = function () {
+ var d = {
+ top: getPaneSize("north", true) // true = include 'spacing' value for p
+ , bottom: getPaneSize("south", true)
+ , left: getPaneSize("west", true)
+ , right: getPaneSize("east", true)
+ , width: 0
+ , height: 0
+ };
+
+ with (d) {
+ width = cDims.innerWidth - left - right;
+ height = cDims.innerHeight - bottom - top;
+ // now add the 'container border/padding' to get final positions - relative to the container
+ top += cDims.top;
+ bottom += cDims.bottom;
+ left += cDims.left;
+ right += cDims.right;
+ }
+
+ return d;
+ };
+
+
+ /**
+ * getElemDims
+ *
+ * Returns data for setting size of an element (container or a pane).
+ *
+ * @callers create(), onWindowResize() for container, plus others for pane
+ * @returns JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
+ */
+ var getElemDims = function ($E) {
+ var
+ d = {} // dimensions hash
+ , e, b, p // edge, border, padding
+ ;
+
+ $.each("Left,Right,Top,Bottom".split(","), function () {
+ e = str(this);
+ b = d["border" +e] = cssNum($E, "border"+e+"Width");
+ p = d["padding"+e] = cssNum($E, "padding"+e);
+ d["offset" +e] = b + p; // total offset of content from outer edge
+ // if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
+ if ($E == $Container)
+ d[e.toLowerCase()] = ($.boxModel ? p : 0);
+ });
+
+ d.innerWidth = d.outerWidth = $E.outerWidth();
+ d.innerHeight = d.outerHeight = $E.outerHeight();
+ if ($.boxModel) {
+ d.innerWidth -= (d.offsetLeft + d.offsetRight);
+ d.innerHeight -= (d.offsetTop + d.offsetBottom);
+ }
+
+ return d;
+ };
+
+
+ var setTimer = function (pane, action, fn, ms) {
+ var
+ Layout = window.layout = window.layout || {}
+ , Timers = Layout.timers = Layout.timers || {}
+ , name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
+ ;
+ if (Timers[name]) return; // timer already set!
+ else Timers[name] = setTimeout(fn, ms);
+ };
+
+ var clearTimer = function (pane, action) {
+ var
+ Layout = window.layout = window.layout || {}
+ , Timers = Layout.timers = Layout.timers || {}
+ , name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
+ ;
+ if (Timers[name]) {
+ clearTimeout( Timers[name] );
+ delete Timers[name];
+ return true;
+ }
+ else
+ return false;
+ };
+
+
+/*
+ * ###########################
+ * INITIALIZATION METHODS
+ * ###########################
+ */
+
+ /**
+ * create
+ *
+ * Initialize the layout - called automatically whenever an instance of layout is created
+ *
+ * @callers NEVER explicity called
+ * @returns An object pointer to the instance created
+ */
+ var create = function () {
+ // initialize config/options
+ initOptions();
+
+ // initialize all objects
+ initContainer(); // set CSS as needed and init state.container dimensions
+ initPanes(); // size & position all panes
+ initHandles(); // create and position all resize bars & togglers buttons
+ initResizable(); // activate resizing on all panes where resizable=true
+ sizeContent("all"); // AFTER panes & handles have been initialized, size 'content' divs
+
+ if (options.scrollToBookmarkOnLoad)
+ with (self.location) if (hash) replace( hash ); // scrollTo Bookmark
+
+ // bind hotkey function - keyDown - if required
+ initHotkeys();
+
+ // bind resizeAll() for 'this layout instance' to window.resize event
+ $(window).resize(function () {
+ var timerID = "timerLayout_"+state.id;
+ if (window[timerID]) clearTimeout(window[timerID]);
+ window[timerID] = null;
+ if (true || $.browser.msie) // use a delay for IE because the resize event fires repeatly
+ window[timerID] = setTimeout(resizeAll, 100);
+ else // most other browsers have a built-in delay before firing the resize event
+ resizeAll(); // resize all layout elements NOW!
+ });
+ };
+
+ /**
+ * initContainer
+ *
+ * Validate and initialize container CSS and events
+ *
+ * @callers create()
+ */
+ var initContainer = function () {
+ try { // format html/body if this is a full page layout
+ if ($Container[0].tagName == "BODY") {
+ $("html").css({
+ height: "100%"
+ , overflow: "hidden"
+ });
+ $("body").css({
+ position: "relative"
+ , height: "100%"
+ , overflow: "hidden"
+ , margin: 0
+ , padding: 0 // TODO: test whether body-padding could be handled?
+ , border: "none" // a body-border creates problems because it cannot be measured!
+ });
+ }
+ else { // set required CSS - overflow and position
+ var
+ CSS = { overflow: "hidden" } // make sure container will not 'scroll'
+ , p = $Container.css("position")
+ , h = $Container.css("height")
+ ;
+ // if this is a NESTED layout, then outer-pane ALREADY has position and height
+ if (!$Container.hasClass("ui-layout-pane")) {
+ if (!p || "fixed,absolute,relative".indexOf(p) < 0)
+ CSS.position = "relative"; // container MUST have a 'position'
+ if (!h || h=="auto")
+ CSS.height = "100%"; // container MUST have a 'height'
+ }
+ $Container.css( CSS );
+ }
+ } catch (ex) {}
+
+ // get layout-container dimensions (updated when necessary)
+ cDims = state.container = getElemDims( $Container ); // update data-pointer too
+ };
+
+ /**
+ * initHotkeys
+ *
+ * Bind layout hotkeys - if options enabled
+ *
+ * @callers create()
+ */
+ var initHotkeys = function () {
+ // bind keyDown to capture hotkeys, if option enabled for ANY pane
+ $.each(c.borderPanes.split(","), function (i,pane) {
+ var o = options[pane];
+ if (o.enableCursorHotkey || o.customHotkey) {
+ $(document).keydown( keyDown ); // only need to bind this ONCE
+ return false; // BREAK - binding was done
+ }
+ });
+ };
+
+ /**
+ * initOptions
+ *
+ * Build final CONFIG and OPTIONS data
+ *
+ * @callers create()
+ */
+ var initOptions = function () {
+ // simplify logic by making sure passed 'opts' var has basic keys
+ opts = transformData( opts );
+
+ // update default effects, if case user passed key
+ if (opts.effects) {
+ $.extend( effects, opts.effects );
+ delete opts.effects;
+ }
+
+ // see if any 'global options' were specified
+ $.each("name,scrollToBookmarkOnLoad".split(","), function (idx,key) {
+ if (opts[key] !== undefined)
+ options[key] = opts[key];
+ else if (opts.defaults[key] !== undefined) {
+ options[key] = opts.defaults[key];
+ delete opts.defaults[key];
+ }
+ });
+
+ // remove any 'defaults' that MUST be set 'per-pane'
+ $.each("paneSelector,resizerCursor,customHotkey".split(","),
+ function (idx,key) { delete opts.defaults[key]; } // is OK if key does not exist
+ );
+
+ // now update options.defaults
+ $.extend( options.defaults, opts.defaults );
+ // make sure required sub-keys exist
+ //if (typeof options.defaults.fxSettings != "object") options.defaults.fxSettings = {};
+
+ // merge all config & options for the 'center' pane
+ c.center = $.extend( true, {}, c.defaults, c.center );
+ $.extend( options.center, opts.center );
+ // Most 'default options' do not apply to 'center', so add only those that DO
+ var o_Center = $.extend( true, {}, options.defaults, opts.defaults, options.center ); // TEMP data
+ $.each("paneClass,contentSelector,contentIgnoreSelector,applyDefaultStyles,showOverflowOnHover".split(","),
+ function (idx,key) { options.center[key] = o_Center[key]; }
+ );
+
+ var defs = options.defaults;
+
+ // create a COMPLETE set of options for EACH border-pane
+ $.each(c.borderPanes.split(","), function(i,pane) {
+ // apply 'pane-defaults' to CONFIG.PANE
+ c[pane] = $.extend( true, {}, c.defaults, c[pane] );
+ // apply 'pane-defaults' + user-options to OPTIONS.PANE
+ o = options[pane] = $.extend( true, {}, options.defaults, options[pane], opts.defaults, opts[pane] );
+
+ // make sure we have base-classes
+ if (!o.paneClass) o.paneClass = defaults.paneClass;
+ if (!o.resizerClass) o.resizerClass = defaults.resizerClass;
+ if (!o.togglerClass) o.togglerClass = defaults.togglerClass;
+
+ // create FINAL fx options for each pane, ie: options.PANE.fxName/fxSpeed/fxSettings[_open|_close]
+ $.each(["_open","_close",""], function (i,n) {
+ var
+ sName = "fxName"+n
+ , sSpeed = "fxSpeed"+n
+ , sSettings = "fxSettings"+n
+ ;
+ // recalculate fxName according to specificity rules
+ o[sName] =
+ opts[pane][sName] // opts.west.fxName_open
+ || opts[pane].fxName // opts.west.fxName
+ || opts.defaults[sName] // opts.defaults.fxName_open
+ || opts.defaults.fxName // opts.defaults.fxName
+ || o[sName] // options.west.fxName_open
+ || o.fxName // options.west.fxName
+ || defs[sName] // options.defaults.fxName_open
+ || defs.fxName // options.defaults.fxName
+ || "none"
+ ;
+ // validate fxName to be sure is a valid effect
+ var fxName = o[sName];
+ if (fxName == "none" || !$.effects || !$.effects[fxName] || (!effects[fxName] && !o[sSettings] && !o.fxSettings))
+ fxName = o[sName] = "none"; // effect not loaded, OR undefined FX AND fxSettings not passed
+ // set vars for effects subkeys to simplify logic
+ var
+ fx = effects[fxName] || {} // effects.slide
+ , fx_all = fx.all || {} // effects.slide.all
+ , fx_pane = fx[pane] || {} // effects.slide.west
+ ;
+ // RECREATE the fxSettings[_open|_close] keys using specificity rules
+ o[sSettings] = $.extend(
+ {}
+ , fx_all // effects.slide.all
+ , fx_pane // effects.slide.west
+ , defs.fxSettings || {} // options.defaults.fxSettings
+ , defs[sSettings] || {} // options.defaults.fxSettings_open
+ , o.fxSettings // options.west.fxSettings
+ , o[sSettings] // options.west.fxSettings_open
+ , opts.defaults.fxSettings // opts.defaults.fxSettings
+ , opts.defaults[sSettings] || {} // opts.defaults.fxSettings_open
+ , opts[pane].fxSettings // opts.west.fxSettings
+ , opts[pane][sSettings] || {} // opts.west.fxSettings_open
+ );
+ // recalculate fxSpeed according to specificity rules
+ o[sSpeed] =
+ opts[pane][sSpeed] // opts.west.fxSpeed_open
+ || opts[pane].fxSpeed // opts.west.fxSpeed (pane-default)
+ || opts.defaults[sSpeed] // opts.defaults.fxSpeed_open
+ || opts.defaults.fxSpeed // opts.defaults.fxSpeed
+ || o[sSpeed] // options.west.fxSpeed_open
+ || o[sSettings].duration // options.west.fxSettings_open.duration
+ || o.fxSpeed // options.west.fxSpeed
+ || o.fxSettings.duration // options.west.fxSettings.duration
+ || defs.fxSpeed // options.defaults.fxSpeed
+ || defs.fxSettings.duration// options.defaults.fxSettings.duration
+ || fx_pane.duration // effects.slide.west.duration
+ || fx_all.duration // effects.slide.all.duration
+ || "normal" // DEFAULT
+ ;
+ // DEBUG: if (pane=="east") debugData( $.extend({}, {speed: o[sSpeed], fxSettings_duration: o[sSettings].duration}, o[sSettings]), pane+"."+sName+" = "+fxName );
+ });
+ });
+ };
+
+ /**
+ * initPanes
+ *
+ * Initialize module objects, styling, size and position for all panes
+ *
+ * @callers create()
+ */
+ var initPanes = function () {
+ // NOTE: do north & south FIRST so we can measure their height - do center LAST
+ $.each(c.allPanes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ , fx = s.fx
+ , dir = c[pane].dir
+ // if o.size is not > 0, then we will use MEASURE the pane and use that as it's 'size'
+ , size = o.size=="auto" || isNaN(o.size) ? 0 : o.size
+ , minSize = o.minSize || 1
+ , maxSize = o.maxSize || 9999
+ , spacing = o.spacing_open || 0
+ , sel = o.paneSelector
+ , isIE6 = ($.browser.msie && $.browser.version < 7)
+ , CSS = {}
+ , $P, $C
+ ;
+ $Cs[pane] = false; // init
+
+ if (sel.substr(0,1)==="#") // ID selector
+ // NOTE: elements selected 'by ID' DO NOT have to be 'children'
+ $P = $Ps[pane] = $Container.find(sel+":first");
+ else { // class or other selector
+ $P = $Ps[pane] = $Container.children(sel+":first");
+ // look for the pane nested inside a 'form' element
+ if (!$P.length) $P = $Ps[pane] = $Container.children("form:first").children(sel+":first");
+ }
+
+ if (!$P.length) {
+ $Ps[pane] = false; // logic
+ return true; // SKIP to next
+ }
+
+ // add basic classes & attributes
+ $P
+ .attr("pane", pane) // add pane-identifier
+ .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
+ ;
+
+ // init pane-logic vars, etc.
+ if (pane != "center") {
+ s.isClosed = false; // true = pane is closed
+ s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
+ s.isResizing= false; // true = pane is in process of being resized
+ s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
+ s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
+ // create special keys for internal use
+ c[pane].pins = []; // used to track and sync 'pin-buttons' for border-panes
+ }
+
+ CSS = $.extend({ visibility: "visible", display: "block" }, c.defaults.cssReq, c[pane].cssReq );
+ if (o.applyDefaultStyles) $.extend( CSS, c.defaults.cssDef, c[pane].cssDef ); // cosmetic defaults
+ $P.css(CSS); // add base-css BEFORE 'measuring' to calc size & position
+ CSS = {}; // reset var
+
+ // set css-position to account for container borders & padding
+ switch (pane) {
+ case "north": CSS.top = cDims.top;
+ CSS.left = cDims.left;
+ CSS.right = cDims.right;
+ break;
+ case "south": CSS.bottom = cDims.bottom;
+ CSS.left = cDims.left;
+ CSS.right = cDims.right;
+ break;
+ case "west": CSS.left = cDims.left; // top, bottom & height set by sizeMidPanes()
+ break;
+ case "east": CSS.right = cDims.right; // ditto
+ break;
+ case "center": // top, left, width & height set by sizeMidPanes()
+ }
+
+ if (dir == "horz") { // north or south pane
+ if (size === 0 || size == "auto") {
+ $P.css({ height: "auto" });
+ size = $P.outerHeight();
+ }
+ size = max(size, minSize);
+ size = min(size, maxSize);
+ size = min(size, cDims.innerHeight - spacing);
+ CSS.height = max(1, cssH(pane, size));
+ s.size = size; // update state
+ // make sure minSize is sufficient to avoid errors
+ s.maxSize = maxSize; // init value
+ s.minSize = max(minSize, size - CSS.height + 1); // = pane.outerHeight when css.height = 1px
+ // handle IE6
+ //if (isIE6) CSS.width = cssW($P, cDims.innerWidth);
+ $P.css(CSS); // apply size & position
+ }
+ else if (dir == "vert") { // east or west pane
+ if (size === 0 || size == "auto") {
+ $P.css({ width: "auto", float: "left" }); // float = FORCE pane to auto-size
+ size = $P.outerWidth();
+ $P.css({ float: "none" }); // RESET
+ }
+ size = max(size, minSize);
+ size = min(size, maxSize);
+ size = min(size, cDims.innerWidth - spacing);
+ CSS.width = max(1, cssW(pane, size));
+ s.size = size; // update state
+ s.maxSize = maxSize; // init value
+ // make sure minSize is sufficient to avoid errors
+ s.minSize = max(minSize, size - CSS.width + 1); // = pane.outerWidth when css.width = 1px
+ $P.css(CSS); // apply size - top, bottom & height set by sizeMidPanes
+ sizeMidPanes(pane, null, true); // true = onInit
+ }
+ else if (pane == "center") {
+ $P.css(CSS); // top, left, width & height set by sizeMidPanes...
+ sizeMidPanes("center", null, true); // true = onInit
+ }
+
+ // close or hide the pane if specified in settings
+ if (o.initClosed && o.closable) {
+ $P.hide().addClass("closed");
+ s.isClosed = true;
+ }
+ else if (o.initHidden || o.initClosed) {
+ hide(pane, true); // will be completely invisible - no resizer or spacing
+ s.isHidden = true;
+ }
+ else
+ $P.addClass("open");
+
+ // check option for auto-handling of pop-ups & drop-downs
+ if (o.showOverflowOnHover)
+ $P.hover( allowOverflow, resetOverflow );
+
+ /*
+ * see if this pane has a 'content element' that we need to auto-size
+ */
+ if (o.contentSelector) {
+ $C = $Cs[pane] = $P.children(o.contentSelector+":first"); // match 1-element only
+ if (!$C.length) {
+ $Cs[pane] = false;
+ return true; // SKIP to next
+ }
+ $C.css( c.content.cssReq );
+ if (o.applyDefaultStyles) $C.css( c.content.cssDef ); // cosmetic defaults
+ // NO PANE-SCROLLING when there is a content-div
+ $P.css({ overflow: "hidden" });
+ }
+ });
+ };
+
+ /**
+ * initHandles
+ *
+ * Initialize module objects, styling, size and position for all resize bars and toggler buttons
+ *
+ * @callers create()
+ */
+ var initHandles = function () {
+ // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
+ $.each(c.borderPanes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ , rClass = o.resizerClass
+ , tClass = o.togglerClass
+ , $P = $Ps[pane]
+ ;
+ $Rs[pane] = false; // INIT
+ $Ts[pane] = false;
+
+ if (!$P || (!o.closable && !o.resizable)) return; // pane does not exist - skip
+
+ var
+ edge = c[pane].edge
+ , isOpen = $P.is(":visible")
+ , spacing = (isOpen ? o.spacing_open : o.spacing_closed)
+ , _pane = "-"+ pane // used for classNames
+ , _state = (isOpen ? "-open" : "-closed") // used for classNames
+ , $R, $T
+ ;
+ // INIT RESIZER BAR
+ $R = $Rs[pane] = $("<span></span>");
+
+ if (isOpen && o.resizable)
+ ; // this is handled by initResizable
+ else if (!isOpen && o.slidable)
+ $R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
+
+ $R
+ // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
+ .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
+ .attr("resizer", pane) // so we can read this from the resizer
+ .css(c.resizers.cssReq) // add base/required styles
+ // POSITION of resizer bar - allow for container border & padding
+ .css(edge, cDims[edge] + getPaneSize(pane))
+ // ADD CLASSNAMES - eg: class="resizer resizer-west resizer-open"
+ .addClass( rClass +" "+ rClass+_pane +" "+ rClass+_state +" "+ rClass+_pane+_state )
+ .appendTo($Container) // append DIV to container
+ ;
+ // ADD VISUAL STYLES
+ if (o.applyDefaultStyles)
+ $R.css(c.resizers.cssDef);
+
+ if (o.closable) {
+ // INIT COLLAPSER BUTTON
+ $T = $Ts[pane] = $("<div></div>");
+ $T
+ // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-toggler"
+ .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
+ .css(c.togglers.cssReq) // add base/required styles
+ .attr("title", (isOpen ? o.togglerTip_open : o.togglerTip_closed))
+ .click(function(evt){ toggle(pane); evt.stopPropagation(); })
+ .mouseover(function(evt){ evt.stopPropagation(); }) // prevent resizer event
+ // ADD CLASSNAMES - eg: class="toggler toggler-west toggler-west-open"
+ .addClass( tClass +" "+ tClass+_pane +" "+ tClass+_state +" "+ tClass+_pane+_state )
+ .appendTo($R) // append SPAN to resizer DIV
+ ;
+
+ // ADD INNER-SPANS TO TOGGLER
+ if (o.togglerContent_open) // ui-layout-open
+ $("<span>"+ o.togglerContent_open +"</span>")
+ .addClass("content content-open")
+ .css("display", s.isClosed ? "none" : "block")
+ .appendTo( $T )
+ ;
+ if (o.togglerContent_closed) // ui-layout-closed
+ $("<span>"+ o.togglerContent_closed +"</span>")
+ .addClass("content content-closed")
+ .css("display", s.isClosed ? "block" : "none")
+ .appendTo( $T )
+ ;
+
+ // ADD BASIC VISUAL STYLES
+ if (o.applyDefaultStyles)
+ $T.css(c.togglers.cssDef);
+
+ if (!isOpen) bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
+ }
+
+ });
+
+ // SET ALL HANDLE SIZES & LENGTHS
+ sizeHandles("all", true); // true = onInit
+ };
+
+ /**
+ * initResizable
+ *
+ * Add resize-bars to all panes that specify it in options
+ *
+ * @dependancies $.fn.resizable - will abort if not found
+ * @callers create()
+ */
+ var initResizable = function () {
+ var
+ draggingAvailable = (typeof $.fn.draggable == "function")
+ , minPosition, maxPosition, edge // set in start()
+ ;
+
+ $.each(c.borderPanes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ ;
+ if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
+ o.resizable = false;
+ return true; // skip to next
+ }
+
+ var
+ rClass = o.resizerClass
+ // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
+ , dragClass = rClass+"-drag" // resizer-drag
+ , dragPaneClass = rClass+"-"+pane+"-drag" // resizer-north-drag
+ // 'dragging' class is applied to the CLONED resizer-bar while it is being dragged
+ , draggingClass = rClass+"-dragging" // resizer-dragging
+ , draggingPaneClass = rClass+"-"+pane+"-dragging" // resizer-north-dragging
+ , draggingClassSet = false // logic var
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+
+ if (!s.isClosed)
+ $R
+ .attr("title", o.resizerTip)
+ .css("cursor", o.resizerCursor) // n-resize, s-resize, etc
+ ;
+
+ $R.draggable({
+ containment: $Container[0] // limit resizing to layout container
+ , axis: (c[pane].dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
+ , delay: 200
+ , distance: 1
+ // basic format for helper - style it using class: .ui-draggable-dragging
+ , helper: "clone"
+ , opacity: o.resizerDragOpacity
+ //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
+ , zIndex: c.zIndex.resizing
+
+ , start: function (e, ui) {
+ // onresize_start callback - will CANCEL hide if returns false
+ // TODO: CONFIRM that dragging can be cancelled like this???
+ if (false === execUserCallback(pane, o.onresize_start)) return false;
+
+ s.isResizing = true; // prevent pane from closing while resizing
+ clearTimer(pane, "closeSlider"); // just in case already triggered
+
+ $R.addClass( dragClass +" "+ dragPaneClass ); // add drag classes
+ draggingClassSet = false; // reset logic var - see drag()
+
+ // SET RESIZING LIMITS - used in drag()
+ var resizerWidth = (pane=="east" || pane=="south" ? o.spacing_open : 0);
+ setPaneMinMaxSizes(pane); // update pane-state
+ s.minPosition -= resizerWidth;
+ s.maxPosition -= resizerWidth;
+ edge = (c[pane].dir=="horz" ? "top" : "left");
+
+ // MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
+ $(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).each(function() {
+ $('<div class="ui-layout-mask"/>')
+ .css({
+ background: "#fff"
+ , opacity: "0.001"
+ , zIndex: 9
+ , position: "absolute"
+ , width: this.offsetWidth+"px"
+ , height: this.offsetHeight+"px"
+ })
+ .css($(this).offset()) // top & left
+ .appendTo(this.parentNode) // put div INSIDE pane to avoid zIndex issues
+ ;
+ });
+ }
+
+ , drag: function (e, ui) {
+ if (!draggingClassSet) { // can only add classes after clone has been added to the DOM
+ $(".ui-draggable-dragging")
+ .addClass( draggingClass +" "+ draggingPaneClass ) // add dragging classes
+ .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
+ ;
+ draggingClassSet = true;
+ // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
+ if (s.isSliding) $Ps[pane].css("zIndex", c.zIndex.sliding);
+ }
+ // CONTAIN RESIZER-BAR TO RESIZING LIMITS
+ if (ui.position[edge] < s.minPosition) ui.position[edge] = s.minPosition;
+ else if (ui.position[edge] > s.maxPosition) ui.position[edge] = s.maxPosition;
+ }
+
+ , stop: function (e, ui) {
+ var
+ dragPos = ui.position
+ , resizerPos
+ , newSize
+ ;
+ $R.removeClass( dragClass +" "+ dragPaneClass ); // remove drag classes
+
+ switch (pane) {
+ case "north": resizerPos = dragPos.top; break;
+ case "west": resizerPos = dragPos.left; break;
+ case "south": resizerPos = cDims.outerHeight - dragPos.top - $R.outerHeight(); break;
+ case "east": resizerPos = cDims.outerWidth - dragPos.left - $R.outerWidth(); break;
+ }
+ // remove container margin from resizer position to get the pane size
+ newSize = resizerPos - cDims[ c[pane].edge ];
+
+ sizePane(pane, newSize);
+
+ // UN-MASK PANES MASKED IN drag.start
+ $("div.ui-layout-mask").remove(); // Remove iframe masks
+
+ s.isResizing = false;
+ }
+
+ });
+ });
+ };
+
+
+
+/*
+ * ###########################
+ * ACTION METHODS
+ * ###########################
+ */
+
+ /**
+ * hide / show
+ *
+ * Completely 'hides' a pane, including its spacing - as if it does not exist
+ * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
+ *
+ * @param String pane The pane being hidden, ie: north, south, east, or west
+ */
+ var hide = function (pane, onInit) {
+ var
+ o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+ if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
+
+ // onhide_start callback - will CANCEL hide if returns false
+ if (false === execUserCallback(pane, o.onhide_start)) return;
+
+ s.isSliding = false; // just in case
+
+ // now hide the elements
+ if ($R) $R.hide(); // hide resizer-bar
+ if (onInit || s.isClosed) {
+ s.isClosed = true; // to trigger open-animation on show()
+ s.isHidden = true;
+ $P.hide(); // no animation when loading page
+ sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
+ execUserCallback(pane, o.onhide_end || o.onhide);
+ }
+ else {
+ s.isHiding = true; // used by onclose
+ close(pane, false); // adjust all panes to fit
+ //s.isHidden = true; - will be set by close - if not cancelled
+ }
+ };
+
+ var show = function (pane, openPane) {
+ var
+ o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+ if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
+
+ // onhide_start callback - will CANCEL hide if returns false
+ if (false === execUserCallback(pane, o.onshow_start)) return;
+
+ s.isSliding = false; // just in case
+ s.isShowing = true; // used by onopen/onclose
+ //s.isHidden = false; - will be set by open/close - if not cancelled
+
+ // now show the elements
+ if ($R && o.spacing_open > 0) $R.show();
+ if (openPane === false)
+ close(pane, true); // true = force
+ else
+ open(pane); // adjust all panes to fit
+ };
+
+
+ /**
+ * toggle
+ *
+ * Toggles a pane open/closed by calling either open or close
+ *
+ * @param String pane The pane being toggled, ie: north, south, east, or west
+ */
+ var toggle = function (pane) {
+ var s = state[pane];
+ if (s.isHidden)
+ show(pane); // will call 'open' after unhiding it
+ else if (s.isClosed)
+ open(pane);
+ else
+ close(pane);
+ };
+
+ /**
+ * close
+ *
+ * Close the specified pane (animation optional), and resize all other panes as needed
+ *
+ * @param String pane The pane being closed, ie: north, south, east, or west
+ */
+ var close = function (pane, force, noAnimation) {
+ var
+ $P = $Ps[pane]
+ , $R = $Rs[pane]
+ , $T = $Ts[pane]
+ , o = options[pane]
+ , s = state[pane]
+ , doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none")
+ , edge = c[pane].edge
+ , rClass = o.resizerClass
+ , tClass = o.togglerClass
+ , _pane = "-"+ pane // used for classNames
+ , _open = "-open"
+ , _sliding= "-sliding"
+ , _closed = "-closed"
+ // transfer logic vars to temp vars
+ , isShowing = s.isShowing
+ , isHiding = s.isHiding
+ ;
+ // now clear the logic vars
+ delete s.isShowing;
+ delete s.isHiding;
+
+ if (!$P || (!o.resizable && !o.closable)) return; // invalid request
+ else if (!force && s.isClosed && !isShowing) return; // already closed
+
+ if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
+ setFlowCallback("close", pane, force); // set a callback for this action, if possible
+ return; // ABORT
+ }
+
+ // onclose_start callback - will CANCEL hide if returns false
+ // SKIP if just 'showing' a hidden pane as 'closed'
+ if (!isShowing && false === execUserCallback(pane, o.onclose_start)) return;
+
+ // SET flow-control flags
+ c[pane].isMoving = true;
+ c.isLayoutBusy = true;
+
+ s.isClosed = true;
+ // update isHidden BEFORE sizing panes
+ if (isHiding) s.isHidden = true;
+ else if (isShowing) s.isHidden = false;
+
+ // sync any 'pin buttons'
+ syncPinBtns(pane, false);
+
+ // resize panes adjacent to this one
+ if (!s.isSliding) sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
+
+ // if this pane has a resizer bar, move it now
+ if ($R) {
+ $R
+ .css(edge, cDims[edge]) // move the resizer bar
+ .removeClass( rClass+_open +" "+ rClass+_pane+_open )
+ .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
+ .addClass( rClass+_closed +" "+ rClass+_pane+_closed )
+ ;
+ // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent
+ if (o.resizable)
+ $R
+ .draggable("disable")
+ .css("cursor", "default")
+ .attr("title","")
+ ;
+ // if pane has a toggler button, adjust that too
+ if ($T) {
+ $T
+ .removeClass( tClass+_open +" "+ tClass+_pane+_open )
+ .addClass( tClass+_closed +" "+ tClass+_pane+_closed )
+ .attr("title", o.togglerTip_closed) // may be blank
+ ;
+ }
+ sizeHandles(); // resize 'length' and position togglers for adjacent panes
+ }
+
+ // ANIMATE 'CLOSE' - if no animation, then was ALREADY shown above
+ if (doFX) {
+ lockPaneForFX(pane, true); // need to set left/top so animation will work
+ $P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
+ lockPaneForFX(pane, false); // undo
+ if (!s.isClosed) return; // pane was opened before animation finished!
+ close_2();
+ });
+ }
+ else {
+ $P.hide(); // just hide pane NOW
+ close_2();
+ }
+
+ // SUBROUTINE
+ function close_2 () {
+ bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
+
+ // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
+ if (!isShowing) execUserCallback(pane, o.onclose_end || o.onclose);
+ // onhide OR onshow callback
+ if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
+ if (isHiding) execUserCallback(pane, o.onhide_end || o.onhide);
+
+ // internal flow-control callback
+ execFlowCallback(pane);
+ }
+ };
+
+ /**
+ * open
+ *
+ * Open the specified pane (animation optional), and resize all other panes as needed
+ *
+ * @param String pane The pane being opened, ie: north, south, east, or west
+ */
+ var open = function (pane, slide, noAnimation) {
+ var
+ $P = $Ps[pane]
+ , $R = $Rs[pane]
+ , $T = $Ts[pane]
+ , o = options[pane]
+ , s = state[pane]
+ , doFX = !noAnimation && s.isClosed && (o.fxName_open != "none")
+ , edge = c[pane].edge
+ , rClass = o.resizerClass
+ , tClass = o.togglerClass
+ , _pane = "-"+ pane // used for classNames
+ , _open = "-open"
+ , _closed = "-closed"
+ , _sliding= "-sliding"
+ // transfer logic var to temp var
+ , isShowing = s.isShowing
+ ;
+ // now clear the logic var
+ delete s.isShowing;
+
+ if (!$P || (!o.resizable && !o.closable)) return; // invalid request
+ else if (!s.isClosed && !s.isSliding) return; // already open
+
+ // pane can ALSO be unhidden by just calling show(), so handle this scenario
+ if (s.isHidden && !isShowing) {
+ show(pane, true);
+ return;
+ }
+
+ if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
+ setFlowCallback("open", pane, slide); // set a callback for this action, if possible
+ return; // ABORT
+ }
+
+ // onopen_start callback - will CANCEL hide if returns false
+ if (false === execUserCallback(pane, o.onopen_start)) return;
+
+ // SET flow-control flags
+ c[pane].isMoving = true;
+ c.isLayoutBusy = true;
+
+ // 'PIN PANE' - stop sliding
+ if (s.isSliding && !slide) // !slide = 'open pane normally' - NOT sliding
+ bindStopSlidingEvents(pane, false); // will set isSliding=false
+
+ s.isClosed = false;
+ // update isHidden BEFORE sizing panes
+ if (isShowing) s.isHidden = false;
+
+ // Container size may have changed - shrink the pane if now 'too big'
+ setPaneMinMaxSizes(pane); // update pane-state
+ if (s.size > s.maxSize) // pane is too big! resize it before opening
+ $P.css( c[pane].sizeType, max(1, cssSize(pane, s.maxSize)) );
+
+ bindStartSlidingEvent(pane, false); // remove trigger event from resizer-bar
+
+ if (doFX) { // ANIMATE
+ lockPaneForFX(pane, true); // need to set left/top so animation will work
+ $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
+ lockPaneForFX(pane, false); // undo
+ if (s.isClosed) return; // pane was closed before animation finished!
+ open_2(); // continue
+ });
+ }
+ else {// no animation
+ $P.show(); // just show pane and...
+ open_2(); // continue
+ }
+
+ // SUBROUTINE
+ function open_2 () {
+ // NOTE: if isSliding, then other panes are NOT 'resized'
+ if (!s.isSliding) // resize all panes adjacent to this one
+ sizeMidPanes(c[pane].dir=="vert" ? "center" : "all");
+
+ // if this pane has a toggler, move it now
+ if ($R) {
+ $R
+ .css(edge, cDims[edge] + getPaneSize(pane)) // move the toggler
+ .removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
+ .addClass( rClass+_open +" "+ rClass+_pane+_open )
+ .addClass( !s.isSliding ? "" : rClass+_sliding +" "+ rClass+_pane+_sliding )
+ ;
+ if (o.resizable)
+ $R
+ .draggable("enable")
+ .css("cursor", o.resizerCursor)
+ .attr("title", o.resizerTip)
+ ;
+ else
+ $R.css("cursor", "default"); // n-resize, s-resize, etc
+ // if pane also has a toggler button, adjust that too
+ if ($T) {
+ $T
+ .removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
+ .addClass( tClass+_open +" "+ tClass+_pane+_open )
+ .attr("title", o.togglerTip_open) // may be blank
+ ;
+ }
+ sizeHandles("all"); // resize resizer & toggler sizes for all panes
+ }
+
+ // resize content every time pane opens - to be sure
+ sizeContent(pane);
+
+ // sync any 'pin buttons'
+ syncPinBtns(pane, !s.isSliding);
+
+ // onopen callback
+ execUserCallback(pane, o.onopen_end || o.onopen);
+
+ // onshow callback
+ if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
+
+ // internal flow-control callback
+ execFlowCallback(pane);
+ }
+ };
+
+
+ /**
+ * lockPaneForFX
+ *
+ * Must set left/top on East/South panes so animation will work properly
+ *
+ * @param String pane The pane to lock, 'east' or 'south' - any other is ignored!
+ * @param Boolean doLock true = set left/top, false = remove
+ */
+ var lockPaneForFX = function (pane, doLock) {
+ var $P = $Ps[pane];
+ if (doLock) {
+ $P.css({ zIndex: c.zIndex.animation }); // overlay all elements during animation
+ if (pane=="south")
+ $P.css({ top: cDims.top + cDims.innerHeight - $P.outerHeight() });
+ else if (pane=="east")
+ $P.css({ left: cDims.left + cDims.innerWidth - $P.outerWidth() });
+ }
+ else {
+ if (!state[pane].isSliding) $P.css({ zIndex: c.zIndex.pane_normal });
+ if (pane=="south")
+ $P.css({ top: "auto" });
+ else if (pane=="east")
+ $P.css({ left: "auto" });
+ }
+ };
+
+
+ /**
+ * bindStartSlidingEvent
+ *
+ * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
+ *
+ * @callers open(), close()
+ * @param String pane The pane to enable/disable, 'north', 'south', etc.
+ * @param Boolean enable Enable or Disable sliding?
+ */
+ var bindStartSlidingEvent = function (pane, enable) {
+ var
+ o = options[pane]
+ , $R = $Rs[pane]
+ , trigger = o.slideTrigger_open
+ ;
+ if (!$R || !o.slidable) return;
+ // make sure we have a valid event
+ if (trigger != "click" && trigger != "dblclick" && trigger != "mouseover") trigger = "click";
+ $R
+ // add or remove trigger event
+ [enable ? "bind" : "unbind"](trigger, slideOpen)
+ // set the appropriate cursor & title/tip
+ .css("cursor", (enable ? o.sliderCursor: "default"))
+ .attr("title", (enable ? o.sliderTip : ""))
+ ;
+ };
+
+ /**
+ * bindStopSlidingEvents
+ *
+ * Add or remove 'mouseout' events to 'slide close' when pane is 'sliding' open or closed
+ * Also increases zIndex when pane is sliding open
+ * See bindStartSlidingEvent for code to control 'slide open'
+ *
+ * @callers slideOpen(), slideClosed()
+ * @param String pane The pane to process, 'north', 'south', etc.
+ * @param Boolean isOpen Is pane open or closed?
+ */
+ var bindStopSlidingEvents = function (pane, enable) {
+ var
+ o = options[pane]
+ , s = state[pane]
+ , trigger = o.slideTrigger_close
+ , action = (enable ? "bind" : "unbind") // can't make 'unbind' work! - see disabled code below
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+
+ s.isSliding = enable; // logic
+ clearTimer(pane, "closeSlider"); // just in case
+
+ // raise z-index when sliding
+ $P.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.pane_normal) });
+ $R.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.resizer_normal) });
+
+ // make sure we have a valid event
+ if (trigger != "click" && trigger != "mouseout") trigger = "mouseout";
+
+ // when trigger is 'mouseout', must cancel timer when mouse moves between 'pane' and 'resizer'
+ if (enable) { // BIND trigger events
+ $P.bind(trigger, slideClosed );
+ $R.bind(trigger, slideClosed );
+ if (trigger = "mouseout") {
+ $P.bind("mouseover", cancelMouseOut );
+ $R.bind("mouseover", cancelMouseOut );
+ }
+ }
+ else { // UNBIND trigger events
+ // TODO: why does unbind of a 'single function' not work reliably?
+ //$P[action](trigger, slideClosed );
+ $P.unbind(trigger);
+ $R.unbind(trigger);
+ if (trigger = "mouseout") {
+ //$P[action]("mouseover", cancelMouseOut );
+ $P.unbind("mouseover");
+ $R.unbind("mouseover");
+ clearTimer(pane, "closeSlider");
+ }
+ }
+
+ // SUBROUTINE for mouseout timer clearing
+ function cancelMouseOut (evt) {
+ clearTimer(pane, "closeSlider");
+ evt.stopPropagation();
+ }
+ };
+
+ var slideOpen = function () {
+ var pane = $(this).attr("resizer"); // attr added by initHandles
+ if (state[pane].isClosed) { // skip if already open!
+ bindStopSlidingEvents(pane, true); // pane is opening, so BIND trigger events to close it
+ open(pane, true); // true = slide - ie, called from here!
+ }
+ };
+
+ var slideClosed = function () {
+ var
+ $E = $(this)
+ , pane = $E.attr("pane") || $E.attr("resizer")
+ , o = options[pane]
+ , s = state[pane]
+ ;
+ if (s.isClosed || s.isResizing)
+ return; // skip if already closed OR in process of resizing
+ else if (o.slideTrigger_close == "click")
+ close_NOW(); // close immediately onClick
+ else // trigger = mouseout - use a delay
+ setTimer(pane, "closeSlider", close_NOW, 300); // .3 sec delay
+
+ // SUBROUTINE for timed close
+ function close_NOW () {
+ bindStopSlidingEvents(pane, false); // pane is being closed, so UNBIND trigger events
+ if (!s.isClosed) close(pane); // skip if already closed!
+ }
+ };
+
+
+ /**
+ * sizePane
+ *
+ * @callers initResizable.stop()
+ * @param String pane The pane being resized - usually west or east, but potentially north or south
+ * @param Integer newSize The new size for this pane - will be validated
+ */
+ var sizePane = function (pane, size) {
+ // TODO: accept "auto" as size, and size-to-fit pane content
+ var
+ edge = c[pane].edge
+ , dir = c[pane].dir
+ , o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+ // calculate 'current' min/max sizes
+ setPaneMinMaxSizes(pane); // update pane-state
+ // compare/update calculated min/max to user-options
+ s.minSize = max(s.minSize, o.minSize);
+ if (o.maxSize > 0) s.maxSize = min(s.maxSize, o.maxSize);
+ // validate passed size
+ size = max(size, s.minSize);
+ size = min(size, s.maxSize);
+ s.size = size; // update state
+
+ // move the resizer bar and resize the pane
+ $R.css( edge, size + cDims[edge] );
+ $P.css( c[pane].sizeType, max(1, cssSize(pane, size)) );
+
+ // resize all the adjacent panes, and adjust their toggler buttons
+ if (!s.isSliding) sizeMidPanes(dir=="horz" ? "all" : "center");
+ sizeHandles();
+ sizeContent(pane);
+ execUserCallback(pane, o.onresize_end || o.onresize);
+ };
+
+ /**
+ * sizeMidPanes
+ *
+ * @callers create(), open(), close(), onWindowResize()
+ */
+ var sizeMidPanes = function (panes, overrideDims, onInit) {
+ if (!panes || panes == "all") panes = "east,west,center";
+
+ var d = getPaneDims();
+ if (overrideDims) $.extend( d, overrideDims );
+
+ $.each(panes.split(","), function() {
+ if (!$Ps[this]) return; // NO PANE - skip
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ , hasRoom = true
+ , CSS = {}
+ ;
+
+ if (pane == "center") {
+ d = getPaneDims(); // REFRESH Dims because may have just 'unhidden' East or West pane after a 'resize'
+ CSS = $.extend( {}, d ); // COPY ALL of the paneDims
+ CSS.width = max(1, cssW(pane, CSS.width));
+ CSS.height = max(1, cssH(pane, CSS.height));
+ hasRoom = (CSS.width > 1 && CSS.height > 1);
+ /*
+ * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
+ * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
+ */
+ if ($.browser.msie && (!$.boxModel || $.browser.version < 7)) {
+ if ($Ps.north) $Ps.north.css({ width: cssW($Ps.north, cDims.innerWidth) });
+ if ($Ps.south) $Ps.south.css({ width: cssW($Ps.south, cDims.innerWidth) });
+ }
+ }
+ else { // for east and west, set only the height
+ CSS.top = d.top;
+ CSS.bottom = d.bottom;
+ CSS.height = max(1, cssH(pane, d.height));
+ hasRoom = (CSS.height > 1);
+ }
+
+ if (hasRoom) {
+ $P.css(CSS);
+ if (s.noRoom) {
+ s.noRoom = false;
+ if (s.isHidden) return;
+ else show(pane, !s.isClosed);
+ /* OLD CODE - keep until sure line above works right!
+ if (!s.isClosed) $P.show(); // in case was previously hidden due to NOT hasRoom
+ if ($R) $R.show();
+ */
+ }
+ if (!onInit) {
+ sizeContent(pane);
+ execUserCallback(pane, o.onresize_end || o.onresize);
+ }
+ }
+ else if (!s.noRoom) { // no room for pane, so just hide it (if not already)
+ s.noRoom = true; // update state
+ if (s.isHidden) return;
+ if (onInit) { // skip onhide callback and other logic onLoad
+ $P.hide();
+ if ($R) $R.hide();
+ }
+ else hide(pane);
+ }
+ });
+ };
+
+
+ var sizeContent = function (panes) {
+ if (!panes || panes == "all") panes = c.allPanes;
+
+ $.each(panes.split(","), function() {
+ if (!$Cs[this]) return; // NO CONTENT - skip
+ var
+ pane = str(this)
+ , ignore = options[pane].contentIgnoreSelector
+ , $P = $Ps[pane]
+ , $C = $Cs[pane]
+ , e_C = $C[0] // DOM element
+ , height = cssH($P); // init to pane.innerHeight
+ ;
+ $P.children().each(function() {
+ if (this == e_C) return; // Content elem - skip
+ var $E = $(this);
+ if (!ignore || !$E.is(ignore))
+ height -= $E.outerHeight();
+ });
+ if (height > 0)
+ height = cssH($C, height);
+ if (height < 1)
+ $C.hide(); // no room for content!
+ else
+ $C.css({ height: height }).show();
+ });
+ };
+
+
+ /**
+ * sizeHandles
+ *
+ * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
+ *
+ * @callers initHandles(), open(), close(), resizeAll()
+ */
+ var sizeHandles = function (panes, onInit) {
+ if (!panes || panes == "all") panes = c.borderPanes;
+
+ $.each(panes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ , $T = $Ts[pane]
+ ;
+ if (!$P || !$R || (!o.resizable && !o.closable)) return; // skip
+
+ var
+ dir = c[pane].dir
+ , _state = (s.isClosed ? "_closed" : "_open")
+ , spacing = o["spacing"+ _state]
+ , togAlign = o["togglerAlign"+ _state]
+ , togLen = o["togglerLength"+ _state]
+ , paneLen
+ , offset
+ , CSS = {}
+ ;
+ if (spacing == 0) {
+ $R.hide();
+ return;
+ }
+ else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
+ $R.show(); // in case was previously hidden
+
+ // Resizer Bar is ALWAYS same width/height of pane it is attached to
+ if (dir == "horz") { // north/south
+ paneLen = $P.outerWidth();
+ $R.css({
+ width: max(1, cssW($R, paneLen)) // account for borders & padding
+ , height: max(1, cssH($R, spacing)) // ditto
+ , left: cssNum($P, "left")
+ });
+ }
+ else { // east/west
+ paneLen = $P.outerHeight();
+ $R.css({
+ height: max(1, cssH($R, paneLen)) // account for borders & padding
+ , width: max(1, cssW($R, spacing)) // ditto
+ , top: cDims.top + getPaneSize("north", true)
+ //, top: cssNum($Ps["center"], "top")
+ });
+
+ }
+
+ if ($T) {
+ if (togLen == 0 || (s.isSliding && o.hideTogglerOnSlide)) {
+ $T.hide(); // always HIDE the toggler when 'sliding'
+ return;
+ }
+ else
+ $T.show(); // in case was previously hidden
+
+ if (!(togLen > 0) || togLen == "100%" || togLen > paneLen) {
+ togLen = paneLen;
+ offset = 0;
+ }
+ else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
+ if (typeof togAlign == "string") {
+ switch (togAlign) {
+ case "top":
+ case "left": offset = 0;
+ break;
+ case "bottom":
+ case "right": offset = paneLen - togLen;
+ break;
+ case "middle":
+ case "center":
+ default: offset = Math.floor((paneLen - togLen) / 2); // 'default' catches typos
+ }
+ }
+ else { // togAlign = number
+ var x = parseInt(togAlign); //
+ if (togAlign >= 0) offset = x;
+ else offset = paneLen - togLen + x; // NOTE: x is negative!
+ }
+ }
+
+ var
+ $TC_o = (o.togglerContent_open ? $T.children(".content-open") : false)
+ , $TC_c = (o.togglerContent_closed ? $T.children(".content-closed") : false)
+ , $TC = (s.isClosed ? $TC_c : $TC_o)
+ ;
+ if ($TC_o) $TC_o.css("display", s.isClosed ? "none" : "block");
+ if ($TC_c) $TC_c.css("display", s.isClosed ? "block" : "none");
+
+ if (dir == "horz") { // north/south
+ var width = cssW($T, togLen);
+ $T.css({
+ width: max(0, width) // account for borders & padding
+ , height: max(1, cssH($T, spacing)) // ditto
+ , left: offset // TODO: VERIFY that toggler positions correctly for ALL values
+ });
+ if ($TC) // CENTER the toggler content SPAN
+ $TC.css("marginLeft", Math.floor((width-$TC.outerWidth())/2)); // could be negative
+ }
+ else { // east/west
+ var height = cssH($T, togLen);
+ $T.css({
+ height: max(0, height) // account for borders & padding
+ , width: max(1, cssW($T, spacing)) // ditto
+ , top: offset // POSITION the toggler
+ });
+ if ($TC) // CENTER the toggler content SPAN
+ $TC.css("marginTop", Math.floor((height-$TC.outerHeight())/2)); // could be negative
+ }
+
+
+ }
+
+ // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
+ if (onInit && o.initHidden) {
+ $R.hide();
+ if ($T) $T.hide();
+ }
+ });
+ };
+
+
+ /**
+ * resizeAll
+ *
+ * @callers window.onresize(), callbacks or custom code
+ */
+ var resizeAll = function () {
+
+ console.log("RESIZE!");
+ console.log($Container);
+ var
+ oldW = cDims.innerWidth
+ , oldH = cDims.innerHeight
+ ;
+ cDims = state.container = getElemDims($Container); // UPDATE container dimensions
+
+ var
+ checkH = (cDims.innerHeight < oldH)
+ , checkW = (cDims.innerWidth < oldW)
+ , s, dir
+ ;
+
+ if (checkH || checkW)
+ // NOTE special order for sizing: S-N-E-W
+ $.each(["south","north","east","west"], function(i,pane) {
+ console.log(this);
+ s = state[pane];
+ dir = c[pane].dir;
+ if (!s.isClosed && ((checkH && dir=="horz") || (checkW && dir=="vert"))) {
+ setPaneMinMaxSizes(pane); // update pane-state
+ // shrink pane if 'too big' to fit
+ if (s.size > s.maxSize) {
+ console.log("sizePane: " + pane);
+ sizePane(pane, s.maxSize);
+ }
+ }
+ });
+
+ sizeMidPanes("all");
+ sizeHandles("all"); // reposition the toggler elements
+ };
+
+
+ /**
+ * keyDown
+ *
+ * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
+ *
+ * @callers document.keydown()
+ */
+ function keyDown (evt) {
+ if (!evt) return true;
+ var code = evt.keyCode;
+ if (code < 33) return true; // ignore special keys: ENTER, TAB, etc
+
+ var
+ PANE = {
+ 38: "north" // Up Cursor
+ , 40: "south" // Down Cursor
+ , 37: "west" // Left Cursor
+ , 39: "east" // Right Cursor
+ }
+ , isCursorKey = (code >= 37 && code <= 40)
+ , ALT = evt.altKey // no worky!
+ , SHIFT = evt.shiftKey
+ , CTRL = evt.ctrlKey
+ , pane = false
+ , s, o, k, m, el
+ ;
+
+ if (!CTRL && !SHIFT)
+ return true; // no modifier key - abort
+ else if (isCursorKey && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
+ pane = PANE[code];
+ else // check to see if this matches a custom-hotkey
+ $.each(c.borderPanes.split(","), function(i,p) { // loop each pane to check its hotkey
+ o = options[p];
+ k = o.customHotkey;
+ m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
+ if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
+ if (k && code == (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
+ pane = p;
+ return false; // BREAK
+ }
+ }
+ });
+
+ if (!pane) return true; // no hotkey - abort
+
+ // validate pane
+ o = options[pane]; // get pane options
+ s = state[pane]; // get pane options
+ if (!o.enableCursorHotkey || s.isHidden || !$Ps[pane]) return true;
+
+ // see if user is in a 'form field' because may be 'selecting text'!
+ el = evt.target || evt.srcElement;
+ if (el && SHIFT && isCursorKey && (el.tagName=="TEXTAREA" || (el.tagName=="INPUT" && (code==37 || code==39))))
+ return true; // allow text-selection
+
+ // SYNTAX NOTES
+ // use "returnValue=false" to abort keystroke but NOT abort function - can run another command afterwards
+ // use "return false" to abort keystroke AND abort function
+ toggle(pane);
+ evt.stopPropagation();
+ evt.returnValue = false; // CANCEL key
+ return false;
+ };
+
+
+/*
+ * ###########################
+ * UTILITY METHODS
+ * called externally only
+ * ###########################
+ */
+
+ function allowOverflow (elem) {
+ if (this && this.tagName) elem = this; // BOUND to element
+ var $P;
+ if (typeof elem=="string")
+ $P = $Ps[elem];
+ else {
+ if ($(elem).attr("pane")) $P = $(elem);
+ else $P = $(elem).parents("div[pane]:first");
+ }
+ if (!$P.length) return; // INVALID
+
+ var
+ pane = $P.attr("pane")
+ , s = state[pane]
+ ;
+
+ // if pane is already raised, then reset it before doing it again!
+ // this would happen if allowOverflow is attached to BOTH the pane and an element
+ if (s.cssSaved)
+ resetOverflow(pane); // reset previous CSS before continuing
+
+ // if pane is raised by sliding or resizing, or it's closed, then abort
+ if (s.isSliding || s.isResizing || s.isClosed) {
+ s.cssSaved = false;
+ return;
+ }
+
+ var
+ newCSS = { zIndex: (c.zIndex.pane_normal + 1) }
+ , curCSS = {}
+ , of = $P.css("overflow")
+ , ofX = $P.css("overflowX")
+ , ofY = $P.css("overflowY")
+ ;
+ // determine which, if any, overflow settings need to be changed
+ if (of != "visible") {
+ curCSS.overflow = of;
+ newCSS.overflow = "visible";
+ }
+ if (ofX && ofX != "visible" && ofX != "auto") {
+ curCSS.overflowX = ofX;
+ newCSS.overflowX = "visible";
+ }
+ if (ofY && ofY != "visible" && ofY != "auto") {
+ curCSS.overflowY = ofX;
+ newCSS.overflowY = "visible";
+ }
+
+ // save the current overflow settings - even if blank!
+ s.cssSaved = curCSS;
+
+ // apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
+ $P.css( newCSS );
+
+ // make sure the zIndex of all other panes is normal
+ $.each(c.allPanes.split(","), function(i, p) {
+ if (p != pane) resetOverflow(p);
+ });
+
+ };
+
+ function resetOverflow (elem) {
+ if (this && this.tagName) elem = this; // BOUND to element
+ var $P;
+ if (typeof elem=="string")
+ $P = $Ps[elem];
+ else {
+ if ($(elem).hasClass("ui-layout-pane")) $P = $(elem);
+ else $P = $(elem).parents("div[pane]:first");
+ }
+ if (!$P.length) return; // INVALID
+
+ var
+ pane = $P.attr("pane")
+ , s = state[pane]
+ , CSS = s.cssSaved || {}
+ ;
+ // reset the zIndex
+ if (!s.isSliding && !s.isResizing)
+ $P.css("zIndex", c.zIndex.pane_normal);
+
+ // reset Overflow - if necessary
+ $P.css( CSS );
+
+ // clear var
+ s.cssSaved = false;
+ };
+
+
+ /**
+ * getBtn
+ *
+ * Helper function to validate params received by addButton utilities
+ *
+ * @param String selector jQuery selector for button, eg: ".ui-layout-north .toggle-button"
+ * @param String pane Name of the pane the button is for: 'north', 'south', etc.
+ * @returns If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise 'false'
+ */
+ function getBtn(selector, pane, action) {
+ var
+ $E = $(selector)
+ , err = "Error Adding Button \n\nInvalid "
+ ;
+ if (!$E.length) // element not found
+ alert(err+"selector: "+ selector);
+ else if (c.borderPanes.indexOf(pane) == -1) // invalid 'pane' sepecified
+ alert(err+"pane: "+ pane);
+ else { // VALID
+ var btn = options[pane].buttonClass +"-"+ action;
+ $E.addClass( btn +" "+ btn +"-"+ pane );
+ return $E;
+ }
+ return false; // INVALID
+ };
+
+
+ /**
+ * addToggleBtn
+ *
+ * Add a custom Toggler button for a pane
+ *
+ * @param String selector jQuery selector for button, eg: ".ui-layout-north .toggle-button"
+ * @param String pane Name of the pane the button is for: 'north', 'south', etc.
+ */
+ function addToggleBtn (selector, pane) {
+ var $E = getBtn(selector, pane, "toggle");
+ if ($E)
+ $E
+ .attr("title", state[pane].isClosed ? "Open" : "Close")
+ .click(function (evt) {
+ toggle(pane);
+ evt.stopPropagation();
+ })
+ ;
+ };
+
+ /**
+ * addOpenBtn
+ *
+ * Add a custom Open button for a pane
+ *
+ * @param String selector jQuery selector for button, eg: ".ui-layout-north .open-button"
+ * @param String pane Name of the pane the button is for: 'north', 'south', etc.
+ */
+ function addOpenBtn (selector, pane) {
+ var $E = getBtn(selector, pane, "open");
+ if ($E)
+ $E
+ .attr("title", "Open")
+ .click(function (evt) {
+ open(pane);
+ evt.stopPropagation();
+ })
+ ;
+ };
+
+ /**
+ * addCloseBtn
+ *
+ * Add a custom Close button for a pane
+ *
+ * @param String selector jQuery selector for button, eg: ".ui-layout-north .close-button"
+ * @param String pane Name of the pane the button is for: 'north', 'south', etc.
+ */
+ function addCloseBtn (selector, pane) {
+ var $E = getBtn(selector, pane, "close");
+ if ($E)
+ $E
+ .attr("title", "Close")
+ .click(function (evt) {
+ close(pane);
+ evt.stopPropagation();
+ })
+ ;
+ };
+
+ /**
+ * addPinBtn
+ *
+ * Add a custom Pin button for a pane
+ *
+ * Four classes are added to the element, based on the paneClass for the associated pane...
+ * Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
+ * - ui-layout-pane-pin
+ * - ui-layout-pane-west-pin
+ * - ui-layout-pane-pin-up
+ * - ui-layout-pane-west-pin-up
+ *
+ * @param String selector jQuery selector for button, eg: ".ui-layout-north .ui-layout-pin"
+ * @param String pane Name of the pane the pin is for: 'north', 'south', etc.
+ */
+ function addPinBtn (selector, pane) {
+ var $E = getBtn(selector, pane, "pin");
+ if ($E) {
+ var s = state[pane];
+ $E.click(function (evt) {
+ setPinState($(this), pane, (s.isSliding || s.isClosed));
+ if (s.isSliding || s.isClosed) open( pane ); // change from sliding to open
+ else close( pane ); // slide-closed
+ evt.stopPropagation();
+ });
+ // add up/down pin attributes and classes
+ setPinState ($E, pane, (!s.isClosed && !s.isSliding));
+ // add this pin to the pane data so we can 'sync it' automatically
+ // PANE.pins key is an array so we can store multiple pins for each pane
+ c[pane].pins.push( selector ); // just save the selector string
+ }
+ };
+
+ /**
+ * syncPinBtns
+ *
+ * INTERNAL function to sync 'pin buttons' when pane is opened or closed
+ * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
+ *
+ * @callers open(), close()
+ * @params pane These are the params returned to callbacks by layout()
+ * @params doPin True means set the pin 'down', False means 'up'
+ */
+ function syncPinBtns (pane, doPin) {
+ $.each(c[pane].pins, function (i, selector) {
+ setPinState($(selector), pane, doPin);
+ });
+ };
+
+ /**
+ * setPinState
+ *
+ * Change the class of the pin button to make it look 'up' or 'down'
+ *
+ * @callers addPinBtn(), syncPinBtns()
+ * @param Element $Pin The pin-span element in a jQuery wrapper
+ * @param Boolean doPin True = set the pin 'down', False = set it 'up'
+ * @param String pinClass The root classname for pins - will add '-up' or '-down' suffix
+ */
+ function setPinState ($Pin, pane, doPin) {
+ var updown = $Pin.attr("pin");
+ if (updown && doPin == (updown=="down")) return; // already in correct state
+ var
+ root = options[pane].buttonClass
+ , class1 = root +"-pin"
+ , class2 = class1 +"-"+ pane
+ , UP1 = class1 + "-up"
+ , UP2 = class2 + "-up"
+ , DN1 = class1 + "-down"
+ , DN2 = class2 + "-down"
+ ;
+ $Pin
+ .attr("pin", doPin ? "down" : "up") // logic
+ .attr("title", doPin ? "Un-Pin" : "Pin")
+ .removeClass( doPin ? UP1 : DN1 )
+ .removeClass( doPin ? UP2 : DN2 )
+ .addClass( doPin ? DN1 : UP1 )
+ .addClass( doPin ? DN2 : UP2 )
+ ;
+ };
+
+
+/*
+ * ###########################
+ * CREATE/RETURN BORDER-LAYOUT
+ * ###########################
+ */
+
+ // init global vars
+ var
+ $Container = $(this).css({ overflow: "hidden" }) // Container elem
+ , $Ps = {} // Panes x4 - set in initPanes()
+ , $Cs = {} // Content x4 - set in initPanes()
+ , $Rs = {} // Resizers x4 - set in initHandles()
+ , $Ts = {} // Togglers x4 - set in initHandles()
+ // object aliases
+ , c = config // alias for config hash
+ , cDims = state.container // alias for easy access to 'container dimensions'
+ ;
+
+ // create the border layout NOW
+ create();
+
+ // return object pointers to expose data & option Properties, and primary action Methods
+ return {
+ options: options // property - options hash
+ , state: state // property - dimensions hash
+ , panes: $Ps // property - object pointers for ALL panes: panes.north, panes.center
+ , toggle: toggle // method - pass a 'pane' ("north", "west", etc)
+ , open: open // method - ditto
+ , close: close // method - ditto
+ , hide: hide // method - ditto
+ , show: show // method - ditto
+ , resizeContent: sizeContent // method - ditto
+ , sizePane: sizePane // method - pass a 'pane' AND a 'size' in pixels
+ , resizeAll: resizeAll // method - no parameters
+ , addToggleBtn: addToggleBtn // utility - pass element selector and 'pane'
+ , addOpenBtn: addOpenBtn // utility - ditto
+ , addCloseBtn: addCloseBtn // utility - ditto
+ , addPinBtn: addPinBtn // utility - ditto
+ , allowOverflow: allowOverflow // utility - pass calling element
+ , resetOverflow: resetOverflow // utility - ditto
+ , cssWidth: cssW
+ , cssHeight: cssH
+ };
+
+}
+})( jQuery );
\ No newline at end of file
More information about the Mapbender_commits
mailing list