From 951287f062faeabe9ca0524224e50db489eba2ec Mon Sep 17 00:00:00 2001 From: thinkgem Date: Sat, 13 Jan 2018 14:13:01 +0800 Subject: [PATCH] =?UTF-8?q?resize=E4=BC=98=E5=8C=96=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jquery-plugins/jquery.layout-latest.css | 496 +- .../jquery-plugins/jquery.layout-latest.js | 12182 ++++++++-------- .../config}/ShiroConfig.java | 2 +- .../com/jeesite/modules/db/InitCoreData.xlsx | Bin 173406 -> 173521 bytes .../core/src/main/resources/jeesite-core.yml | 6 +- ....eclipse.wst.common.project.facet.core.xml | 14 +- web/pom.xml | 2 +- .../{ => modules}/config/Application.java | 4 +- .../config/task/MsgLocalSendTask.java | 2 +- .../config/web/DruidStatConfig.java | 2 +- .../config/web/FilterConfig.java | 2 +- .../config/web/ListenerConfig.java | 2 +- .../config/web/ServletConfig.java | 2 +- .../web/interceptor/LogInterceptorConfig.java | 2 +- .../MobileViewInterceptorConfig.java | 2 +- .../java/com/jeesite/test/InitCoreData.java | 22 +- 16 files changed, 6371 insertions(+), 6371 deletions(-) rename modules/core/src/main/java/com/jeesite/{config/common => modules/config}/ShiroConfig.java (96%) rename web/src/main/java/com/jeesite/{ => modules}/config/Application.java (88%) rename web/src/main/java/com/jeesite/{ => modules}/config/task/MsgLocalSendTask.java (93%) rename web/src/main/java/com/jeesite/{ => modules}/config/web/DruidStatConfig.java (93%) rename web/src/main/java/com/jeesite/{ => modules}/config/web/FilterConfig.java (95%) rename web/src/main/java/com/jeesite/{ => modules}/config/web/ListenerConfig.java (92%) rename web/src/main/java/com/jeesite/{ => modules}/config/web/ServletConfig.java (82%) rename web/src/main/java/com/jeesite/{ => modules}/config/web/interceptor/LogInterceptorConfig.java (94%) rename web/src/main/java/com/jeesite/{ => modules}/config/web/interceptor/MobileViewInterceptorConfig.java (92%) diff --git a/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.css b/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.css index e06e0272..51f89abd 100644 --- a/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.css +++ b/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.css @@ -1,249 +1,249 @@ -/* - * Default Layout Theme - * - * Created for jquery.layout - * - * Copyright (c) 2010 - * Fabrizio Balliano (http://www.fabrizioballiano.net) - * Kevin Dalman (http://allpro.net) - * - * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) - * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. - * - * Last Updated: 2010-02-10 - * NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars - */ - -.ui-draggable-handle{-ms-touch-action:none;touch-action:none} - -/* - * DEFAULT FONT - * Just to make demo-pages look better - not actually relevant to Layout! - */ -/* body { */ -/* font-family: Geneva, Arial, Helvetica, sans-serif; */ -/* font-size: 100%; */ -/* *font-size: 80%; */ -/* } */ - -/* - * PANES & CONTENT-DIVs - */ -.ui-layout-pane { /* all 'panes' */ -/* background: #FFF; */ -/* border: 1px solid #eee; */ -/* padding: 10px; */ - overflow: auto; - /* DO NOT add scrolling (or padding) to 'panes' that have a content-div, - otherwise you may get double-scrollbars - on the pane AND on the content-div - - use ui-layout-wrapper class if pane has a content-div - - use ui-layout-container if pane has an inner-layout - */ - } - /* (scrolling) content-div inside pane allows for fixed header(s) and/or footer(s) */ - .ui-layout-content { - padding: 10px; - position: relative; /* contain floated or positioned elements */ - overflow: auto; /* add scrolling to content-div */ - width: 100%; - border: 0; - } - -/* - * UTILITY CLASSES - * Must come AFTER pane-class above so will override - * These classes are NOT auto-generated and are NOT used by Layout - */ -.layout-child-container, -.layout-content-container { - padding: 0; - overflow: hidden; -} -.layout-child-container { - border: 0; /* remove border because inner-layout-panes probably have borders */ -} -.layout-scroll { - overflow: auto; -} -.layout-hide { - display: none; -} - -/* - * RESIZER-BARS - */ -.ui-layout-resizer { /* all 'resizer-bars' */ - background: #fafafa; - border: 1px solid #eee; - border-width: 0; - } - .ui-layout-resizer-drag { /* REAL resizer while resize in progress */ - } - .ui-layout-resizer-hover { /* affects both open and closed states */ - } - /* NOTE: It looks best when 'hover' and 'dragging' are set to the same color, - otherwise color shifts while dragging when bar can't keep up with mouse */ - .ui-layout-resizer-open-hover , /* hover-color to 'resize' */ - .ui-layout-resizer-dragging { /* resizer beging 'dragging' */ - background: #fafafa; - } - .ui-layout-resizer-dragging { /* CLONED resizer being dragged */ - border: 1px solid #eee; - } - .ui-layout-resizer-north-dragging, - .ui-layout-resizer-south-dragging { - border-width: 1px 0; - } - .ui-layout-resizer-west-dragging, - .ui-layout-resizer-east-dragging { - border-width: 0 1px; - } - /* NOTE: Add a 'dragging-limit' color to provide visual feedback when resizer hits min/max size limits */ - .ui-layout-resizer-dragging-limit { /* CLONED resizer at min or max size-limit */ - background: #E1A4A4; /* red */ - } - - .ui-layout-resizer-closed-hover { /* hover-color to 'slide open' */ - background: #EBD5AA; - } - .ui-layout-resizer-sliding { /* resizer when pane is 'slid open' */ -/* opacity: .10; show only a slight shadow */ -/* filter: alpha(opacity=10); */ - } - .ui-layout-resizer-sliding-hover { /* sliding resizer - hover */ -/* opacity: 1.00; on-hover, show the resizer-bar normally */ -/* filter: alpha(opacity=100); */ - } - /* sliding resizer - add 'outside-border' to resizer on-hover - * this sample illustrates how to target specific panes and states */ - .ui-layout-resizer-north-sliding-hover { border-bottom-width: 1px; } - .ui-layout-resizer-south-sliding-hover { border-top-width: 1px; } - .ui-layout-resizer-west-sliding-hover { border-right-width: 1px; } - .ui-layout-resizer-east-sliding-hover { border-left-width: 1px; } - -/* - * TOGGLER-BUTTONS - */ -.ui-layout-toggler { - border: 1px solid #eee; /* match pane-border */ - background-color: #eee; - } - .ui-layout-resizer-hover .ui-layout-toggler { - opacity: 1.00; - filter: alpha(opacity=100); - } - .ui-layout-toggler-hover , /* need when NOT resizable */ - .ui-layout-resizer-hover .ui-layout-toggler-hover { /* need specificity when IS resizable */ - background-color: #FC6; - opacity: 1.00; - filter: alpha(opacity=100); - } - .ui-layout-toggler-north , - .ui-layout-toggler-south { - border-width: 0 1px; /* left/right borders */ - } - .ui-layout-toggler-west , - .ui-layout-toggler-east { - border-width: 1px 0; /* top/bottom borders */ - } - /* hide the toggler-button when the pane is 'slid open' */ - .ui-layout-resizer-sliding .ui-layout-toggler { - display: none; - } - /* - * style the text we put INSIDE the togglers - */ - .ui-layout-toggler .ui-content { - color: #666; - font-size: 12px; - font-weight: bold; - line-height: 8px; - width: 100%; - padding-bottom: 0.35ex; /* to 'vertically center' text inside text-span */ - } - .ui-layout-toggler .ui-content .fa{ - line-height: 8px; - } - - .ui-layout-toggler-north-closed .fa:before, - .ui-layout-toggler-south-open .fa:before { - content: "\f0d7";/* 下 */ - } - .ui-layout-toggler-south-closed .fa:before, - .ui-layout-toggler-north-open .fa:before { - content: "\f0d8";/* 上 */ - } - .ui-layout-toggler-west-closed .fa:before, - .ui-layout-toggler-east-open .fa:before { - content: "\f0da";/* 右 */ - } - .ui-layout-toggler-east-closed .fa:before, - .ui-layout-toggler-west-open .fa:before { - content: "\f0d9";/* 左 */ - } - -/* - * PANE-MASKS - * these styles are hard-coded on mask elems, but are also - * included here as !important to ensure will overrides any generic styles - */ -.ui-layout-mask { - border: none !important; - padding: 0 !important; - margin: 0 !important; - overflow: hidden !important; - position: absolute !important; - opacity: 0 !important; - filter: Alpha(Opacity="0") !important; -} -.ui-layout-mask-inside-pane { /* masks always inside pane EXCEPT when pane is an iframe */ - top: 0 !important; - left: 0 !important; - width: 100% !important; - height: 100% !important; -} -div.ui-layout-mask {} /* standard mask for iframes */ -iframe.ui-layout-mask {} /* extra mask for objects/applets */ - -/* - * Default printing styles - */ -@media print { - /* - * Unless you want to print the layout as it appears onscreen, - * these html/body styles are needed to allow the content to 'flow' - */ - html { - height: auto !important; - overflow: visible !important; - } - body.ui-layout-container { - position: static !important; - top: auto !important; - bottom: auto !important; - left: auto !important; - right: auto !important; - /* only IE6 has container width & height set by Layout */ - _width: auto !important; - _height: auto !important; - } - .ui-layout-resizer, .ui-layout-toggler { - display: none !important; - } - /* - * Default pane print styles disables positioning, borders and backgrounds. - * You can modify these styles however it suit your needs. - */ - .ui-layout-pane { - border: none !important; - background: transparent !important; - position: relative !important; - top: auto !important; - bottom: auto !important; - left: auto !important; - right: auto !important; - width: auto !important; - height: auto !important; - overflow: visible !important; - } +/* + * Default Layout Theme + * + * Created for jquery.layout + * + * Copyright (c) 2010 + * Fabrizio Balliano (http://www.fabrizioballiano.net) + * Kevin Dalman (http://allpro.net) + * + * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) + * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. + * + * Last Updated: 2010-02-10 + * NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars + */ + +.ui-draggable-handle{-ms-touch-action:none;touch-action:none} + +/* + * DEFAULT FONT + * Just to make demo-pages look better - not actually relevant to Layout! + */ +/* body { */ +/* font-family: Geneva, Arial, Helvetica, sans-serif; */ +/* font-size: 100%; */ +/* *font-size: 80%; */ +/* } */ + +/* + * PANES & CONTENT-DIVs + */ +.ui-layout-pane { /* all 'panes' */ +/* background: #FFF; */ +/* border: 1px solid #eee; */ +/* padding: 10px; */ + overflow: auto; + /* DO NOT add scrolling (or padding) to 'panes' that have a content-div, + otherwise you may get double-scrollbars - on the pane AND on the content-div + - use ui-layout-wrapper class if pane has a content-div + - use ui-layout-container if pane has an inner-layout + */ + } + /* (scrolling) content-div inside pane allows for fixed header(s) and/or footer(s) */ + .ui-layout-content { + padding: 10px; + position: relative; /* contain floated or positioned elements */ + overflow: auto; /* add scrolling to content-div */ + width: 100%; + border: 0; + } + +/* + * UTILITY CLASSES + * Must come AFTER pane-class above so will override + * These classes are NOT auto-generated and are NOT used by Layout + */ +.layout-child-container, +.layout-content-container { + padding: 0; + overflow: hidden; +} +.layout-child-container { + border: 0; /* remove border because inner-layout-panes probably have borders */ +} +.layout-scroll { + overflow: auto; +} +.layout-hide { + display: none; +} + +/* + * RESIZER-BARS + */ +.ui-layout-resizer { /* all 'resizer-bars' */ + background: #fafafa; + border: 1px solid #eee; + border-width: 0; + } + .ui-layout-resizer-drag { /* REAL resizer while resize in progress */ + } + .ui-layout-resizer-hover { /* affects both open and closed states */ + } + /* NOTE: It looks best when 'hover' and 'dragging' are set to the same color, + otherwise color shifts while dragging when bar can't keep up with mouse */ + .ui-layout-resizer-open-hover , /* hover-color to 'resize' */ + .ui-layout-resizer-dragging { /* resizer beging 'dragging' */ + background: #fafafa; + } + .ui-layout-resizer-dragging { /* CLONED resizer being dragged */ + border: 1px solid #eee; + } + .ui-layout-resizer-north-dragging, + .ui-layout-resizer-south-dragging { + border-width: 1px 0; + } + .ui-layout-resizer-west-dragging, + .ui-layout-resizer-east-dragging { + border-width: 0 1px; + } + /* NOTE: Add a 'dragging-limit' color to provide visual feedback when resizer hits min/max size limits */ + .ui-layout-resizer-dragging-limit { /* CLONED resizer at min or max size-limit */ + background: #E1A4A4; /* red */ + } + + .ui-layout-resizer-closed-hover { /* hover-color to 'slide open' */ + background: #EBD5AA; + } + .ui-layout-resizer-sliding { /* resizer when pane is 'slid open' */ +/* opacity: .10; show only a slight shadow */ +/* filter: alpha(opacity=10); */ + } + .ui-layout-resizer-sliding-hover { /* sliding resizer - hover */ +/* opacity: 1.00; on-hover, show the resizer-bar normally */ +/* filter: alpha(opacity=100); */ + } + /* sliding resizer - add 'outside-border' to resizer on-hover + * this sample illustrates how to target specific panes and states */ + .ui-layout-resizer-north-sliding-hover { border-bottom-width: 1px; } + .ui-layout-resizer-south-sliding-hover { border-top-width: 1px; } + .ui-layout-resizer-west-sliding-hover { border-right-width: 1px; } + .ui-layout-resizer-east-sliding-hover { border-left-width: 1px; } + +/* + * TOGGLER-BUTTONS + */ +.ui-layout-toggler { + border: 1px solid #eee; /* match pane-border */ + background-color: #eee; + } + .ui-layout-resizer-hover .ui-layout-toggler { + opacity: 1.00; + filter: alpha(opacity=100); + } + .ui-layout-toggler-hover , /* need when NOT resizable */ + .ui-layout-resizer-hover .ui-layout-toggler-hover { /* need specificity when IS resizable */ + background-color: #FC6; + opacity: 1.00; + filter: alpha(opacity=100); + } + .ui-layout-toggler-north , + .ui-layout-toggler-south { + border-width: 0 1px; /* left/right borders */ + } + .ui-layout-toggler-west , + .ui-layout-toggler-east { + border-width: 1px 0; /* top/bottom borders */ + } + /* hide the toggler-button when the pane is 'slid open' */ + .ui-layout-resizer-sliding .ui-layout-toggler { + display: none; + } + /* + * style the text we put INSIDE the togglers + */ + .ui-layout-toggler .ui-content { + color: #666; + font-size: 12px; + font-weight: bold; + line-height: 8px; + width: 100%; + padding-bottom: 0.35ex; /* to 'vertically center' text inside text-span */ + } + .ui-layout-toggler .ui-content .fa{ + line-height: 8px; + } + + .ui-layout-toggler-north-closed .fa:before, + .ui-layout-toggler-south-open .fa:before { + content: "\f0d7";/* 下 */ + } + .ui-layout-toggler-south-closed .fa:before, + .ui-layout-toggler-north-open .fa:before { + content: "\f0d8";/* 上 */ + } + .ui-layout-toggler-west-closed .fa:before, + .ui-layout-toggler-east-open .fa:before { + content: "\f0da";/* 右 */ + } + .ui-layout-toggler-east-closed .fa:before, + .ui-layout-toggler-west-open .fa:before { + content: "\f0d9";/* 左 */ + } + +/* + * PANE-MASKS + * these styles are hard-coded on mask elems, but are also + * included here as !important to ensure will overrides any generic styles + */ +.ui-layout-mask { + border: none !important; + padding: 0 !important; + margin: 0 !important; + overflow: hidden !important; + position: absolute !important; + opacity: 0 !important; + filter: Alpha(Opacity="0") !important; +} +.ui-layout-mask-inside-pane { /* masks always inside pane EXCEPT when pane is an iframe */ + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; +} +div.ui-layout-mask {} /* standard mask for iframes */ +iframe.ui-layout-mask {} /* extra mask for objects/applets */ + +/* + * Default printing styles + */ +@media print { + /* + * Unless you want to print the layout as it appears onscreen, + * these html/body styles are needed to allow the content to 'flow' + */ + html { + height: auto !important; + overflow: visible !important; + } + body.ui-layout-container { + position: static !important; + top: auto !important; + bottom: auto !important; + left: auto !important; + right: auto !important; + /* only IE6 has container width & height set by Layout */ + _width: auto !important; + _height: auto !important; + } + .ui-layout-resizer, .ui-layout-toggler { + display: none !important; + } + /* + * Default pane print styles disables positioning, borders and backgrounds. + * You can modify these styles however it suit your needs. + */ + .ui-layout-pane { + border: none !important; + background: transparent !important; + position: relative !important; + top: auto !important; + bottom: auto !important; + left: auto !important; + right: auto !important; + width: auto !important; + height: auto !important; + overflow: visible !important; + } } \ No newline at end of file diff --git a/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.js b/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.js index 3bd209e2..9e3e7290 100644 --- a/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.js +++ b/common/src/main/resources/static/jquery-plugins/jquery.layout-latest.js @@ -1,6091 +1,6091 @@ -/** - * @preserve - * jquery.layout 1.4.4 - * $Date: 2014-11-29 08:00:00 (Sat, 29 November 2014) $ - * $Rev: 1.0404 $ - * - * Copyright (c) 2014 Kevin Dalman (http://jquery-dev.com) - * Based on work by Fabrizio Balliano (http://www.fabrizioballiano.net) - * - * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) - * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. - * - * SEE: http://layout.jquery-dev.com/LICENSE.txt - * - * Changelog: http://layout.jquery-dev.com/changelog.cfm - * - * Docs: http://layout.jquery-dev.com/documentation.html - * Tips: http://layout.jquery-dev.com/tips.html - * Help: http://groups.google.com/group/jquery-ui-layout - */ - -//
-//
-//
西
-//
-//
-// - -/* JavaDoc Info: http://code.google.com/closure/compiler/docs/js-for-compiler.html - * {!Object} non-nullable type (never NULL) - * {?string} nullable type (sometimes NULL) - default for {Object} - * {number=} optional parameter - * {*} ALL types - */ -/* TODO for jQ 2.x - * check $.fn.disableSelection - this is in jQuery UI 1.9.x - */ - -// NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars - -;(function ($) { - -// alias Math methods - used a lot! -var min = Math.min -, max = Math.max -, round = Math.floor - -, isStr = function (v) { return $.type(v) === "string"; } - - /** - * @param {!Object} Instance - * @param {Array.} a_fn - */ -, runPluginCallbacks = function (Instance, a_fn) { - if ($.isArray(a_fn)) - for (var i=0, c=a_fn.length; i').appendTo("body") - , d = { width: $c.outerWidth - $c[0].clientWidth, height: 100 - $c[0].clientHeight }; - $c.remove(); - window.scrollbarWidth = d.width; - window.scrollbarHeight = d.height; - return dim.match(/^(width|height)$/) ? d[dim] : d; - } - - -, disableTextSelection: function () { - var $d = $(document) - , s = 'textSelectionDisabled' - , x = 'textSelectionInitialized' - ; - if ($.fn.disableSelection) { - if (!$d.data(x)) // document hasn't been initialized yet - $d.on('mouseup', $.layout.enableTextSelection ).data(x, true); - if (!$d.data(s)) - $d.disableSelection().data(s, true); - } - } -, enableTextSelection: function () { - var $d = $(document) - , s = 'textSelectionDisabled'; - if ($.fn.enableSelection && $d.data(s)) - $d.enableSelection().data(s, false); - } - - - /** - * Returns hash container 'display' and 'visibility' - * - * @see $.swap() - swaps CSS, runs callback, resets CSS - * @param {!Object} $E jQuery element - * @param {boolean=} [force=false] Run even if display != none - * @return {!Object} Returns current style props, if applicable - */ -, showInvisibly: function ($E, force) { - if ($E && $E.length && (force || $E.css("display") === "none")) { // only if not *already hidden* - var s = $E[0].style - // save ONLY the 'style' props because that is what we must restore - , CSS = { display: s.display || '', visibility: s.visibility || '' }; - // show element 'invisibly' so can be measured - $E.css({ display: "block", visibility: "hidden" }); - return CSS; - } - return {}; - } - - /** - * Returns data for setting size of an element (container or a pane). - * - * @see _create(), onWindowResize() for container, plus others for pane - * @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc - */ -, getElementDimensions: function ($E, inset) { - var - // dimensions hash - start with current data IF passed - d = { css: {}, inset: {} } - , x = d.css // CSS hash - , i = { bottom: 0 } // TEMP insets (bottom = complier hack) - , N = $.layout.cssNum - , R = Math.round - , off = $E.offset() - , b, p, ei // TEMP border, padding - ; - d.offsetLeft = off.left; - d.offsetTop = off.top; - - if (!inset) inset = {}; // simplify logic below - - $.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge - b = x["border" + e] = $.layout.borderWidth($E, e); - p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e); - ei = e.toLowerCase(); - d.inset[ei] = inset[ei] >= 0 ? inset[ei] : p; // any missing insetX value = paddingX - i[ei] = d.inset[ei] + b; // total offset of content from outer side - }); - - x.width = R($E.width()); - x.height = R($E.height()); - x.top = N($E,"top",true); - x.bottom = N($E,"bottom",true); - x.left = N($E,"left",true); - x.right = N($E,"right",true); - - d.outerWidth = R($E.outerWidth()); - d.outerHeight = R($E.outerHeight()); - // calc the TRUE inner-dimensions, even in quirks-mode! - d.innerWidth = max(0, d.outerWidth - i.left - i.right); - d.innerHeight = max(0, d.outerHeight - i.top - i.bottom); - // layoutWidth/Height is used in calcs for manual resizing - // layoutW/H only differs from innerW/H when in quirks-mode - then is like outerW/H - d.layoutWidth = R($E.innerWidth()); - d.layoutHeight = R($E.innerHeight()); - - //if ($E.prop('tagName') === 'BODY') { debugData( d, $E.prop('tagName') ); } // DEBUG - - //d.visible = $E.is(":visible");// && x.width > 0 && x.height > 0; - - return d; - } - -, getElementStyles: function ($E, list) { - var - CSS = {} - , style = $E[0].style - , props = list.split(",") - , sides = "Top,Bottom,Left,Right".split(",") - , attrs = "Color,Style,Width".split(",") - , p, s, a, i, j, k - ; - for (i=0; i < props.length; i++) { - p = props[i]; - if (p.match(/(border|padding|margin)$/)) - for (j=0; j < 4; j++) { - s = sides[j]; - if (p === "border") - for (k=0; k < 3; k++) { - a = attrs[k]; - CSS[p+s+a] = style[p+s+a]; - } - else - CSS[p+s] = style[p+s]; - } - else - CSS[p] = style[p]; - }; - return CSS - } - - /** - * Return the innerWidth for the current browser/doctype - * - * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles() - * @param {Array.} $E Must pass a jQuery object - first element is processed - * @param {number=} outerWidth (optional) Can pass a width, allowing calculations BEFORE element is resized - * @return {number} Returns the innerWidth of the elem by subtracting padding and borders - */ -, cssWidth: function ($E, outerWidth) { - // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed - if (outerWidth <= 0) return 0; - - var lb = $.layout.browser - , bs = !lb.boxModel ? "border-box" : lb.boxSizing ? $E.css("boxSizing") : "content-box" - , b = $.layout.borderWidth - , n = $.layout.cssNum - , W = outerWidth - ; - // strip border and/or padding from outerWidth to get CSS Width - if (bs !== "border-box") - W -= (b($E, "Left") + b($E, "Right")); - if (bs === "content-box") - W -= (n($E, "paddingLeft") + n($E, "paddingRight")); - return max(0,W); - } - - /** - * Return the innerHeight for the current browser/doctype - * - * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles() - * @param {Array.} $E Must pass a jQuery object - first element is processed - * @param {number=} outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized - * @return {number} Returns the innerHeight of the elem by subtracting padding and borders - */ -, cssHeight: function ($E, outerHeight) { - // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed - if (outerHeight <= 0) return 0; - - var lb = $.layout.browser - , bs = !lb.boxModel ? "border-box" : lb.boxSizing ? $E.css("boxSizing") : "content-box" - , b = $.layout.borderWidth - , n = $.layout.cssNum - , H = outerHeight - ; - // strip border and/or padding from outerHeight to get CSS Height - if (bs !== "border-box") - H -= (b($E, "Top") + b($E, "Bottom")); - if (bs === "content-box") - H -= (n($E, "paddingTop") + n($E, "paddingBottom")); - return max(0,H); - } - - /** - * Returns the 'current CSS numeric value' for a CSS property - 0 if property does not exist - * - * @see Called by many methods - * @param {Array.} $E Must pass a jQuery object - first element is processed - * @param {string} prop The name of the CSS property, eg: top, width, etc. - * @param {boolean=} [allowAuto=false] true = return 'auto' if that is value; false = return 0 - * @return {(string|number)} Usually used to get an integer value for position (top, left) or size (height, width) - */ -, cssNum: function ($E, prop, allowAuto) { - if (!$E.jquery) $E = $($E); - var CSS = $.layout.showInvisibly($E) - , p = $.css($E[0], prop, true) - , v = allowAuto && p=="auto" ? p : Math.round(parseFloat(p) || 0); - $E.css( CSS ); // RESET - return v; - } - -, borderWidth: function (el, side) { - if (el.jquery) el = el[0]; - var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left - return $.css(el, b+"Style", true) === "none" ? 0 : Math.round(parseFloat($.css(el, b+"Width", true)) || 0); - } - - /** - * Mouse-tracking utility - FUTURE REFERENCE - * - * init: if (!window.mouse) { - * window.mouse = { x: 0, y: 0 }; - * $(document).mousemove( $.layout.trackMouse ); - * } - * - * @param {Object} evt - * -, trackMouse: function (evt) { - window.mouse = { x: evt.clientX, y: evt.clientY }; - } - */ - - /** - * SUBROUTINE for preventPrematureSlideClose option - * - * @param {Object} evt - * @param {Object=} el - */ -, isMouseOverElem: function (evt, el) { - var - $E = $(el || this) - , d = $E.offset() - , T = d.top - , L = d.left - , R = L + $E.outerWidth() - , B = T + $E.outerHeight() - , x = evt.pageX // evt.clientX ? - , y = evt.pageY // evt.clientY ? - ; - // if X & Y are < 0, probably means is over an open SELECT - return ($.layout.browser.msie && x < 0 && y < 0) || ((x >= L && x <= R) && (y >= T && y <= B)); - } - - /** - * Message/Logging Utility - * - * @example $.layout.msg("My message"); // log text - * @example $.layout.msg("My message", true); // alert text - * @example $.layout.msg({ foo: "bar" }, "Title"); // log hash-data, with custom title - * @example $.layout.msg({ foo: "bar" }, true, "Title", { sort: false }); -OR- - * @example $.layout.msg({ foo: "bar" }, "Title", { sort: false, display: true }); // alert hash-data - * - * @param {(Object|string)} info String message OR Hash/Array - * @param {(Boolean|string|Object)=} [popup=false] True means alert-box - can be skipped - * @param {(Object|string)=} [debugTitle=""] Title for Hash data - can be skipped - * @param {Object=} [debugOpts] Extra options for debug output - */ -, msg: function (info, popup, debugTitle, debugOpts) { - if ($.isPlainObject(info) && window.debugData) { - if (typeof popup === "string") { - debugOpts = debugTitle; - debugTitle = popup; - } - else if (typeof debugTitle === "object") { - debugOpts = debugTitle; - debugTitle = null; - } - var t = debugTitle || "log( )" - , o = $.extend({ sort: false, returnHTML: false, display: false }, debugOpts); - if (popup === true || o.display) - debugData( info, t, o ); - else if (window.console) - console.log(debugData( info, t, o )); - } - else if (popup) - alert(info); - else if (window.console) - console.log(info); - else { - var id = "#layoutLogger" - , $l = $(id); - if (!$l.length) - $l = createLog(); - $l.children("ul").append('
  • '+ info.replace(/\/g,">") +'
  • '); - } - - function createLog () { - var pos = $.support.fixedPosition ? 'fixed' : 'absolute' - , $e = $('
    ' - + '
    ' - + 'XLayout console.log
    ' - + '
      ' - + '
      ' - ).appendTo("body"); - $e.css('left', $(window).width() - $e.outerWidth() - 5) - if ($.ui.draggable) $e.draggable({ handle: ':first-child' }); - return $e; - }; - } - -}; - - -/* - * $.layout.browser REPLACES removed $.browser, with extra data - * Parsing code here adapted from jQuery 1.8 $.browse - */ -(function(){ - var u = navigator.userAgent.toLowerCase() - , m = /(chrome)[ \/]([\w.]+)/.exec( u ) - || /(webkit)[ \/]([\w.]+)/.exec( u ) - || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( u ) - || /(msie) ([\w.]+)/.exec( u ) - || u.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( u ) - || [] - , b = m[1] || "" - , v = m[2] || 0 - , ie = b === "msie" - , cm = document.compatMode - , $s = $.support - , bs = $s.boxSizing !== undefined ? $s.boxSizing : $s.boxSizingReliable - , bm = !ie || !cm || cm === "CSS1Compat" || $s.boxModel || false - , lb = $.layout.browser = { - version: v - , safari: b === "webkit" // webkit (NOT chrome) = safari - , webkit: b === "chrome" // chrome = webkit - , msie: ie - , isIE6: ie && v == 6 - // ONLY IE reverts to old box-model - Note that compatMode was deprecated as of IE8 - , boxModel: bm - , boxSizing: !!(typeof bs === "function" ? bs() : bs) - }; - ; - if (b) lb[b] = true; // set CURRENT browser - /* OLD versions of jQuery only set $.support.boxModel after page is loaded - * so if this is IE, use support.boxModel to test for quirks-mode (ONLY IE changes boxModel) */ - if (!bm && !cm) $(function(){ lb.boxModel = $s.boxModel; }); -})(); - - -// DEFAULT OPTIONS -$.layout.defaults = { -/* - * LAYOUT & LAYOUT-CONTAINER OPTIONS - * - none of these options are applicable to individual panes - */ - name: "" // Not required, but useful for buttons and used for the state-cookie -, containerClass: "ui-layout-container" // layout-container element -, inset: null // custom container-inset values (override padding) -, scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark) -, resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event -, resizeWithWindowDelay: 200 // delay calling resizeAll because makes window resizing very jerky -, resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized -, maskPanesEarly: false // true = create pane-masks on resizer.mouseDown instead of waiting for resizer.dragstart -, onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific -, onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific -, onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements -, onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized -, onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload -, onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload -, initPanes: true // false = DO NOT initialize the panes onLoad - will init later -, showErrorMessages: true // enables fatal error messages to warn developers of common errors -, showDebugMessages: false // display console-and-alert debug msgs - IF this Layout version _has_ debugging code! -// Changing this zIndex value will cause other zIndex values to automatically change -, zIndex: null // the PANE zIndex - resizers and masks will be +1 -// DO NOT CHANGE the zIndex values below unless you clearly understand their relationships -, zIndexes: { // set _default_ z-index values here... - pane_normal: 0 // normal z-index for panes - , content_mask: 1 // applied to overlays used to mask content INSIDE panes during resizing - , resizer_normal: 2 // normal z-index for resizer-bars - , pane_sliding: 100 // applied to *BOTH* the pane and its resizer when a pane is 'slid open' - , pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer - , resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged' - } -, errors: { - pane: "pane" // description of "layout pane element" - used only in error messages - , selector: "selector" // description of "jQuery-selector" - used only in error messages - , addButtonError: "Error Adding Button\nInvalid " - , containerMissing: "UI Layout Initialization Error\nThe specified layout-container does not exist." - , centerPaneMissing: "UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element." - , noContainerHeight: "UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!" - , callbackError: "UI Layout Callback Error\nThe EVENT callback is not a valid function." - } -/* - * PANE DEFAULT SETTINGS - * - settings under the 'panes' key become the default settings for *all panes* - * - ALL pane-options can also be set specifically for each panes, which will override these 'default values' - */ -, panes: { // default options for 'all panes' - will be overridden by 'per-pane settings' - applyDemoStyles: false // NOTE: renamed from applyDefaultStyles for clarity - , 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 - , initClosed: false // true = init pane as 'closed' - , initHidden: false // true = init pane as 'hidden' - no resizer-bar/spacing - // SELECTORS - //, paneSelector: "" // MUST be pane-specific - jQuery selector for pane - , contentSelector: ".ui-layout-content" // ThinkGem 自动高度的内容类。 // INNER div/element to auto-size so only it scrolls, not the entire pane! - , contentIgnoreSelector: ".ui-layout-ignore" // element(s) to 'ignore' when measuring 'content' - , findNestedContent: true // ThinkGem 设置 contentSelector 自动高度,默认false // true = $P.find(contentSelector), false = $P.children(contentSelector) - // GENERIC ROOT-CLASSES - for auto-generated classNames - , paneClass: "ui-layout-pane" // Layout Pane - , resizerClass: "ui-layout-resizer" // Resizer Bar - , togglerClass: "ui-layout-toggler" // Toggler Button - , buttonClass: "ui-layout-button" // CUSTOM Buttons - eg: '[ui-layout-button]-toggle/-open/-close/-pin' - // ELEMENT SIZE & SPACING - //, size: 100 // MUST be pane-specific -initial size of pane - , minSize: 0 // when manually resizing a pane - , maxSize: 0 // ditto, 0 = no limit - , spacing_open: 8 // ThinkGem 分隔符宽度,默认6 // space between pane and adjacent panes - when pane is 'open' - , spacing_closed: 8 // ThinkGem 分隔符宽度,默认6 // ditto - when pane is 'closed' - , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south sides - HEIGHT on east/west sides - , 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 - , togglerContent_open: "" // ThinkGem 设置展开折叠图标,默认空"" // text or HTML to put INSIDE the toggler - , togglerContent_closed: "" // ThinkGem 设置展开折叠图标,默认空"" // ditto - // RESIZING OPTIONS - , resizerDblClickToggle: true // - , autoResize: true // IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes - , autoReopen: true // IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed - , resizerDragOpacity: 1 // option for ui.draggable - //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar - , maskContents: false // true = add DIV-mask over-or-inside this pane so can 'drag' over IFRAMES - , maskObjects: false // true = add IFRAME-mask over-or-inside this pane to cover objects/applets - content-mask will overlay this mask - , maskZindex: null // will override zIndexes.content_mask if specified - not applicable to iframe-panes - , resizingGrid: false // grid size that the resizers will snap-to during resizing, eg: [20,20] - , livePaneResizing: false // true = LIVE Resizing as resizer is dragged - , liveContentResizing: false // true = re-measure header/footer heights as resizer is dragged - , liveResizingTolerance: 1 // how many px change before pane resizes, to control performance - // SLIDING OPTIONS - , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding' - , slideTrigger_open: "mouseenter"// ThinkGem 设置侧边栏自动展开动作,默认click // click, dblclick, mouseenter - , slideTrigger_close: "mouseleave"// ThinkGem 设置侧边栏自动展开动作,默认mouseleave // click, mouseleave - , slideDelay_open: 100 // ThinkGem 设置鼠标放到侧边栏的时候自动展开的延迟时间,默认300 //applies only for mouseenter event - 0 = instant open - , slideDelay_close: 500 // ThinkGem 设置鼠标放到侧边栏的时候自动展开的延迟时间,默认300 //applies only for mouseleave event (300ms is the minimum!) - , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show? - , preventQuickSlideClose: $.layout.browser.webkit // Chrome triggers slideClosed as it is opening - , preventPrematureSlideClose: false // handle incorrect mouseleave trigger, like when over a SELECT-list in IE - // PANE-SPECIFIC TIPS & MESSAGES - , tips: { - Open: "展开" // ThinkGem 汉化,默认Open // eg: "Open Pane" - , Close: "折叠" // ThinkGem 汉化,默认Close - , Resize: "调整大小/双击折叠" // ThinkGem 汉化,默认Resize - , Slide: "鼠标停留自动展开" // ThinkGem 汉化,默认Slide Open - , Pin: "Pin" - , Unpin: "Un-Pin" - , noRoomToOpen: "Not enough room to show this panel." // alert if user tries to open a pane that cannot - , minSizeWarning: "Panel has reached its minimum size" // displays in browser statusbar - , maxSizeWarning: "Panel has reached its maximum size" // ditto - } - // HOT-KEYS & MISC - , 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' - // PANE ANIMATION - // NOTE: fxSss_open, fxSss_close & fxSss_size options (eg: fxName_open) are auto-generated if not passed - , fxName: "slide" // ('none' or blank), slide, drop, scale -- only relevant to 'open' & 'close', NOT 'size' - , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration - , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 } - , fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation - , animatePaneSizing: false // true = animate resizing after dragging resizer-bar OR sizePane() is called - /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set: - fxName_open: "slide" // 'Open' pane animation - fnName_close: "slide" // 'Close' pane animation - fxName_size: "slide" // 'Size' pane animation - when animatePaneSizing = true - fxSpeed_open: null - fxSpeed_close: null - fxSpeed_size: null - fxSettings_open: {} - fxSettings_close: {} - fxSettings_size: {} - */ - // CHILD/NESTED LAYOUTS - , children: null // Layout-options for nested/child layout - even {} is valid as options - , containerSelector: '' // if child is NOT 'directly nested', a selector to find it/them (can have more than one child layout!) - , initChildren: true // true = child layout will be created as soon as _this_ layout completes initialization - , destroyChildren: true // true = destroy child-layout if this pane is destroyed - , resizeChildren: true // true = trigger child-layout.resizeAll() when this pane is resized - // EVENT TRIGGERING - , triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes - , triggerEventsDuringLiveResize: true // true = trigger onresize callback REPEATEDLY if livePaneResizing==true - // PANE CALLBACKS - , onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start - , onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end - , onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start - , onhide_end: null // CALLBACK when pane ENDS being Closed - AFTER onclose_end - , onopen_start: null // CALLBACK when pane STARTS to Open - , onopen_end: null // CALLBACK when pane ENDS being Opened - , onclose_start: null // CALLBACK when pane STARTS to Close - , onclose_end: null // CALLBACK when pane ENDS being Closed - , onresize_start: null // CALLBACK when pane STARTS being Resized ***FOR ANY REASON*** - , onresize_end: null // CALLBACK when pane ENDS being Resized ***FOR ANY REASON*** - , onsizecontent_start: null // CALLBACK when sizing of content-element STARTS - , onsizecontent_end: null // CALLBACK when sizing of content-element ENDS - , onswap_start: null // CALLBACK when pane STARTS to Swap - , onswap_end: null // CALLBACK when pane ENDS being Swapped - , ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized - , ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized - } -/* - * PANE-SPECIFIC SETTINGS - * - options listed below MUST be specified per-pane - they CANNOT be set under 'panes' - * - all options under the 'panes' key can also be set specifically for any pane - * - most options under the 'panes' key apply only to 'border-panes' - NOT the the center-pane - */ -, north: { - paneSelector: ".ui-layout-north" - , size: "auto" // eg: "auto", "30%", .30, 200 - , resizerCursor: "n-resize" // custom = url(myCursor.cur) - , customHotkey: "" // EITHER a charCode (43) OR a character ("o") - } -, south: { - paneSelector: ".ui-layout-south" - , size: "auto" - , resizerCursor: "s-resize" - , customHotkey: "" - } -, east: { - paneSelector: ".ui-layout-east" - , size: 200 - , resizerCursor: "e-resize" - , customHotkey: "" - } -, west: { - paneSelector: ".ui-layout-west" - , size: 200 - , resizerCursor: "w-resize" - , customHotkey: "" - } -, center: { - paneSelector: ".ui-layout-center" - , minWidth: 0 - , minHeight: 0 - } -}; - -$.layout.optionsMap = { - // layout/global options - NOT pane-options - layout: ("name,instanceKey,stateManagement,effects,inset,zIndexes,errors," - + "zIndex,scrollToBookmarkOnLoad,showErrorMessages,maskPanesEarly," - + "outset,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay," - + "onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end").split(",") -// borderPanes: [ ALL options that are NOT specified as 'layout' ] - // default.panes options that apply to the center-pane (most options apply _only_ to border-panes) -, center: ("paneClass,contentSelector,contentIgnoreSelector,findNestedContent,applyDemoStyles,triggerEventsOnLoad," - + "showOverflowOnHover,maskContents,maskObjects,liveContentResizing," - + "containerSelector,children,initChildren,resizeChildren,destroyChildren," - + "onresize,onresize_start,onresize_end,onsizecontent,onsizecontent_start,onsizecontent_end").split(",") - // options that MUST be specifically set 'per-pane' - CANNOT set in the panes (defaults) key -, noDefault: ("paneSelector,resizerCursor,customHotkey").split(",") -}; - -/** - * Processes options passed in converts flat-format data into subkey (JSON) format - * In flat-format, subkeys are _currently_ separated with 2 underscores, like north__optName - * Plugins may also call this method so they can transform their own data - * - * @param {!Object} hash Data/options passed by user - may be a single level or nested levels - * @param {boolean=} [addKeys=false] Should the primary layout.options keys be added if they do not exist? - * @return {Object} Returns hash of minWidth & minHeight - */ -$.layout.transformData = function (hash, addKeys) { - var json = addKeys ? { panes: {}, center: {} } : {} // init return object - , branch, optKey, keys, key, val, i, c; - - if (typeof hash !== "object") return json; // no options passed - - // convert all 'flat-keys' to 'sub-key' format - for (optKey in hash) { - branch = json; - val = hash[ optKey ]; - keys = optKey.split("__"); // eg: west__size or north__fxSettings__duration - c = keys.length - 1; - // convert underscore-delimited to subkeys - for (i=0; i <= c; i++) { - key = keys[i]; - if (i === c) { // last key = value - if ($.isPlainObject( val )) - branch[key] = $.layout.transformData( val ); // RECURSE - else - branch[key] = val; - } - else { - if (!branch[key]) - branch[key] = {}; // create the subkey - // recurse to sub-key for next loop - if not done - branch = branch[key]; - } - } - } - return json; -}; - -// INTERNAL CONFIG DATA - DO NOT CHANGE THIS! -$.layout.backwardCompatibility = { - // data used by renameOldOptions() - map: { - // OLD Option Name: NEW Option Name - applyDefaultStyles: "applyDemoStyles" - // CHILD/NESTED LAYOUTS - , childOptions: "children" - , initChildLayout: "initChildren" - , destroyChildLayout: "destroyChildren" - , resizeChildLayout: "resizeChildren" - , resizeNestedLayout: "resizeChildren" - // MISC Options - , resizeWhileDragging: "livePaneResizing" - , resizeContentWhileDragging: "liveContentResizing" - , triggerEventsWhileDragging: "triggerEventsDuringLiveResize" - , maskIframesOnResize: "maskContents" - // STATE MANAGEMENT - , useStateCookie: "stateManagement.enabled" - , "cookie.autoLoad": "stateManagement.autoLoad" - , "cookie.autoSave": "stateManagement.autoSave" - , "cookie.keys": "stateManagement.stateKeys" - , "cookie.name": "stateManagement.cookie.name" - , "cookie.domain": "stateManagement.cookie.domain" - , "cookie.path": "stateManagement.cookie.path" - , "cookie.expires": "stateManagement.cookie.expires" - , "cookie.secure": "stateManagement.cookie.secure" - // OLD Language options - , noRoomToOpenTip: "tips.noRoomToOpen" - , togglerTip_open: "tips.Close" // open = Close - , togglerTip_closed: "tips.Open" // closed = Open - , resizerTip: "tips.Resize" - , sliderTip: "tips.Slide" - } - -/** -* @param {Object} opts -*/ -, renameOptions: function (opts) { - var map = $.layout.backwardCompatibility.map - , oldData, newData, value - ; - for (var itemPath in map) { - oldData = getBranch( itemPath ); - value = oldData.branch[ oldData.key ]; - if (value !== undefined) { - newData = getBranch( map[itemPath], true ); - newData.branch[ newData.key ] = value; - delete oldData.branch[ oldData.key ]; - } - } - - /** - * @param {string} path - * @param {boolean=} [create=false] Create path if does not exist - */ - function getBranch (path, create) { - var a = path.split(".") // split keys into array - , c = a.length - 1 - , D = { branch: opts, key: a[c] } // init branch at top & set key (last item) - , i = 0, k, undef; - for (; i 0) { - if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) { - $E.show().data('autoHidden', false); - if (!browser.mozilla) // FireFox refreshes iframes - IE does not - // make hidden, then visible to 'refresh' display after animation - $E.css(_c.hidden).css(_c.visible); - } - } - else if (autoHide && !$E.data('autoHidden')) - $E.hide().data('autoHidden', true); - } - - /** - * @param {(string|!Object)} el - * @param {number=} outerHeight - * @param {boolean=} [autoHide=false] - */ -, setOuterHeight = function (el, outerHeight, autoHide) { - var $E = el, h; - if (isStr(el)) $E = $Ps[el]; // west - else if (!el.jquery) $E = $(el); - h = cssH($E, outerHeight); - $E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent - if (h > 0 && $E.innerWidth() > 0) { - if (autoHide && $E.data('autoHidden')) { - $E.show().data('autoHidden', false); - if (!browser.mozilla) // FireFox refreshes iframes - IE does not - $E.css(_c.hidden).css(_c.visible); - } - } - else if (autoHide && !$E.data('autoHidden')) - $E.hide().data('autoHidden', true); - } - - - /** - * Converts any 'size' params to a pixel/integer size, if not already - * If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated - * - /** - * @param {string} pane - * @param {(string|number)=} size - * @param {string=} [dir] - * @return {number} - */ -, _parseSize = function (pane, size, dir) { - if (!dir) dir = _c[pane].dir; - - if (isStr(size) && size.match(/%/)) - size = (size === '100%') ? -1 : parseInt(size, 10) / 100; // convert % to decimal - - if (size === 0) - return 0; - else if (size >= 1) - return parseInt(size, 10); - - var o = options, avail = 0; - if (dir=="horz") // north or south or center.minHeight - avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0); - else if (dir=="vert") // east or west or center.minWidth - avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0); - - if (size === -1) // -1 == 100% - return avail; - else if (size > 0) // percentage, eg: .25 - return round(avail * size); - else if (pane=="center") - return 0; - else { // size < 0 || size=='auto' || size==Missing || size==Invalid - // auto-size the pane - var dim = (dir === "horz" ? "height" : "width") - , $P = $Ps[pane] - , $C = dim === 'height' ? $Cs[pane] : false - , vis = $.layout.showInvisibly($P) // show pane invisibly if hidden - , szP = $P.css(dim) // SAVE current pane size - , szC = $C ? $C.css(dim) : 0 // SAVE current content size - ; - $P.css(dim, "auto"); - if ($C) $C.css(dim, "auto"); - size = (dim === "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE - $P.css(dim, szP).css(vis); // RESET size & visibility - if ($C) $C.css(dim, szC); - return size; - } - } - - /** - * Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added - * - * @param {(string|!Object)} pane - * @param {boolean=} [inclSpace=false] - * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes - */ -, 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; - } - - /** - * Calculate min/max pane dimensions and limits for resizing - * - * @param {string} pane - * @param {boolean=} [slide=false] - */ -, setSizeLimits = function (pane, slide) { - if (!isInitialized()) return; - var - o = options[pane] - , s = state[pane] - , c = _c[pane] - , dir = c.dir - , type = c.sizeType.toLowerCase() - , isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param - , $P = $Ps[pane] - , paneSpacing = o.spacing_open - // measure the pane on the *opposite side* from this pane - , altPane = _c.oppositeEdge[pane] - , altS = state[altPane] - , $altP = $Ps[altPane] - , altPaneSize = (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth())) - , altPaneSpacing = ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0) - // limitSize prevents this pane from 'overlapping' opposite pane - , containerSize = (dir=="horz" ? sC.innerHeight : sC.innerWidth) - , minCenterDims = cssMinDims("center") - , minCenterSize = dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth) - // if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them - , limitSize = (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing))) - , minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize ) - , maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize ) - , r = s.resizerPosition = {} // used to set resizing limits - , top = sC.inset.top - , left = sC.inset.left - , W = sC.innerWidth - , H = sC.innerHeight - , rW = o.spacing_open // subtract resizer-width to get top/left position for south/east - ; - switch (pane) { - case "north": r.min = top + minSize; - r.max = top + maxSize; - break; - case "west": r.min = left + minSize; - r.max = left + maxSize; - break; - case "south": r.min = top + H - maxSize - rW; - r.max = top + H - minSize - rW; - break; - case "east": r.min = left + W - maxSize - rW; - r.max = left + W - minSize - rW; - break; - }; - } - - /** - * Returns data for setting the size/position of center pane. Also used to set Height for east/west panes - * - * @return JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height - */ -, calcNewCenterPaneDims = function () { - var d = { - top: getPaneSize("north", true) // true = include 'spacing' value for pane - , bottom: getPaneSize("south", true) - , left: getPaneSize("west", true) - , right: getPaneSize("east", true) - , width: 0 - , height: 0 - }; - - // NOTE: sC = state.container - // calc center-pane outer dimensions - d.width = sC.innerWidth - d.left - d.right; // outerWidth - d.height = sC.innerHeight - d.bottom - d.top; // outerHeight - // add the 'container border/padding' to get final positions relative to the container - d.top += sC.inset.top; - d.bottom += sC.inset.bottom; - d.left += sC.inset.left; - d.right += sC.inset.right; - - return d; - } - - - /** - * @param {!Object} el - * @param {boolean=} [allStates=false] - */ -, getHoverClasses = function (el, allStates) { - var - $El = $(el) - , type = $El.data("layoutRole") - , pane = $El.data("layoutEdge") - , o = options[pane] - , root = o[type +"Class"] - , _pane = "-"+ pane // eg: "-west" - , _open = "-open" - , _closed = "-closed" - , _slide = "-sliding" - , _hover = "-hover " // NOTE the trailing space - , _state = $El.hasClass(root+_closed) ? _closed : _open - , _alt = _state === _closed ? _open : _closed - , classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover) - ; - if (allStates) // when 'removing' classes, also remove alternate-state classes - classes += (root+_alt+_hover) + (root+_pane+_alt+_hover); - - if (type=="resizer" && $El.hasClass(root+_slide)) - classes += (root+_slide+_hover) + (root+_pane+_slide+_hover); - - return $.trim(classes); - } -, addHover = function (evt, el) { - var $E = $(el || this); - if (evt && $E.data("layoutRole") === "toggler") - evt.stopPropagation(); // prevent triggering 'slide' on Resizer-bar - $E.addClass( getHoverClasses($E) ); - } -, removeHover = function (evt, el) { - var $E = $(el || this); - $E.removeClass( getHoverClasses($E, true) ); - } - -, onResizerEnter = function (evt) { // ALSO called by toggler.mouseenter - var pane = $(this).data("layoutEdge") - , s = state[pane] - , $d = $(document) - ; - // ignore closed-panes and mouse moving back & forth over resizer! - // also ignore if ANY pane is currently resizing - if ( s.isResizing || state.paneResizing ) return; - - if (options.maskPanesEarly) - showMasks( pane, { resizing: true }); - } -, onResizerLeave = function (evt, el) { - var e = el || this // el is only passed when called by the timer - , pane = $(e).data("layoutEdge") - , name = pane +"ResizerLeave" - , $d = $(document) - ; - timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set - timer.clear(name); // cancel enableSelection timer - may re/set below - // this method calls itself on a timer because it needs to allow - // enough time for dragging to kick-in and set the isResizing flag - // dragging has a 100ms delay set, so this delay must be >100 - if (!el) // 1st call - mouseleave event - timer.set(name, function(){ onResizerLeave(evt, e); }, 200); - // if user is resizing, dragStop will reset everything, so skip it here - else if (options.maskPanesEarly && !state.paneResizing) // 2nd call - by timer - hideMasks(); - } - -/* - * ########################### - * INITIALIZATION METHODS - * ########################### - */ - - /** - * Initialize the layout - called automatically whenever an instance of layout is created - * - * @see none - triggered onInit - * @return mixed true = fully initialized | false = panes not initialized (yet) | 'cancel' = abort - */ -, _create = function () { - // initialize config/options - initOptions(); - var o = options - , s = state; - - // TEMP state so isInitialized returns true during init process - s.creatingLayout = true; - - // init plugins for this layout, if there are any (eg: stateManagement) - runPluginCallbacks( Instance, $.layout.onCreate ); - - // options & state have been initialized, so now run beforeLoad callback - // onload will CANCEL layout creation if it returns false - if (false === _runCallbacks("onload_start")) - return 'cancel'; - - // initialize the container element - _initContainer(); - - // bind hotkey function - keyDown - if required - initHotkeys(); - - // bind window.onunload - $(window).bind("unload."+ sID, unload); - - // init plugins for this layout, if there are any (eg: customButtons) - runPluginCallbacks( Instance, $.layout.onLoad ); - - // if layout elements are hidden, then layout WILL NOT complete initialization! - // initLayoutElements will set initialized=true and run the onload callback IF successful - if (o.initPanes) _initLayoutElements(); - - delete s.creatingLayout; - - return state.initialized; - } - - /** - * Initialize the layout IF not already - * - * @see All methods in Instance run this test - * @return boolean true = layoutElements have been initialized | false = panes are not initialized (yet) - */ -, isInitialized = function () { - if (state.initialized || state.creatingLayout) return true; // already initialized - else return _initLayoutElements(); // try to init panes NOW - } - - /** - * Initialize the layout - called automatically whenever an instance of layout is created - * - * @see _create() & isInitialized - * @param {boolean=} [retry=false] // indicates this is a 2nd try - * @return An object pointer to the instance created - */ -, _initLayoutElements = function (retry) { - // initialize config/options - var o = options; - // CANNOT init panes inside a hidden container! - if (!$N.is(":visible")) { - // handle Chrome bug where popup window 'has no height' - // if layout is BODY element, try again in 50ms - // SEE: http://layout.jquery-dev.com/samples/test_popup_window.html - if ( !retry && browser.webkit && $N[0].tagName === "BODY" ) - setTimeout(function(){ _initLayoutElements(true); }, 50); - return false; - } - - // a center pane is required, so make sure it exists - if (!getPane("center").length) { - return _log( o.errors.centerPaneMissing ); - } - - // TEMP state so isInitialized returns true during init process - state.creatingLayout = true; - - // update Container dims - $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values - - // initialize all layout elements - initPanes(); // size & position panes - calls initHandles() - which calls initResizable() - - if (o.scrollToBookmarkOnLoad) { - var l = self.location; - if (l.hash) l.replace( l.hash ); // scrollTo Bookmark - } - - // check to see if this layout 'nested' inside a pane - if (Instance.hasParentLayout) - o.resizeWithWindow = false; - // bind resizeAll() for 'this layout instance' to window.resize event - else if (o.resizeWithWindow) - $(window).bind("resize."+ sID, windowResize); - - delete state.creatingLayout; - state.initialized = true; - - // init plugins for this layout, if there are any - runPluginCallbacks( Instance, $.layout.onReady ); - - // now run the onload callback, if exists - _runCallbacks("onload_end"); - - return true; // elements initialized successfully - } - - /** - * Initialize nested layouts for a specific pane - can optionally pass layout-options - * - * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west - * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].children - * @return An object pointer to the layout instance created - or null - */ -, createChildren = function (evt_or_pane, opts) { - var pane = evtPane.call(this, evt_or_pane) - , $P = $Ps[pane] - ; - if (!$P) return; - var $C = $Cs[pane] - , s = state[pane] - , o = options[pane] - , sm = options.stateManagement || {} - , cos = opts ? (o.children = opts) : o.children - ; - if ( $.isPlainObject( cos ) ) - cos = [ cos ]; // convert a hash to a 1-elem array - else if (!cos || !$.isArray( cos )) - return; - - $.each( cos, function (idx, co) { - if ( !$.isPlainObject( co ) ) return; - - // determine which element is supposed to be the 'child container' - // if pane has a 'containerSelector' OR a 'content-div', use those instead of the pane - var $containers = co.containerSelector ? $P.find( co.containerSelector ) : ($C || $P); - - $containers.each(function(){ - var $cont = $(this) - , child = $cont.data("layout") // see if a child-layout ALREADY exists on this element - ; - // if no layout exists, but children are set, try to create the layout now - if (!child) { - // TODO: see about moving this to the stateManagement plugin, as a method - // set a unique child-instance key for this layout, if not already set - setInstanceKey({ container: $cont, options: co }, s ); - // If THIS layout has a hash in stateManagement.autoLoad, - // then see if it also contains state-data for this child-layout - // If so, copy the stateData to child.options.stateManagement.autoLoad - if ( sm.includeChildren && state.stateData[pane] ) { - // THIS layout's state was cached when its state was loaded - var paneChildren = state.stateData[pane].children || {} - , childState = paneChildren[ co.instanceKey ] - , co_sm = co.stateManagement || (co.stateManagement = { autoLoad: true }) - ; - // COPY the stateData into the autoLoad key - if ( co_sm.autoLoad === true && childState ) { - co_sm.autoSave = false; // disable autoSave because saving handled by parent-layout - co_sm.includeChildren = true; // cascade option - FOR NOW - co_sm.autoLoad = $.extend(true, {}, childState); // COPY the state-hash - } - } - - // create the layout - child = $cont.layout( co ); - - // if successful, update data - if (child) { - // add the child and update all layout-pointers - // MAY have already been done by child-layout calling parent.refreshChildren() - refreshChildren( pane, child ); - } - } - }); - }); - } - -, setInstanceKey = function (child, parentPaneState) { - // create a named key for use in state and instance branches - var $c = child.container - , o = child.options - , sm = o.stateManagement - , key = o.instanceKey || $c.data("layoutInstanceKey") - ; - if (!key) key = (sm && sm.cookie ? sm.cookie.name : '') || o.name; // look for a name/key - if (!key) key = "layout"+ (++parentPaneState.childIdx); // if no name/key found, generate one - else key = key.replace(/[^\w-]/gi, '_').replace(/_{2,}/g, '_'); // ensure is valid as a hash key - o.instanceKey = key; - $c.data("layoutInstanceKey", key); // useful if layout is destroyed and then recreated - return key; - } - - /** - * @param {string} pane The pane being opened, ie: north, south, east, or west - * @param {Object=} newChild New child-layout Instance to add to this pane - */ -, refreshChildren = function (pane, newChild) { - var $P = $Ps[pane] - , pC = children[pane] - , s = state[pane] - , o - ; - // check for destroy()ed layouts and update the child pointers & arrays - if ($.isPlainObject( pC )) { - $.each( pC, function (key, child) { - if (child.destroyed) delete pC[key] - }); - // if no more children, remove the children hash - if ($.isEmptyObject( pC )) - pC = children[pane] = null; // clear children hash - } - - // see if there is a directly-nested layout inside this pane - // if there is, then there can be only ONE child-layout, so check that... - if (!newChild && !pC) { - newChild = $P.data("layout"); - } - - // if a newChild instance was passed, add it to children[pane] - if (newChild) { - // update child.state - newChild.hasParentLayout = true; // set parent-flag in child - // instanceKey is a key-name used in both state and children - o = newChild.options; - // set a unique child-instance key for this layout, if not already set - setInstanceKey( newChild, s ); - // add pointer to pane.children hash - if (!pC) pC = children[pane] = {}; // create an empty children hash - pC[ o.instanceKey ] = newChild.container.data("layout"); // add childLayout instance - } - - // ALWAYS refresh the pane.children alias, even if null - Instance[pane].children = children[pane]; - - // if newChild was NOT passed - see if there is a child layout NOW - if (!newChild) { - createChildren(pane); // MAY create a child and re-call this method - } - } - -, windowResize = function () { - var o = options - , delay = Number(o.resizeWithWindowDelay); - if (delay < 10) delay = 100; // MUST have a delay! - // resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway - timer.clear("winResize"); // if already running - timer.set("winResize", function(){ - timer.clear("winResize"); - timer.clear("winResizeRepeater"); - var dims = elDims( $N, o.inset ); - // only trigger resizeAll() if container has changed size - if (dims.innerWidth !== sC.innerWidth || dims.innerHeight !== sC.innerHeight) - resizeAll(); - }, delay); - // ALSO set fixed-delay timer, if not already running - if (!timer.data["winResizeRepeater"]) setWindowResizeRepeater(); - } - -, setWindowResizeRepeater = function () { - var delay = Number(options.resizeWithWindowMaxDelay); - if (delay > 0) - timer.set("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay); - } - -, unload = function () { - var o = options; - - _runCallbacks("onunload_start"); - - // trigger plugin callabacks for this layout (eg: stateManagement) - runPluginCallbacks( Instance, $.layout.onUnload ); - - _runCallbacks("onunload_end"); - } - - /** - * Validate and initialize container CSS and events - * - * @see _create() - */ -, _initContainer = function () { - var - N = $N[0] - , $H = $("html") - , tag = sC.tagName = N.tagName - , id = sC.id = N.id - , cls = sC.className = N.className - , o = options - , name = o.name - , props = "position,margin,padding,border" - , css = "layoutCSS" - , CSS = {} - , hid = "hidden" // used A LOT! - // see if this container is a 'pane' inside an outer-layout - , parent = $N.data("parentLayout") // parent-layout Instance - , pane = $N.data("layoutEdge") // pane-name in parent-layout - , isChild = parent && pane - , num = $.layout.cssNum - , $parent, n - ; - // sC = state.container - sC.selector = $N.selector.split(".slice")[0]; - sC.ref = (o.name ? o.name +' layout / ' : '') + tag + (id ? "#"+id : cls ? '.['+cls+']' : ''); // used in messages - sC.isBody = (tag === "BODY"); - - // try to find a parent-layout - if (!isChild && !sC.isBody) { - $parent = $N.closest("."+ $.layout.defaults.panes.paneClass); - parent = $parent.data("parentLayout"); - pane = $parent.data("layoutEdge"); - isChild = parent && pane; - } - - $N .data({ - layout: Instance - , layoutContainer: sID // FLAG to indicate this is a layout-container - contains unique internal ID - }) - .addClass(o.containerClass) - ; - var layoutMethods = { - destroy: '' - , initPanes: '' - , resizeAll: 'resizeAll' - , resize: 'resizeAll' - }; - // loop hash and bind all methods - include layoutID namespacing - for (name in layoutMethods) { - $N.bind("layout"+ name.toLowerCase() +"."+ sID, Instance[ layoutMethods[name] || name ]); - } - - // if this container is another layout's 'pane', then set child/parent pointers - if (isChild) { - // update parent flag - Instance.hasParentLayout = true; - // set pointers to THIS child-layout (Instance) in parent-layout - parent.refreshChildren( pane, Instance ); - } - - // SAVE original container CSS for use in destroy() - if (!$N.data(css)) { - // handle props like overflow different for BODY & HTML - has 'system default' values - if (sC.isBody) { - // SAVE CSS - $N.data(css, $.extend( styles($N, props), { - height: $N.css("height") - , overflow: $N.css("overflow") - , overflowX: $N.css("overflowX") - , overflowY: $N.css("overflowY") - })); - // ALSO SAVE CSS - $H.data(css, $.extend( styles($H, 'padding'), { - height: "auto" // FF would return a fixed px-size! - , overflow: $H.css("overflow") - , overflowX: $H.css("overflowX") - , overflowY: $H.css("overflowY") - })); - } - else // handle props normally for non-body elements - $N.data(css, styles($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY") ); - } - - try { - // common container CSS - CSS = { - overflow: hid - , overflowX: hid - , overflowY: hid - }; - $N.css( CSS ); - - if (o.inset && !$.isPlainObject(o.inset)) { - // can specify a single number for equal outset all-around - n = parseInt(o.inset, 10) || 0 - o.inset = { - top: n - , bottom: n - , left: n - , right: n - }; - } - - // format html & body if this is a full page layout - if (sC.isBody) { - // if HTML has padding, use this as an outer-spacing around BODY - if (!o.outset) { - // use padding from parent-elem (HTML) as outset - o.outset = { - top: num($H, "paddingTop") - , bottom: num($H, "paddingBottom") - , left: num($H, "paddingLeft") - , right: num($H, "paddingRight") - }; - } - else if (!$.isPlainObject(o.outset)) { - // can specify a single number for equal outset all-around - n = parseInt(o.outset, 10) || 0 - o.outset = { - top: n - , bottom: n - , left: n - , right: n - }; - } - // HTML - $H.css( CSS ).css({ - height: "100%" - , border: "none" // no border or padding allowed when using height = 100% - , padding: 0 // ditto - , margin: 0 - }); - // BODY - if (browser.isIE6) { - // IE6 CANNOT use the trick of setting absolute positioning on all 4 sides - must have 'height' - $N.css({ - width: "100%" - , height: "100%" - , border: "none" // no border or padding allowed when using height = 100% - , padding: 0 // ditto - , margin: 0 - , position: "relative" - }); - // convert body padding to an inset option - the border cannot be measured in IE6! - if (!o.inset) o.inset = elDims( $N ).inset; - } - else { // use absolute positioning for BODY to allow borders & padding without overflow - $N.css({ - width: "auto" - , height: "auto" - , margin: 0 - , position: "absolute" // allows for border and padding on BODY - }); - // apply edge-positioning created above - $N.css( o.outset ); - } - // set current layout-container dimensions - $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values - } - else { - // container MUST have 'position' - var p = $N.css("position"); - if (!p || !p.match(/(fixed|absolute|relative)/)) - $N.css("position","relative"); - - // set current layout-container dimensions - if ( $N.is(":visible") ) { - $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT change insetX (padding) values - if (sC.innerHeight < 1) // container has no 'height' - warn developer - _log( o.errors.noContainerHeight.replace(/CONTAINER/, sC.ref) ); - } - } - - // if container has min-width/height, then enable scrollbar(s) - if ( num($N, "minWidth") ) $N.parent().css("overflowX","auto"); - if ( num($N, "minHeight") ) $N.parent().css("overflowY","auto"); - - } catch (ex) {} - } - - /** - * Bind layout hotkeys - if options enabled - * - * @see _create() and addPane() - * @param {string=} [panes=""] The edge(s) to process - */ -, initHotkeys = function (panes) { - panes = panes ? panes.split(",") : _c.borderPanes; - // bind keyDown to capture hotkeys, if option enabled for ANY pane - $.each(panes, function (i, pane) { - var o = options[pane]; - if (o.enableCursorHotkey || o.customHotkey) { - $(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE - return false; // BREAK - binding was done - } - }); - } - - /** - * Build final OPTIONS data - * - * @see _create() - */ -, initOptions = function () { - var data, d, pane, key, val, i, c, o; - - // reprocess user's layout-options to have correct options sub-key structure - opts = $.layout.transformData( opts, true ); // panes = default subkey - - // auto-rename old options for backward compatibility - opts = $.layout.backwardCompatibility.renameAllOptions( opts ); - - // if user-options has 'panes' key (pane-defaults), clean it... - if (!$.isEmptyObject(opts.panes)) { - // REMOVE any pane-defaults that MUST be set per-pane - data = $.layout.optionsMap.noDefault; - for (i=0, c=data.length; i 0) { - z.pane_normal = zo; - z.content_mask = max(zo+1, z.content_mask); // MIN = +1 - z.resizer_normal = max(zo+2, z.resizer_normal); // MIN = +2 - } - - // DELETE 'panes' key now that we are done - values were copied to EACH pane - delete options.panes; - - - function createFxOptions ( pane ) { - var o = options[pane] - , d = options.panes; - // ensure fxSettings key to avoid errors - if (!o.fxSettings) o.fxSettings = {}; - if (!d.fxSettings) d.fxSettings = {}; - - $.each(["_open","_close","_size"], function (i,n) { - var - sName = "fxName"+ n - , sSpeed = "fxSpeed"+ n - , sSettings = "fxSettings"+ n - // recalculate fxName according to specificity rules - , fxName = o[sName] = - o[sName] // options.west.fxName_open - || d[sName] // options.panes.fxName_open - || o.fxName // options.west.fxName - || d.fxName // options.panes.fxName - || "none" // MEANS $.layout.defaults.panes.fxName == "" || false || null || 0 - , fxExists = $.effects && ($.effects[fxName] || ($.effects.effect && $.effects.effect[fxName])) - ; - // validate fxName to ensure is valid effect - MUST have effect-config data in options.effects - if (fxName === "none" || !options.effects[fxName] || !fxExists) - fxName = o[sName] = "none"; // effect not loaded OR unrecognized fxName - - // set vars for effects subkeys to simplify logic - var fx = options.effects[fxName] || {} // effects.slide - , fx_all = fx.all || null // effects.slide.all - , fx_pane = fx[pane] || null // effects.slide.west - ; - // create fxSpeed[_open|_close|_size] - o[sSpeed] = - o[sSpeed] // options.west.fxSpeed_open - || d[sSpeed] // options.west.fxSpeed_open - || o.fxSpeed // options.west.fxSpeed - || d.fxSpeed // options.panes.fxSpeed - || null // DEFAULT - let fxSetting.duration control speed - ; - // create fxSettings[_open|_close|_size] - o[sSettings] = $.extend( - true - , {} - , fx_all // effects.slide.all - , fx_pane // effects.slide.west - , d.fxSettings // options.panes.fxSettings - , o.fxSettings // options.west.fxSettings - , d[sSettings] // options.panes.fxSettings_open - , o[sSettings] // options.west.fxSettings_open - ); - }); - - // DONE creating action-specific-settings for this pane, - // so DELETE generic options - are no longer meaningful - delete o.fxName; - delete o.fxSpeed; - delete o.fxSettings; - } - } - - /** - * Initialize module objects, styling, size and position for all panes - * - * @see _initElements() - * @param {string} pane The pane to process - */ -, getPane = function (pane) { - var sel = options[pane].paneSelector - if (sel.substr(0,1)==="#") // ID selector - // NOTE: elements selected 'by ID' DO NOT have to be 'children' - return $N.find(sel).eq(0); - else { // class or other selector - var $P = $N.children(sel).eq(0); - // look for the pane nested inside a 'form' element - return $P.length ? $P : $N.children("form:first").children(sel).eq(0); - } - } - - /** - * @param {Object=} evt - */ -, initPanes = function (evt) { - // stopPropagation if called by trigger("layoutinitpanes") - use evtPane utility - evtPane(evt); - - // NOTE: do north & south FIRST so we can measure their height - do center LAST - $.each(_c.allPanes, function (idx, pane) { - addPane( pane, true ); - }); - - // init the pane-handles NOW in case we have to hide or close the pane below - initHandles(); - - // now that all panes have been initialized and initially-sized, - // make sure there is really enough space available for each pane - $.each(_c.borderPanes, function (i, pane) { - if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN - setSizeLimits(pane); - makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit() - } - }); - // size center-pane AGAIN in case we 'closed' a border-pane in loop above - sizeMidPanes("center"); - - // Chrome/Webkit sometimes fires callbacks BEFORE it completes resizing! - // Before RC30.3, there was a 10ms delay here, but that caused layout - // to load asynchrously, which is BAD, so try skipping delay for now - - // process pane contents and callbacks, and init/resize child-layout if exists - $.each(_c.allPanes, function (idx, pane) { - afterInitPane(pane); - }); - } - - /** - * Add a pane to the layout - subroutine of initPanes() - * - * @see initPanes() - * @param {string} pane The pane to process - * @param {boolean=} [force=false] Size content after init - */ -, addPane = function (pane, force) { - if ( !force && !isInitialized() ) return; - var - o = options[pane] - , s = state[pane] - , c = _c[pane] - , dir = c.dir - , fx = s.fx - , spacing = o.spacing_open || 0 - , isCenter = (pane === "center") - , CSS = {} - , $P = $Ps[pane] - , size, minSize, maxSize, child - ; - // if pane-pointer already exists, remove the old one first - if ($P) - removePane( pane, false, true, false ); - else - $Cs[pane] = false; // init - - $P = $Ps[pane] = getPane(pane); - if (!$P.length) { - $Ps[pane] = false; // logic - return; - } - - // SAVE original Pane CSS - if (!$P.data("layoutCSS")) { - var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border"; - $P.data("layoutCSS", styles($P, props)); - } - - // create alias for pane data in Instance - initHandles will add more - Instance[pane] = { - name: pane - , pane: $Ps[pane] - , content: $Cs[pane] - , options: options[pane] - , state: state[pane] - , children: children[pane] - }; - - // add classes, attributes & events - $P .data({ - parentLayout: Instance // pointer to Layout Instance - , layoutPane: Instance[pane] // NEW pointer to pane-alias-object - , layoutEdge: pane - , layoutRole: "pane" - }) - .css(c.cssReq).css("zIndex", options.zIndexes.pane_normal) - .css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles - .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector' - .bind("mouseenter."+ sID, addHover ) - .bind("mouseleave."+ sID, removeHover ) - ; - var paneMethods = { - hide: '' - , show: '' - , toggle: '' - , close: '' - , open: '' - , slideOpen: '' - , slideClose: '' - , slideToggle: '' - , size: 'sizePane' - , sizePane: 'sizePane' - , sizeContent: '' - , sizeHandles: '' - , enableClosable: '' - , disableClosable: '' - , enableSlideable: '' - , disableSlideable: '' - , enableResizable: '' - , disableResizable: '' - , swapPanes: 'swapPanes' - , swap: 'swapPanes' - , move: 'swapPanes' - , removePane: 'removePane' - , remove: 'removePane' - , createChildren: '' - , resizeChildren: '' - , resizeAll: 'resizeAll' - , resizeLayout: 'resizeAll' - } - , name; - // loop hash and bind all methods - include layoutID namespacing - for (name in paneMethods) { - $P.bind("layoutpane"+ name.toLowerCase() +"."+ sID, Instance[ paneMethods[name] || name ]); - } - - // see if this pane has a 'scrolling-content element' - initContent(pane, false); // false = do NOT sizeContent() - called later - - if (!isCenter) { - // call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden) - // if o.size is auto or not valid, then MEASURE the pane and use that as its 'size' - size = s.size = _parseSize(pane, o.size); - minSize = _parseSize(pane,o.minSize) || 1; - maxSize = _parseSize(pane,o.maxSize) || 100000; - if (size > 0) size = max(min(size, maxSize), minSize); - s.autoResize = o.autoResize; // used with percentage sizes - - // state for border-panes - 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! - - // array for 'pin buttons' whose classNames are auto-updated on pane-open/-close - if (!s.pins) s.pins = []; - } - // states common to ALL panes - s.tagName = $P[0].tagName; - s.edge = pane; // useful if pane is (or about to be) 'swapped' - easy find out where it is (or is going) - s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically - s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic - - // init pane positioning - setPanePosition( pane ); - - // if pane is not visible, - if (dir === "horz") // north or south pane - CSS.height = cssH($P, size); - else if (dir === "vert") // east or west pane - CSS.width = cssW($P, size); - //else if (isCenter) {} - - $P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes - if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback - - // if manually adding a pane AFTER layout initialization, then... - if (state.initialized) { - initHandles( pane ); - initHotkeys( pane ); - } - - // close or hide the pane if specified in settings - if (o.initClosed && o.closable && !o.initHidden) - close(pane, true, true); // true, true = force, noAnimation - else if (o.initHidden || o.initClosed) - hide(pane); // will be completely invisible - no resizer or spacing - else if (!s.noRoom) - // make the pane visible - in case was initially hidden - $P.css("display","block"); - // ELSE setAsOpen() - called later by initHandles() - - // RESET visibility now - pane will appear IF display:block - $P.css("visibility","visible"); - - // check option for auto-handling of pop-ups & drop-downs - if (o.showOverflowOnHover) - $P.hover( allowOverflow, resetOverflow ); - - // if manually adding a pane AFTER layout initialization, then... - if (state.initialized) { - afterInitPane( pane ); - } - } - -, afterInitPane = function (pane) { - var $P = $Ps[pane] - , s = state[pane] - , o = options[pane] - ; - if (!$P) return; - - // see if there is a directly-nested layout inside this pane - if ($P.data("layout")) - refreshChildren( pane, $P.data("layout") ); - - // process pane contents and callbacks, and init/resize child-layout if exists - if (s.isVisible) { // pane is OPEN - if (state.initialized) // this pane was added AFTER layout was created - resizeAll(); // will also sizeContent - else - sizeContent(pane); - - if (o.triggerEventsOnLoad) - _runCallbacks("onresize_end", pane); - else // automatic if onresize called, otherwise call it specifically - // resize child - IF inner-layout already exists (created before this layout) - resizeChildren(pane, true); // a previously existing childLayout - } - - // init childLayouts - even if pane is not visible - if (o.initChildren && o.children) - createChildren(pane); - } - - /** - * @param {string=} panes The pane(s) to process - */ -, setPanePosition = function (panes) { - panes = panes ? panes.split(",") : _c.borderPanes; - - // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV - $.each(panes, function (i, pane) { - var $P = $Ps[pane] - , $R = $Rs[pane] - , o = options[pane] - , s = state[pane] - , side = _c[pane].side - , CSS = {} - ; - if (!$P) return; // pane does not exist - skip - - // set css-position to account for container borders & padding - switch (pane) { - case "north": CSS.top = sC.inset.top; - CSS.left = sC.inset.left; - CSS.right = sC.inset.right; - break; - case "south": CSS.bottom = sC.inset.bottom; - CSS.left = sC.inset.left; - CSS.right = sC.inset.right; - break; - case "west": CSS.left = sC.inset.left; // top, bottom & height set by sizeMidPanes() - break; - case "east": CSS.right = sC.inset.right; // ditto - break; - case "center": // top, left, width & height set by sizeMidPanes() - } - // apply position - $P.css(CSS); - - // update resizer position - if ($R && s.isClosed) - $R.css(side, sC.inset[side]); - else if ($R && !s.isHidden) - $R.css(side, sC.inset[side] + getPaneSize(pane)); - }); - } - - /** - * Initialize module objects, styling, size and position for all resize bars and toggler buttons - * - * @see _create() - * @param {string=} [panes=""] The edge(s) to process - */ -, initHandles = function (panes) { - panes = panes ? panes.split(",") : _c.borderPanes; - - // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV - $.each(panes, function (i, pane) { - var $P = $Ps[pane]; - $Rs[pane] = false; // INIT - $Ts[pane] = false; - if (!$P) return; // pane does not exist - skip - - var o = options[pane] - , s = state[pane] - , c = _c[pane] - , paneId = o.paneSelector.substr(0,1) === "#" ? o.paneSelector.substr(1) : "" - , rClass = o.resizerClass - , tClass = o.togglerClass - , spacing = (s.isVisible ? o.spacing_open : o.spacing_closed) - , _pane = "-"+ pane // used for classNames - , _state = (s.isVisible ? "-open" : "-closed") // used for classNames - , I = Instance[pane] - // INIT RESIZER BAR - , $R = I.resizer = $Rs[pane] = $("
      ") - // INIT TOGGLER BUTTON - , $T = I.toggler = (o.closable ? $Ts[pane] = $("
      ") : false) - ; - - //if (s.isVisible && o.resizable) ... handled by initResizable - if (!s.isVisible && o.slidable) - $R.attr("title", o.tips.Slide).css("cursor", o.sliderCursor); - - $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer" - .attr("id", paneId ? paneId +"-resizer" : "" ) - .data({ - parentLayout: Instance - , layoutPane: Instance[pane] // NEW pointer to pane-alias-object - , layoutEdge: pane - , layoutRole: "resizer" - }) - .css(_c.resizers.cssReq).css("zIndex", options.zIndexes.resizer_normal) - .css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles - .addClass(rClass +" "+ rClass+_pane) - .hover(addHover, removeHover) // ALWAYS add hover-classes, even if resizing is not enabled - handle with CSS instead - .hover(onResizerEnter, onResizerLeave) // ALWAYS NEED resizer.mouseleave to balance toggler.mouseenter - .mousedown($.layout.disableTextSelection) // prevent text-selection OUTSIDE resizer - .mouseup($.layout.enableTextSelection) // not really necessary, but just in case - .appendTo($N) // append DIV to container - ; - if ($.fn.disableSelection) - $R.disableSelection(); // prevent text-selection INSIDE resizer - if (o.resizerDblClickToggle) - $R.bind("dblclick."+ sID, toggle ); - - if ($T) { - $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler" - .attr("id", paneId ? paneId +"-toggler" : "" ) - .data({ - parentLayout: Instance - , layoutPane: Instance[pane] // NEW pointer to pane-alias-object - , layoutEdge: pane - , layoutRole: "toggler" - }) - .css(_c.togglers.cssReq) // add base/required styles - .css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles - .addClass(tClass +" "+ tClass+_pane) - .hover(addHover, removeHover) // ALWAYS add hover-classes, even if toggling is not enabled - handle with CSS instead - .bind("mouseenter", onResizerEnter) // NEED toggler.mouseenter because mouseenter MAY NOT fire on resizer - .appendTo($R) // append SPAN to resizer DIV - ; - // ADD INNER-SPANS TO TOGGLER - if (o.togglerContent_open) // ui-layout-open - $(""+ o.togglerContent_open +"") - .data({ - layoutEdge: pane - , layoutRole: "togglerContent" - }) - .data("layoutRole", "togglerContent") - .data("layoutEdge", pane) -// .addClass("content content-open") - .addClass("ui-content content-open") // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 - .css("display","none") - .appendTo( $T ) - //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-open instead! - ; - if (o.togglerContent_closed) // ui-layout-closed - $(""+ o.togglerContent_closed +"") - .data({ - layoutEdge: pane - , layoutRole: "togglerContent" - }) -// .addClass("content content-closed") - .addClass("ui-content content-closed") // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 - .css("display","none") - .appendTo( $T ) - //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-closed instead! - ; - // ADD TOGGLER.click/.hover - enableClosable(pane); - } - - // add Draggable events - initResizable(pane); - - // ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open" - if (s.isVisible) - setAsOpen(pane); // onOpen will be called, but NOT onResize - else { - setAsClosed(pane); // onClose will be called - bindStartSlidingEvents(pane, true); // will enable events IF option is set - } - - }); - - // SET ALL HANDLE DIMENSIONS - sizeHandles(); - } - - - /** - * Initialize scrolling ui-layout-content div - if exists - * - * @see initPane() - or externally after an Ajax injection - * @param {string} pane The pane to process - * @param {boolean=} [resize=true] Size content after init - */ -, initContent = function (pane, resize) { - if (!isInitialized()) return; - var - o = options[pane] - , sel = o.contentSelector - , I = Instance[pane] - , $P = $Ps[pane] - , $C - ; - if (sel) $C = I.content = $Cs[pane] = (o.findNestedContent) - ? $P.find(sel).eq(0) // match 1-element only - : $P.children(sel).eq(0) - ; - if ($C && $C.length) { - $C.data("layoutRole", "content"); - // SAVE original Content CSS - if (!$C.data("layoutCSS")) - $C.data("layoutCSS", styles($C, "height")); - $C.css( _c.content.cssReq ); - if (o.applyDemoStyles) { - $C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div - $P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane - } - // ensure no vertical scrollbar on pane - will mess up measurements - if ($P.css("overflowX").match(/(scroll|auto)/)) { - $P.css("overflow", "hidden"); - } - state[pane].content = {}; // init content state - if (resize !== false) sizeContent(pane); - // sizeContent() is called AFTER init of all elements - } - else - I.content = $Cs[pane] = false; - } - - - /** - * Add resize-bars to all panes that specify it in options - * -dependancy: $.fn.resizable - will skip if not found - * - * @see _create() - * @param {string=} [panes=""] The edge(s) to process - */ -, initResizable = function (panes) { - var draggingAvailable = $.layout.plugins.draggable - , side // set in start() - ; - panes = panes ? panes.split(",") : _c.borderPanes; - - $.each(panes, function (idx, pane) { - var o = options[pane]; - if (!draggingAvailable || !$Ps[pane] || !o.resizable) { - o.resizable = false; - return true; // skip to next - } - - var s = state[pane] - , z = options.zIndexes - , c = _c[pane] - , side = c.dir=="horz" ? "top" : "left" - , $P = $Ps[pane] - , $R = $Rs[pane] - , base = o.resizerClass - , lastPos = 0 // used when live-resizing - , r, live // set in start because may change - // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process - , resizerClass = base+"-drag" // resizer-drag - , resizerPaneClass = base+"-"+pane+"-drag" // resizer-north-drag - // 'helper' class is applied to the CLONED resizer-bar while it is being dragged - , helperClass = base+"-dragging" // resizer-dragging - , helperPaneClass = base+"-"+pane+"-dragging" // resizer-north-dragging - , helperLimitClass = base+"-dragging-limit" // resizer-drag - , helperPaneLimitClass = base+"-"+pane+"-dragging-limit" // resizer-north-drag - , helperClassesSet = false // logic var - ; - - if (!s.isClosed) - $R.attr("title", o.tips.Resize) - .css("cursor", o.resizerCursor); // n-resize, s-resize, etc - - $R.draggable({ - containment: $N[0] // limit resizing to layout container - , axis: (c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis - , delay: 0 - , distance: 1 - , grid: o.resizingGrid - // basic format for helper - style it using class: .ui-draggable-dragging - , helper: "clone" - , opacity: o.resizerDragOpacity - , addClasses: false // avoid ui-state-disabled class when disabled - //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed - , zIndex: z.resizer_drag - - , iframeFix: true // ThinkGem 修复有iframe时拖拽不平滑问题 - - , start: function (e, ui) { - // REFRESH options & state pointers in case we used swapPanes - o = options[pane]; - s = state[pane]; - // re-read options - live = o.livePaneResizing; - - // ondrag_start callback - will CANCEL hide if returns false - // TODO: dragging CANNOT be cancelled like this, so see if there is a way? - if (false === _runCallbacks("ondrag_start", pane)) return false; - - s.isResizing = true; // prevent pane from closing while resizing - state.paneResizing = pane; // easy to see if ANY pane is resizing - timer.clear(pane+"_closeSlider"); // just in case already triggered - - // SET RESIZER LIMITS - used in drag() - setSizeLimits(pane); // update pane/resizer state - r = s.resizerPosition; - lastPos = ui.position[ side ] - - $R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes - helperClassesSet = false; // reset logic var - see drag() - - // MASK PANES CONTAINING IFRAMES, APPLETS OR OTHER TROUBLESOME ELEMENTS - showMasks( pane, { resizing: true }); - } - - , drag: function (e, ui) { - if (!helperClassesSet) { // can only add classes after clone has been added to the DOM - //$(".ui-draggable-dragging") - ui.helper - .addClass( helperClass +" "+ helperPaneClass ) // add helper classes - .css({ right: "auto", bottom: "auto" }) // fix dir="rtl" issue - .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar - ; - helperClassesSet = true; - // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane! - if (s.isSliding) $Ps[pane].css("zIndex", z.pane_sliding); - } - // CONTAIN RESIZER-BAR TO RESIZING LIMITS - var limit = 0; - if (ui.position[side] < r.min) { - ui.position[side] = r.min; - limit = -1; - } - else if (ui.position[side] > r.max) { - ui.position[side] = r.max; - limit = 1; - } - // ADD/REMOVE dragging-limit CLASS - if (limit) { - ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit - window.defaultStatus = (limit>0 && pane.match(/(north|west)/)) || (limit<0 && pane.match(/(south|east)/)) ? o.tips.maxSizeWarning : o.tips.minSizeWarning; - } - else { - ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit - window.defaultStatus = ""; - } - // DYNAMICALLY RESIZE PANES IF OPTION ENABLED - // won't trigger unless resizer has actually moved! - if (live && Math.abs(ui.position[side] - lastPos) >= o.liveResizingTolerance) { - lastPos = ui.position[side]; - resizePanes(e, ui, pane) - } - } - - , stop: function (e, ui) { - $('body').enableSelection(); // RE-ENABLE TEXT SELECTION - window.defaultStatus = ""; // clear 'resizing limit' message from statusbar - $R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer - s.isResizing = false; - state.paneResizing = false; // easy to see if ANY pane is resizing - resizePanes(e, ui, pane, true); // true = resizingDone - } - - }); - }); - - /** - * resizePanes - * - * Sub-routine called from stop() - and drag() if livePaneResizing - * - * @param {!Object} evt - * @param {!Object} ui - * @param {string} pane - * @param {boolean=} [resizingDone=false] - */ - var resizePanes = function (evt, ui, pane, resizingDone) { - var dragPos = ui.position - , c = _c[pane] - , o = options[pane] - , s = state[pane] - , resizerPos - ; - switch (pane) { - case "north": resizerPos = dragPos.top; break; - case "west": resizerPos = dragPos.left; break; - case "south": resizerPos = sC.layoutHeight - dragPos.top - o.spacing_open; break; - case "east": resizerPos = sC.layoutWidth - dragPos.left - o.spacing_open; break; - }; - // remove container margin from resizer position to get the pane size - var newSize = resizerPos - sC.inset[c.side]; - - // Disable OR Resize Mask(s) created in drag.start - if (!resizingDone) { - // ensure we meet liveResizingTolerance criteria - if (Math.abs(newSize - s.size) < o.liveResizingTolerance) - return; // SKIP resize this time - // resize the pane - manualSizePane(pane, newSize, false, true); // true = noAnimation - sizeMasks(); // resize all visible masks - } - else { // resizingDone - // ondrag_end callback - if (false !== _runCallbacks("ondrag_end", pane)) - manualSizePane(pane, newSize, false, true); // true = noAnimation - hideMasks(true); // true = force hiding all masks even if one is 'sliding' - if (s.isSliding) // RE-SHOW 'object-masks' so objects won't show through sliding pane - showMasks( pane, { resizing: true }); - } - }; - } - - /** - * sizeMask - * - * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane - * Called when mask created, and during livePaneResizing - */ -, sizeMask = function () { - var $M = $(this) - , pane = $M.data("layoutMask") // eg: "west" - , s = state[pane] - ; - // only masks over an IFRAME-pane need manual resizing - if (s.tagName == "IFRAME" && s.isVisible) // no need to mask closed/hidden panes - $M.css({ - top: s.offsetTop - , left: s.offsetLeft - , width: s.outerWidth - , height: s.outerHeight - }); - /* ALT Method... - var $P = $Ps[pane]; - $M.css( $P.position() ).css({ width: $P[0].offsetWidth, height: $P[0].offsetHeight }); - */ - } -, sizeMasks = function () { - $Ms.each( sizeMask ); // resize all 'visible' masks - } - - /** - * @param {string} pane The pane being resized, animated or isSliding - * @param {Object=} [args] (optional) Options: which masks to apply, and to which panes - */ -, showMasks = function (pane, args) { - var c = _c[pane] - , panes = ["center"] - , z = options.zIndexes - , a = $.extend({ - objectsOnly: false - , animation: false - , resizing: true - , sliding: state[pane].isSliding - }, args ) - , o, s - ; - if (a.resizing) - panes.push( pane ); - if (a.sliding) - panes.push( _c.oppositeEdge[pane] ); // ADD the oppositeEdge-pane - - if (c.dir === "horz") { - panes.push("west"); - panes.push("east"); - } - - $.each(panes, function(i,p){ - s = state[p]; - o = options[p]; - if (s.isVisible && ( o.maskObjects || (!a.objectsOnly && o.maskContents) )) { - getMasks(p).each(function(){ - sizeMask.call(this); - this.style.zIndex = s.isSliding ? z.pane_sliding+1 : z.pane_normal+1 - this.style.display = "block"; - }); - } - }); - } - - /** - * @param {boolean=} force Hide masks even if a pane is sliding - */ -, hideMasks = function (force) { - // ensure no pane is resizing - could be a timing issue - if (force || !state.paneResizing) { - $Ms.hide(); // hide ALL masks - } - // if ANY pane is sliding, then DO NOT remove masks from panes with maskObjects enabled - else if (!force && !$.isEmptyObject( state.panesSliding )) { - var i = $Ms.length - 1 - , p, $M; - for (; i >= 0; i--) { - $M = $Ms.eq(i); - p = $M.data("layoutMask"); - if (!options[p].maskObjects) { - $M.hide(); - } - } - } - } - - /** - * @param {string} pane - */ -, getMasks = function (pane) { - var $Masks = $([]) - , $M, i = 0, c = $Ms.length - ; - for (; i CSS - if (sC.tagName === "BODY" && ($N = $("html")).data(css)) // RESET CSS - $N.css( $N.data(css) ).removeData(css); - - // trigger plugins for this layout, if there are any - runPluginCallbacks( Instance, $.layout.onDestroy ); - - // trigger state-management and onunload callback - unload(); - - // clear the Instance of everything except for container & options (so could recreate) - // RE-CREATE: myLayout = myLayout.container.layout( myLayout.options ); - for (var n in Instance) - if (!n.match(/^(container|options)$/)) delete Instance[ n ]; - // add a 'destroyed' flag to make it easy to check - Instance.destroyed = true; - - // if this is a child layout, CLEAR the child-pointer in the parent - /* for now the pointer REMAINS, but with only container, options and destroyed keys - if (parentPane) { - var layout = parentPane.pane.data("parentLayout") - , key = layout.options.instanceKey || 'error'; - // THIS SYNTAX MAY BE WRONG! - parentPane.children[key] = layout.children[ parentPane.name ].children[key] = null; - } - */ - - return Instance; // for coding convenience - } - - /** - * Remove a pane from the layout - subroutine of destroy() - * - * @see destroy() - * @param {(string|Object)} evt_or_pane The pane to process - * @param {boolean=} [remove=false] Remove the DOM element? - * @param {boolean=} [skipResize=false] Skip calling resizeAll()? - * @param {boolean=} [destroyChild=true] Destroy Child-layouts? If not passed, obeys options setting - */ -, removePane = function (evt_or_pane, remove, skipResize, destroyChild) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $P = $Ps[pane] - , $C = $Cs[pane] - , $R = $Rs[pane] - , $T = $Ts[pane] - ; - // NOTE: elements can still exist even after remove() - // so check for missing data(), which is cleared by removed() - if ($P && $.isEmptyObject( $P.data() )) $P = false; - if ($C && $.isEmptyObject( $C.data() )) $C = false; - if ($R && $.isEmptyObject( $R.data() )) $R = false; - if ($T && $.isEmptyObject( $T.data() )) $T = false; - - if ($P) $P.stop(true, true); - - var o = options[pane] - , s = state[pane] - , d = "layout" - , css = "layoutCSS" - , pC = children[pane] - , hasChildren = $.isPlainObject( pC ) && !$.isEmptyObject( pC ) - , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildren - ; - // FIRST destroy the child-layout(s) - if (hasChildren && destroy) { - $.each( pC, function (key, child) { - if (!child.destroyed) - child.destroy(true);// tell child-layout to destroy ALL its child-layouts too - if (child.destroyed) // destroy was successful - delete pC[key]; - }); - // if no more children, remove the children hash - if ($.isEmptyObject( pC )) { - pC = children[pane] = null; // clear children hash - hasChildren = false; - } - } - - // Note: can't 'remove' a pane element with non-destroyed children - if ($P && remove && !hasChildren) - $P.remove(); // remove the pane-element and everything inside it - else if ($P && $P[0]) { - // create list of ALL pane-classes that need to be removed - var root = o.paneClass // default="ui-layout-pane" - , pRoot = root +"-"+ pane // eg: "ui-layout-pane-west" - , _open = "-open" - , _sliding= "-sliding" - , _closed = "-closed" - , classes = [ root, root+_open, root+_closed, root+_sliding, // generic classes - pRoot, pRoot+_open, pRoot+_closed, pRoot+_sliding ] // pane-specific classes - ; - $.merge(classes, getHoverClasses($P, true)); // ADD hover-classes - // remove all Layout classes from pane-element - $P .removeClass( classes.join(" ") ) // remove ALL pane-classes - .removeData("parentLayout") - .removeData("layoutPane") - .removeData("layoutRole") - .removeData("layoutEdge") - .removeData("autoHidden") // in case set - .unbind("."+ sID) // remove ALL Layout events - // TODO: remove these extra unbind commands when jQuery is fixed - //.unbind("mouseenter"+ sID) - //.unbind("mouseleave"+ sID) - ; - // do NOT reset CSS if this pane/content is STILL the container of a nested layout! - // the nested layout will reset its 'container' CSS when/if it is destroyed - if (hasChildren && $C) { - // a content-div may not have a specific width, so give it one to contain the Layout - $C.width( $C.width() ); - $.each( pC, function (key, child) { - child.resizeAll(); // resize the Layout - }); - } - else if ($C) - $C.css( $C.data(css) ).removeData(css).removeData("layoutRole"); - // remove pane AFTER content in case there was a nested layout - if (!$P.data(d)) - $P.css( $P.data(css) ).removeData(css); - } - - // REMOVE pane resizer and toggler elements - if ($T) $T.remove(); - if ($R) $R.remove(); - - // CLEAR all pointers and state data - Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = false; - s = { removed: true }; - - if (!skipResize) - resizeAll(); - } - - -/* - * ########################### - * ACTION METHODS - * ########################### - */ - - /** - * @param {string} pane - */ -, _hidePane = function (pane) { - var $P = $Ps[pane] - , o = options[pane] - , s = $P[0].style - ; - if (o.useOffscreenClose) { - if (!$P.data(_c.offscreenReset)) - $P.data(_c.offscreenReset, { left: s.left, right: s.right }); - $P.css( _c.offscreenCSS ); - } - else - $P.hide().removeData(_c.offscreenReset); - } - - /** - * @param {string} pane - */ -, _showPane = function (pane) { - var $P = $Ps[pane] - , o = options[pane] - , off = _c.offscreenCSS - , old = $P.data(_c.offscreenReset) - , s = $P[0].style - ; - $P .show() // ALWAYS show, just in case - .removeData(_c.offscreenReset); - if (o.useOffscreenClose && old) { - if (s.left == off.left) - s.left = old.left; - if (s.right == off.right) - s.right = old.right; - } - } - - - /** - * 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|Object)} evt_or_pane The pane being hidden, ie: north, south, east, or west - * @param {boolean=} [noAnimation=false] - */ -, hide = function (evt_or_pane, noAnimation) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , o = options[pane] - , s = state[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - ; - if (pane === "center" || !$P || s.isHidden) return; // pane does not exist OR is already hidden - - // onhide_start callback - will CANCEL hide if returns false - if (state.initialized && false === _runCallbacks("onhide_start", pane)) return; - - s.isSliding = false; // just in case - delete state.panesSliding[pane]; - - // now hide the elements - if ($R) $R.hide(); // hide resizer-bar - if (!state.initialized || s.isClosed) { - s.isClosed = true; // to trigger open-animation on show() - s.isHidden = true; - s.isVisible = false; - if (!state.initialized) - _hidePane(pane); // no animation when loading page - sizeMidPanes(_c[pane].dir === "horz" ? "" : "center"); - if (state.initialized || o.triggerEventsOnLoad) - _runCallbacks("onhide_end", pane); - } - else { - s.isHiding = true; // used by onclose - close(pane, false, noAnimation); // adjust all panes to fit - } - } - - /** - * Show a hidden pane - show as 'closed' by default unless openPane = true - * - * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west - * @param {boolean=} [openPane=false] - * @param {boolean=} [noAnimation=false] - * @param {boolean=} [noAlert=false] - */ -, show = function (evt_or_pane, openPane, noAnimation, noAlert) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , o = options[pane] - , s = state[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - ; - if (pane === "center" || !$P || !s.isHidden) return; // pane does not exist OR is not hidden - - // onshow_start callback - will CANCEL show if returns false - if (false === _runCallbacks("onshow_start", pane)) return; - - s.isShowing = true; // used by onopen/onclose - //s.isHidden = false; - will be set by open/close - if not cancelled - s.isSliding = false; // just in case - delete state.panesSliding[pane]; - - // now show the elements - //if ($R) $R.show(); - will be shown by open/close - if (openPane === false) - close(pane, true); // true = force - else - open(pane, false, noAnimation, noAlert); // adjust all panes to fit - } - - - /** - * Toggles a pane open/closed by calling either open or close - * - * @param {(string|Object)} evt_or_pane The pane being toggled, ie: north, south, east, or west - * @param {boolean=} [slide=false] - */ -, toggle = function (evt_or_pane, slide) { - if (!isInitialized()) return; - var evt = evtObj(evt_or_pane) - , pane = evtPane.call(this, evt_or_pane) - , s = state[pane] - ; - if (evt) // called from to $R.dblclick OR triggerPaneEvent - evt.stopImmediatePropagation(); - if (s.isHidden) - show(pane); // will call 'open' after unhiding it - else if (s.isClosed) - open(pane, !!slide); - else - close(pane); - } - - - /** - * Utility method used during init or other auto-processes - * - * @param {string} pane The pane being closed - * @param {boolean=} [setHandles=false] - */ -, _closePane = function (pane, setHandles) { - var - $P = $Ps[pane] - , s = state[pane] - ; - _hidePane(pane); - s.isClosed = true; - s.isVisible = false; - if (setHandles) setAsClosed(pane); - } - - /** - * Close the specified pane (animation optional), and resize all other panes as needed - * - * @param {(string|Object)} evt_or_pane The pane being closed, ie: north, south, east, or west - * @param {boolean=} [force=false] - * @param {boolean=} [noAnimation=false] - * @param {boolean=} [skipCallback=false] - */ -, close = function (evt_or_pane, force, noAnimation, skipCallback) { - var pane = evtPane.call(this, evt_or_pane); - if (pane === "center") return; // validate - // if pane has been initialized, but NOT the complete layout, close pane instantly - if (!state.initialized && $Ps[pane]) { - _closePane(pane, true); // INIT pane as closed - return; - } - if (!isInitialized()) return; - - var - $P = $Ps[pane] - , $R = $Rs[pane] - , $T = $Ts[pane] - , o = options[pane] - , s = state[pane] - , c = _c[pane] - , doFX, isShowing, isHiding, wasSliding; - - // QUEUE in case another action/animation is in progress - $N.queue(function( queueNext ){ - - if ( !$P - || (!o.closable && !s.isShowing && !s.isHiding) // invalid request // (!o.resizable && !o.closable) ??? - || (!force && s.isClosed && !s.isShowing) // already closed - ) return queueNext(); - - // onclose_start callback - will CANCEL hide if returns false - // SKIP if just 'showing' a hidden pane as 'closed' - var abort = !s.isShowing && false === _runCallbacks("onclose_start", pane); - - // transfer logic vars to temp vars - isShowing = s.isShowing; - isHiding = s.isHiding; - wasSliding = s.isSliding; - // now clear the logic vars (REQUIRED before aborting) - delete s.isShowing; - delete s.isHiding; - - if (abort) return queueNext(); - - doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none"); - s.isMoving = true; - s.isClosed = true; - s.isVisible = false; - // update isHidden BEFORE sizing panes - if (isHiding) s.isHidden = true; - else if (isShowing) s.isHidden = false; - - if (s.isSliding) // pane is being closed, so UNBIND trigger events - bindStopSlidingEvents(pane, false); // will set isSliding=false - else // resize panes adjacent to this one - sizeMidPanes(_c[pane].dir === "horz" ? "" : "center", false); // false = NOT skipCallback - - // if this pane has a resizer bar, move it NOW - before animation - setAsClosed(pane); - - // CLOSE THE PANE - if (doFX) { // animate the close - 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) close_2(); - queueNext(); - }); - } - else { // hide the pane without animation - _hidePane(pane); - close_2(); - queueNext(); - }; - }); - - // SUBROUTINE - function close_2 () { - s.isMoving = false; - bindStartSlidingEvents(pane, true); // will enable if o.slidable = true - - // if opposite-pane was autoClosed, see if it can be autoOpened now - var altPane = _c.oppositeEdge[pane]; - if (state[ altPane ].noRoom) { - setSizeLimits( altPane ); - makePaneFit( altPane ); - } - - if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) { - // onclose callback - UNLESS just 'showing' a hidden pane as 'closed' - if (!isShowing) _runCallbacks("onclose_end", pane); - // onhide OR onshow callback - if (isShowing) _runCallbacks("onshow_end", pane); - if (isHiding) _runCallbacks("onhide_end", pane); - } - } - } - - /** - * @param {string} pane The pane just closed, ie: north, south, east, or west - */ -, setAsClosed = function (pane) { - if (!$Rs[pane]) return; // handles not initialized yet! - var - $P = $Ps[pane] - , $R = $Rs[pane] - , $T = $Ts[pane] - , o = options[pane] - , s = state[pane] - , side = _c[pane].side - , rClass = o.resizerClass - , tClass = o.togglerClass - , _pane = "-"+ pane // used for classNames - , _open = "-open" - , _sliding= "-sliding" - , _closed = "-closed" - ; - $R - .css(side, sC.inset[side]) // move the resizer - .removeClass( rClass+_open +" "+ rClass+_pane+_open ) - .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding ) - .addClass( rClass+_closed +" "+ rClass+_pane+_closed ) - ; - // handle already-hidden panes in case called by swap() or a similar method - if (s.isHidden) $R.hide(); // hide resizer-bar - - // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvents? - if (o.resizable && $.layout.plugins.draggable) - $R - .draggable("disable") - .removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here - .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.tips.Open) // may be blank - ; - // toggler-content - if exists - $T.children(".content-open").hide(); - $T.children(".content-closed").css("display","block"); - } - - // sync any 'pin buttons' - syncPinBtns(pane, false); - - if (state.initialized) { - // resize 'length' and position togglers for adjacent panes - sizeHandles(); - } - } - - /** - * Open the specified pane (animation optional), and resize all other panes as needed - * - * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west - * @param {boolean=} [slide=false] - * @param {boolean=} [noAnimation=false] - * @param {boolean=} [noAlert=false] - */ -, open = function (evt_or_pane, slide, noAnimation, noAlert) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $P = $Ps[pane] - , $R = $Rs[pane] - , $T = $Ts[pane] - , o = options[pane] - , s = state[pane] - , c = _c[pane] - , doFX, isShowing - ; - if (pane === "center") return; // validate - // QUEUE in case another action/animation is in progress - $N.queue(function( queueNext ){ - - if ( !$P - || (!o.resizable && !o.closable && !s.isShowing) // invalid request - || (s.isVisible && !s.isSliding) // already open - ) return queueNext(); - - // pane can ALSO be unhidden by just calling show(), so handle this scenario - if (s.isHidden && !s.isShowing) { - queueNext(); // call before show() because it needs the queue free - show(pane, true); - return; - } - - if (s.autoResize && s.size != o.size) // resize pane to original size set in options - sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize - else - // make sure there is enough space available to open the pane - setSizeLimits(pane, slide); - - // onopen_start callback - will CANCEL open if returns false - var cbReturn = _runCallbacks("onopen_start", pane); - - if (cbReturn === "abort") - return queueNext(); - - // update pane-state again in case options were changed in onopen_start - if (cbReturn !== "NC") // NC = "No Callback" - setSizeLimits(pane, slide); - - if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN! - syncPinBtns(pane, false); // make sure pin-buttons are reset - if (!noAlert && o.tips.noRoomToOpen) - alert(o.tips.noRoomToOpen); - return queueNext(); // ABORT - } - - if (slide) // START Sliding - will set isSliding=true - bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane - else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead - bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false - else if (o.slidable) - bindStartSlidingEvents(pane, false); // UNBIND trigger events - - s.noRoom = false; // will be reset by makePaneFit if 'noRoom' - makePaneFit(pane); - - // transfer logic var to temp var - isShowing = s.isShowing; - // now clear the logic var - delete s.isShowing; - - doFX = !noAnimation && s.isClosed && (o.fxName_open != "none"); - s.isMoving = true; - s.isVisible = true; - s.isClosed = false; - // update isHidden BEFORE sizing panes - WHY??? Old? - if (isShowing) s.isHidden = false; - - if (doFX) { // ANIMATE - // mask adjacent panes with objects - 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.isVisible) open_2(); // continue - queueNext(); - }); - } - else { // no animation - _showPane(pane);// just show pane and... - open_2(); // continue - queueNext(); - }; - }); - - // SUBROUTINE - function open_2 () { - s.isMoving = false; - - // cure iframe display issues - _fixIframe(pane); - - // 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" : "", false); // false = NOT skipCallback - } - - // set classes, position handles and execute callbacks... - setAsOpen(pane); - }; - - } - - /** - * @param {string} pane The pane just opened, ie: north, south, east, or west - * @param {boolean=} [skipCallback=false] - */ -, setAsOpen = function (pane, skipCallback) { - var - $P = $Ps[pane] - , $R = $Rs[pane] - , $T = $Ts[pane] - , o = options[pane] - , s = state[pane] - , side = _c[pane].side - , rClass = o.resizerClass - , tClass = o.togglerClass - , _pane = "-"+ pane // used for classNames - , _open = "-open" - , _closed = "-closed" - , _sliding= "-sliding" - ; - $R - .css(side, sC.inset[side] + getPaneSize(pane)) // move the resizer - .removeClass( rClass+_closed +" "+ rClass+_pane+_closed ) - .addClass( rClass+_open +" "+ rClass+_pane+_open ) - ; - if (s.isSliding) - $R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding ) - else // in case 'was sliding' - $R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding ) - - removeHover( 0, $R ); // remove hover classes - if (o.resizable && $.layout.plugins.draggable) - $R .draggable("enable") - .css("cursor", o.resizerCursor) - .attr("title", o.tips.Resize); - else if (!s.isSliding) - $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.tips.Close); // may be blank - removeHover( 0, $T ); // remove hover classes - // toggler-content - if exists - $T.children(".content-closed").hide(); - $T.children(".content-open").css("display","block"); - } - - // sync any 'pin buttons' - syncPinBtns(pane, !s.isSliding); - - // update pane-state dimensions - BEFORE resizing content - $.extend(s, elDims($P)); - - if (state.initialized) { - // resize resizer & toggler sizes for all panes - sizeHandles(); - // resize content every time pane opens - to be sure - sizeContent(pane, true); // true = remeasure headers/footers, even if 'pane.isMoving' - } - - if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) { - // onopen callback - _runCallbacks("onopen_end", pane); - // onshow callback - TODO: should this be here? - if (s.isShowing) _runCallbacks("onshow_end", pane); - - // ALSO call onresize because layout-size *may* have changed while pane was closed - if (state.initialized) - _runCallbacks("onresize_end", pane); - } - - // TODO: Somehow sizePane("north") is being called after this point??? - } - - - /** - * slideOpen / slideClose / slideToggle - * - * Pass-though methods for sliding - */ -, slideOpen = function (evt_or_pane) { - if (!isInitialized()) return; - var evt = evtObj(evt_or_pane) - , pane = evtPane.call(this, evt_or_pane) - , s = state[pane] - , delay = options[pane].slideDelay_open - ; - if (pane === "center") return; // validate - // prevent event from triggering on NEW resizer binding created below - if (evt) evt.stopImmediatePropagation(); - - if (s.isClosed && evt && evt.type === "mouseenter" && delay > 0) - // trigger = mouseenter - use a delay - timer.set(pane+"_openSlider", open_NOW, delay); - else - open_NOW(); // will unbind events if is already open - - /** - * SUBROUTINE for timed open - */ - function open_NOW () { - if (!s.isClosed) // skip if no longer closed! - bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane - else if (!s.isMoving) - open(pane, true); // true = slide - open() will handle binding - }; - } - -, slideClose = function (evt_or_pane) { - if (!isInitialized()) return; - var evt = evtObj(evt_or_pane) - , pane = evtPane.call(this, evt_or_pane) - , o = options[pane] - , s = state[pane] - , delay = s.isMoving ? 1000 : 300 // MINIMUM delay - option may override - ; - if (pane === "center") return; // validate - 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 if (o.preventQuickSlideClose && s.isMoving) - return; // handle Chrome quick-close on slide-open - else if (o.preventPrematureSlideClose && evt && $.layout.isMouseOverElem(evt, $Ps[pane])) - return; // handle incorrect mouseleave trigger, like when over a SELECT-list in IE - else if (evt) // trigger = mouseleave - use a delay - // 1 sec delay if 'opening', else .3 sec - timer.set(pane+"_closeSlider", close_NOW, max(o.slideDelay_close, delay)); - else // called programically - close_NOW(); - - /** - * SUBROUTINE for timed close - */ - function close_NOW () { - if (s.isClosed) // skip 'close' if already closed! - bindStopSlidingEvents(pane, false); // UNBIND trigger events - TODO: is this needed here? - else if (!s.isMoving) - close(pane); // close will handle unbinding - }; - } - - /** - * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west - */ -, slideToggle = function (evt_or_pane) { - var pane = evtPane.call(this, evt_or_pane); - toggle(pane, true); - } - - - /** - * 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 - */ -, lockPaneForFX = function (pane, doLock) { - var $P = $Ps[pane] - , s = state[pane] - , o = options[pane] - , z = options.zIndexes - ; - if (doLock) { - showMasks( pane, { animation: true, objectsOnly: true }); - $P.css({ zIndex: z.pane_animate }); // overlay all elements during animation - if (pane=="south") - $P.css({ top: sC.inset.top + sC.innerHeight - $P.outerHeight() }); - else if (pane=="east") - $P.css({ left: sC.inset.left + sC.innerWidth - $P.outerWidth() }); - } - else { // animation DONE - RESET CSS - hideMasks(); - $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) }); - if (pane=="south") - $P.css({ top: "auto" }); - // if pane is positioned 'off-screen', then DO NOT screw with it! - else if (pane=="east" && !$P.css("left").match(/\-99999/)) - $P.css({ left: "auto" }); - // fix anti-aliasing in IE - only needed for animations that change opacity - if (browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1) - $P[0].style.removeAttribute('filter'); - } - } - - - /** - * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger - * - * @see open(), close() - * @param {string} pane The pane to enable/disable, 'north', 'south', etc. - * @param {boolean} enable Enable or Disable sliding? - */ -, bindStartSlidingEvents = function (pane, enable) { - var o = options[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - , evtName = o.slideTrigger_open.toLowerCase() - ; - if (!$R || (enable && !o.slidable)) return; - - // make sure we have a valid event - if (evtName.match(/mouseover/)) - evtName = o.slideTrigger_open = "mouseenter"; - else if (!evtName.match(/(click|dblclick|mouseenter)/)) - evtName = o.slideTrigger_open = "click"; - - // must remove double-click-toggle when using dblclick-slide - if (o.resizerDblClickToggle && evtName.match(/click/)) { - $R[enable ? "unbind" : "bind"]('dblclick.'+ sID, toggle) - } - - $R - // add or remove event - [enable ? "bind" : "unbind"](evtName +'.'+ sID, slideOpen) - // set the appropriate cursor & title/tip - .css("cursor", enable ? o.sliderCursor : "default") - .attr("title", enable ? o.tips.Slide : "") - ; - } - - /** - * Add or remove 'mouseleave' events to 'slide close' when pane is 'sliding' open or closed - * Also increases zIndex when pane is sliding open - * See bindStartSlidingEvents for code to control 'slide open' - * - * @see slideOpen(), slideClose() - * @param {string} pane The pane to process, 'north', 'south', etc. - * @param {boolean} enable Enable or Disable events? - */ -, bindStopSlidingEvents = function (pane, enable) { - var o = options[pane] - , s = state[pane] - , c = _c[pane] - , z = options.zIndexes - , evtName = o.slideTrigger_close.toLowerCase() - , action = (enable ? "bind" : "unbind") - , $P = $Ps[pane] - , $R = $Rs[pane] - ; - timer.clear(pane+"_closeSlider"); // just in case - - if (enable) { - s.isSliding = true; - state.panesSliding[pane] = true; - // remove 'slideOpen' event from resizer - // ALSO will raise the zIndex of the pane & resizer - bindStartSlidingEvents(pane, false); - } - else { - s.isSliding = false; - delete state.panesSliding[pane]; - } - - // RE/SET zIndex - increases when pane is sliding-open, resets to normal when not - $P.css("zIndex", enable ? z.pane_sliding : z.pane_normal); - $R.css("zIndex", enable ? z.pane_sliding+2 : z.resizer_normal); // NOTE: mask = pane_sliding+1 - - // make sure we have a valid event - if (!evtName.match(/(click|mouseleave)/)) - evtName = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout' - - // add/remove slide triggers - $R[action](evtName, slideClose); // base event on resize - // need extra events for mouseleave - if (evtName === "mouseleave") { - // also close on pane.mouseleave - $P[action]("mouseleave."+ sID, slideClose); - // cancel timer when mouse moves between 'pane' and 'resizer' - $R[action]("mouseenter."+ sID, cancelMouseOut); - $P[action]("mouseenter."+ sID, cancelMouseOut); - } - - if (!enable) - timer.clear(pane+"_closeSlider"); - else if (evtName === "click" && !o.resizable) { - // IF pane is not resizable (which already has a cursor and tip) - // then set the a cursor & title/tip on resizer when sliding - $R.css("cursor", enable ? o.sliderCursor : "default"); - $R.attr("title", enable ? o.tips.Close : ""); // use Toggler-tip, eg: "Close Pane" - } - - // SUBROUTINE for mouseleave timer clearing - function cancelMouseOut (evt) { - timer.clear(pane+"_closeSlider"); - evt.stopPropagation(); - } - } - - - /** - * Hides/closes a pane if there is insufficient room - reverses this when there is room again - * MUST have already called setSizeLimits() before calling this method - * - * @param {string} pane The pane being resized - * @param {boolean=} [isOpening=false] Called from onOpen? - * @param {boolean=} [skipCallback=false] Should the onresize callback be run? - * @param {boolean=} [force=false] - */ -, makePaneFit = function (pane, isOpening, skipCallback, force) { - var o = options[pane] - , s = state[pane] - , c = _c[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - , isSidePane = c.dir==="vert" - , hasRoom = false - ; - // special handling for center & east/west panes - if (pane === "center" || (isSidePane && s.noVerticalRoom)) { - // see if there is enough room to display the pane - // ERROR: hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth); - hasRoom = (s.maxHeight >= 0); - if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now - _showPane(pane); - if ($R) $R.show(); - s.isVisible = true; - s.noRoom = false; - if (isSidePane) s.noVerticalRoom = false; - _fixIframe(pane); - } - else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now - _hidePane(pane); - if ($R) $R.hide(); - s.isVisible = false; - s.noRoom = true; - } - } - - // see if there is enough room to fit the border-pane - if (pane === "center") { - // ignore center in this block - } - else if (s.minSize <= s.maxSize) { // pane CAN fit - hasRoom = true; - if (s.size > s.maxSize) // pane is too big - shrink it - sizePane(pane, s.maxSize, skipCallback, true, force); // true = noAnimation - else if (s.size < s.minSize) // pane is too small - enlarge it - sizePane(pane, s.minSize, skipCallback, true, force); // true = noAnimation - // need s.isVisible because new pseudoClose method keeps pane visible, but off-screen - else if ($R && s.isVisible && $P.is(":visible")) { - // make sure resizer-bar is positioned correctly - // handles situation where nested layout was 'hidden' when initialized - var pos = s.size + sC.inset[c.side]; - if ($.layout.cssNum( $R, c.side ) != pos) $R.css( c.side, pos ); - } - - // if was previously hidden due to noRoom, then RESET because NOW there is room - if (s.noRoom) { - // s.noRoom state will be set by open or show - if (s.wasOpen && o.closable) { - if (o.autoReopen) - open(pane, false, true, true); // true = noAnimation, true = noAlert - else // leave the pane closed, so just update state - s.noRoom = false; - } - else - show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert - } - } - else { // !hasRoom - pane CANNOT fit - if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now... - s.noRoom = true; // update state - s.wasOpen = !s.isClosed && !s.isSliding; - if (s.isClosed){} // SKIP - else if (o.closable) // 'close' if possible - close(pane, true, true); // true = force, true = noAnimation - else // 'hide' pane if cannot just be closed - hide(pane, true); // true = noAnimation - } - } - } - - - /** - * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized' - * - * @param {(string|Object)} evt_or_pane The pane being resized - * @param {number} size The *desired* new size for this pane - will be validated - * @param {boolean=} [skipCallback=false] Should the onresize callback be run? - * @param {boolean=} [noAnimation=false] - * @param {boolean=} [force=false] Force resizing even if does not seem necessary - */ -, manualSizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , o = options[pane] - , s = state[pane] - // if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete... - , forceResize = force || (o.livePaneResizing && !s.isResizing) - ; - if (pane === "center") return; // validate - // ANY call to manualSizePane disables autoResize - ie, percentage sizing - s.autoResize = false; - // flow-through... - sizePane(pane, size, skipCallback, noAnimation, forceResize); // will animate resize if option enabled - } - - /** - * sizePane is called only by internal methods whenever a pane needs to be resized - * - * @param {(string|Object)} evt_or_pane The pane being resized - * @param {number} size The *desired* new size for this pane - will be validated - * @param {boolean=} [skipCallback=false] Should the onresize callback be run? - * @param {boolean=} [noAnimation=false] - * @param {boolean=} [force=false] Force resizing even if does not seem necessary - */ -, sizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) // probably NEVER called from event? - , o = options[pane] - , s = state[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - , side = _c[pane].side - , dimName = _c[pane].sizeType.toLowerCase() - , skipResizeWhileDragging = s.isResizing && !o.triggerEventsDuringLiveResize - , doFX = noAnimation !== true && o.animatePaneSizing - , oldSize, newSize - ; - if (pane === "center") return; // validate - // QUEUE in case another action/animation is in progress - $N.queue(function( queueNext ){ - // calculate 'current' min/max sizes - setSizeLimits(pane); // update pane-state - oldSize = s.size; - size = _parseSize(pane, size); // handle percentages & auto - size = max(size, _parseSize(pane, o.minSize)); - size = min(size, s.maxSize); - if (size < s.minSize) { // not enough room for pane! - queueNext(); // call before makePaneFit() because it needs the queue free - makePaneFit(pane, false, skipCallback); // will hide or close pane - return; - } - - // IF newSize is same as oldSize, then nothing to do - abort - if (!force && size === oldSize) - return queueNext(); - - s.newSize = size; - - // onresize_start callback CANNOT cancel resizing because this would break the layout! - if (!skipCallback && state.initialized && s.isVisible) - _runCallbacks("onresize_start", pane); - - // resize the pane, and make sure its visible - newSize = cssSize(pane, size); - - if (doFX && $P.is(":visible")) { // ANIMATE - var fx = $.layout.effects.size[pane] || $.layout.effects.size.all - , easing = o.fxSettings_size.easing || fx.easing - , z = options.zIndexes - , props = {}; - props[ dimName ] = newSize +'px'; - s.isMoving = true; - // overlay all elements during animation - $P.css({ zIndex: z.pane_animate }) - .show().animate( props, o.fxSpeed_size, easing, function(){ - // reset zIndex after animation - $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) }); - s.isMoving = false; - delete s.newSize; - sizePane_2(); // continue - queueNext(); - }); - } - else { // no animation - $P.css( dimName, newSize ); // resize pane - delete s.newSize; - // if pane is visible, then - if ($P.is(":visible")) - sizePane_2(); // continue - else { - // pane is NOT VISIBLE, so just update state data... - // when pane is *next opened*, it will have the new size - s.size = size; // update state.size - //$.extend(s, elDims($P)); // update state dimensions - CANNOT do this when not visible! } - } - queueNext(); - }; - - }); - - // SUBROUTINE - function sizePane_2 () { - /* Panes are sometimes not sized precisely in some browsers!? - * This code will resize the pane up to 3 times to nudge the pane to the correct size - */ - var actual = dimName==='width' ? $P.outerWidth() : $P.outerHeight() - , tries = [{ - pane: pane - , count: 1 - , target: size - , actual: actual - , correct: (size === actual) - , attempt: size - , cssSize: newSize - }] - , lastTry = tries[0] - , thisTry = {} - , msg = 'Inaccurate size after resizing the '+ pane +'-pane.' - ; - while ( !lastTry.correct ) { - thisTry = { pane: pane, count: lastTry.count+1, target: size }; - - if (lastTry.actual > size) - thisTry.attempt = max(0, lastTry.attempt - (lastTry.actual - size)); - else // lastTry.actual < size - thisTry.attempt = max(0, lastTry.attempt + (size - lastTry.actual)); - - thisTry.cssSize = cssSize(pane, thisTry.attempt); - $P.css( dimName, thisTry.cssSize ); - - thisTry.actual = dimName=='width' ? $P.outerWidth() : $P.outerHeight(); - thisTry.correct = (size === thisTry.actual); - - // log attempts and alert the user of this *non-fatal error* (if showDebugMessages) - if ( tries.length === 1) { - _log(msg, false, true); - _log(lastTry, false, true); - } - _log(thisTry, false, true); - // after 4 tries, is as close as its gonna get! - if (tries.length > 3) break; - - tries.push( thisTry ); - lastTry = tries[ tries.length - 1 ]; - } - // END TESTING CODE - - // update pane-state dimensions - s.size = size; - $.extend(s, elDims($P)); - - if (s.isVisible && $P.is(":visible")) { - // reposition the resizer-bar - if ($R) $R.css( side, size + sC.inset[side] ); - // resize the content-div - sizeContent(pane); - } - - if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible) - _runCallbacks("onresize_end", pane); - - // resize all the adjacent panes, and adjust their toggler buttons - // when skipCallback passed, it means the controlling method will handle 'other panes' - if (!skipCallback) { - // also no callback if live-resize is in progress and NOT triggerEventsDuringLiveResize - if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "" : "center", skipResizeWhileDragging, force); - sizeHandles(); - } - - // if opposite-pane was autoClosed, see if it can be autoOpened now - var altPane = _c.oppositeEdge[pane]; - if (size < oldSize && state[ altPane ].noRoom) { - setSizeLimits( altPane ); - makePaneFit( altPane, false, skipCallback ); - } - - // DEBUG - ALERT user/developer so they know there was a sizing problem - if (tries.length > 1) - _log(msg +'\nSee the Error Console for details.', true, true); - } - } - - /** - * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide() - * @param {(Array.|string)} panes The pane(s) being resized, comma-delmited string - * @param {boolean=} [skipCallback=false] Should the onresize callback be run? - * @param {boolean=} [force=false] - */ -, sizeMidPanes = function (panes, skipCallback, force) { - panes = (panes ? panes : "east,west,center").split(","); - - $.each(panes, function (i, pane) { - if (!$Ps[pane]) return; // NO PANE - skip - var - o = options[pane] - , s = state[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - , isCenter= (pane=="center") - , hasRoom = true - , CSS = {} - // if pane is not visible, show it invisibly NOW rather than for *each call* in this script - , visCSS = $.layout.showInvisibly($P) - - , newCenter = calcNewCenterPaneDims() - ; - - // update pane-state dimensions - $.extend(s, elDims($P)); - - if (pane === "center") { - if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight) { - $P.css(visCSS); - return true; // SKIP - pane already the correct size - } - // set state for makePaneFit() logic - $.extend(s, cssMinDims(pane), { - maxWidth: newCenter.width - , maxHeight: newCenter.height - }); - CSS = newCenter; - s.newWidth = CSS.width; - s.newHeight = CSS.height; - // convert OUTER width/height to CSS width/height - CSS.width = cssW($P, CSS.width); - // NEW - allow pane to extend 'below' visible area rather than hide it - CSS.height = cssH($P, CSS.height); - hasRoom = CSS.width >= 0 && CSS.height >= 0; // height >= 0 = ALWAYS TRUE NOW - - // during layout init, try to shrink east/west panes to make room for center - if (!state.initialized && o.minWidth > newCenter.width) { - var - reqPx = o.minWidth - s.outerWidth - , minE = options.east.minSize || 0 - , minW = options.west.minSize || 0 - , sizeE = state.east.size - , sizeW = state.west.size - , newE = sizeE - , newW = sizeW - ; - if (reqPx > 0 && state.east.isVisible && sizeE > minE) { - newE = max( sizeE-minE, sizeE-reqPx ); - reqPx -= sizeE-newE; - } - if (reqPx > 0 && state.west.isVisible && sizeW > minW) { - newW = max( sizeW-minW, sizeW-reqPx ); - reqPx -= sizeW-newW; - } - // IF we found enough extra space, then resize the border panes as calculated - if (reqPx === 0) { - if (sizeE && sizeE != minE) - sizePane('east', newE, true, true, force); // true = skipCallback/noAnimation - initPanes will handle when done - if (sizeW && sizeW != minW) - sizePane('west', newW, true, true, force); // true = skipCallback/noAnimation - // now start over! - sizeMidPanes('center', skipCallback, force); - $P.css(visCSS); - return; // abort this loop - } - } - } - else { // for east and west, set only the height, which is same as center height - // set state.min/maxWidth/Height for makePaneFit() logic - if (s.isVisible && !s.noVerticalRoom) - $.extend(s, elDims($P), cssMinDims(pane)) - if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight) { - $P.css(visCSS); - return true; // SKIP - pane already the correct size - } - // east/west have same top, bottom & height as center - CSS.top = newCenter.top; - CSS.bottom = newCenter.bottom; - s.newSize = newCenter.height - // NEW - allow pane to extend 'below' visible area rather than hide it - CSS.height = cssH($P, newCenter.height); - s.maxHeight = CSS.height; - hasRoom = (s.maxHeight >= 0); // ALWAYS TRUE NOW - if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic - } - - if (hasRoom) { - // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized - if (!skipCallback && state.initialized) - _runCallbacks("onresize_start", pane); - - $P.css(CSS); // apply the CSS to pane - if (pane !== "center") - sizeHandles(pane); // also update resizer length - if (s.noRoom && !s.isClosed && !s.isHidden) - makePaneFit(pane); // will re-open/show auto-closed/hidden pane - if (s.isVisible) { - $.extend(s, elDims($P)); // update pane dimensions - if (state.initialized) sizeContent(pane); // also resize the contents, if exists - } - } - else if (!s.noRoom && s.isVisible) // no room for pane - makePaneFit(pane); // will hide or close pane - - // reset visibility, if necessary - $P.css(visCSS); - - delete s.newSize; - delete s.newWidth; - delete s.newHeight; - - if (!s.isVisible) - return true; // DONE - next pane - - /* - * 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 - * ALSO required when pane is an IFRAME because will NOT default to 'full width' - * TODO: Can I use width:100% for a north/south iframe? - * TODO: Sounds like a job for $P.outerWidth( sC.innerWidth ) SETTER METHOD - */ - if (pane === "center") { // finished processing midPanes - var fix = browser.isIE6 || !browser.boxModel; - if ($Ps.north && (fix || state.north.tagName=="IFRAME")) - $Ps.north.css("width", cssW($Ps.north, sC.innerWidth)); - if ($Ps.south && (fix || state.south.tagName=="IFRAME")) - $Ps.south.css("width", cssW($Ps.south, sC.innerWidth)); - } - - // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized - if (!skipCallback && state.initialized) - _runCallbacks("onresize_end", pane); - }); - } - - - /** - * @see window.onresize(), callbacks or custom code - * @param {(Object|boolean)=} evt_or_refresh If 'true', then also reset pane-positioning - */ -, resizeAll = function (evt_or_refresh) { - var oldW = sC.innerWidth - , oldH = sC.innerHeight - ; - // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility - evtPane(evt_or_refresh); - - // cannot size layout when 'container' is hidden or collapsed - if (!$N.is(":visible")) return; - - if (!state.initialized) { - _initLayoutElements(); - return; // no need to resize since we just initialized! - } - - if (evt_or_refresh === true && $.isPlainObject(options.outset)) { - // update container CSS in case outset option has changed - $N.css( options.outset ); - } - // UPDATE container dimensions - $.extend(sC, elDims( $N, options.inset )); - if (!sC.outerHeight) return; - - // if 'true' passed, refresh pane & handle positioning too - if (evt_or_refresh === true) { - setPanePosition(); - } - - // onresizeall_start will CANCEL resizing if returns false - // state.container has already been set, so user can access this info for calcuations - if (false === _runCallbacks("onresizeall_start")) return false; - - var // see if container is now 'smaller' than before - shrunkH = (sC.innerHeight < oldH) - , shrunkW = (sC.innerWidth < oldW) - , $P, o, s - ; - // NOTE special order for sizing: S-N-E-W - $.each(["south","north","east","west"], function (i, pane) { - if (!$Ps[pane]) return; // no pane - SKIP - o = options[pane]; - s = state[pane]; - if (s.autoResize && s.size != o.size) // resize pane to original size set in options - sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize - else { - setSizeLimits(pane); - makePaneFit(pane, false, true, true); // true=skipCallback/forceResize - } - }); - - sizeMidPanes("", true, true); // true=skipCallback/forceResize - sizeHandles(); // reposition the toggler elements - - // trigger all individual pane callbacks AFTER layout has finished resizing - $.each(_c.allPanes, function (i, pane) { - $P = $Ps[pane]; - if (!$P) return; // SKIP - if (state[pane].isVisible) // undefined for non-existent panes - _runCallbacks("onresize_end", pane); // callback - if exists - }); - - _runCallbacks("onresizeall_end"); - //_triggerLayoutEvent(pane, 'resizeall'); - } - - /** - * Whenever a pane resizes or opens that has a nested layout, trigger resizeAll - * - * @param {(string|Object)} evt_or_pane The pane just resized or opened - */ -, resizeChildren = function (evt_or_pane, skipRefresh) { - var pane = evtPane.call(this, evt_or_pane); - - if (!options[pane].resizeChildren) return; - - // ensure the pane-children are up-to-date - if (!skipRefresh) refreshChildren( pane ); - var pC = children[pane]; - if ($.isPlainObject( pC )) { - // resize one or more children - $.each( pC, function (key, child) { - if (!child.destroyed) child.resizeAll(); - }); - } - } - - /** - * IF pane has a content-div, then resize all elements inside pane to fit pane-height - * - * @param {(string|Object)} evt_or_panes The pane(s) being resized - * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured? - */ -, sizeContent = function (evt_or_panes, remeasure) { - if (!isInitialized()) return; - - var panes = evtPane.call(this, evt_or_panes); - panes = panes ? panes.split(",") : _c.allPanes; - - $.each(panes, function (idx, pane) { - var - $P = $Ps[pane] - , $C = $Cs[pane] - , o = options[pane] - , s = state[pane] - , m = s.content // m = measurements - ; - if (!$P || !$C || !$P.is(":visible")) return true; // NOT VISIBLE - skip - - // if content-element was REMOVED, update OR remove the pointer - if (!$C.length) { - initContent(pane, false); // false = do NOT sizeContent() - already there! - if (!$C) return; // no replacement element found - pointer have been removed - } - - // onsizecontent_start will CANCEL resizing if returns false - if (false === _runCallbacks("onsizecontent_start", pane)) return; - - // skip re-measuring offsets if live-resizing - if ((!s.isMoving && !s.isResizing) || o.liveContentResizing || remeasure || m.top == undefined) { - _measure(); - // if any footers are below pane-bottom, they may not measure correctly, - // so allow pane overflow and re-measure - if (m.hiddenFooters > 0 && $P.css("overflow") === "hidden") { - $P.css("overflow", "visible"); - _measure(); // remeasure while overflowing - $P.css("overflow", "hidden"); - } - } - // NOTE: spaceAbove/Below *includes* the pane paddingTop/Bottom, but not pane.borders - var newH = s.innerHeight - (m.spaceAbove - s.css.paddingTop) - (m.spaceBelow - s.css.paddingBottom); - - if (!$C.is(":visible") || m.height != newH) { - // size the Content element to fit new pane-size - will autoHide if not enough room - setOuterHeight($C, newH, true); // true=autoHide - m.height = newH; // save new height - }; - - if (state.initialized) - _runCallbacks("onsizecontent_end", pane); - - function _below ($E) { - return max(s.css.paddingBottom, (parseInt($E.css("marginBottom"), 10) || 0)); - }; - - function _measure () { - var - ignore = options[pane].contentIgnoreSelector - , $Fs = $C.nextAll().not(".ui-layout-mask").not(ignore || ":lt(0)") // not :lt(0) = ALL - , $Fs_vis = $Fs.filter(':visible') - , $F = $Fs_vis.filter(':last') - ; - m = { - top: $C[0].offsetTop - , height: $C.outerHeight() - , numFooters: $Fs.length - , hiddenFooters: $Fs.length - $Fs_vis.length - , spaceBelow: 0 // correct if no content footer ($E) - } - m.spaceAbove = m.top; // just for state - not used in calc - m.bottom = m.top + m.height; - if ($F.length) - //spaceBelow = (LastFooter.top + LastFooter.height) [footerBottom] - Content.bottom + max(LastFooter.marginBottom, pane.paddingBotom) - m.spaceBelow = ($F[0].offsetTop + $F.outerHeight()) - m.bottom + _below($F); - else // no footer - check marginBottom on Content element itself - m.spaceBelow = _below($C); - }; - }); - } - - - /** - * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary - * - * @see initHandles(), open(), close(), resizeAll() - * @param {(string|Object)=} evt_or_panes The pane(s) being resized - */ -, sizeHandles = function (evt_or_panes) { - var panes = evtPane.call(this, evt_or_panes) - panes = panes ? panes.split(",") : _c.borderPanes; - - $.each(panes, function (i, pane) { - var - o = options[pane] - , s = state[pane] - , $P = $Ps[pane] - , $R = $Rs[pane] - , $T = $Ts[pane] - , $TC - ; - if (!$P || !$R) return; - - var - dir = _c[pane].dir - , _state = (s.isClosed ? "_closed" : "_open") - , spacing = o["spacing"+ _state] - , togAlign = o["togglerAlign"+ _state] - , togLen = o["togglerLength"+ _state] - , paneLen - , left - , 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(); // s.outerWidth || - paneLen = sC.innerWidth; // handle offscreen-panes - s.resizerLength = paneLen; - left = $.layout.cssNum($P, "left") - $R.css({ - width: cssW($R, paneLen) // account for borders & padding - , height: cssH($R, spacing) // ditto - , left: left > -9999 ? left : sC.inset.left // handle offscreen-panes - }); - } - else { // east/west - paneLen = $P.outerHeight(); // s.outerHeight || - s.resizerLength = paneLen; - $R.css({ - height: cssH($R, paneLen) // account for borders & padding - , width: cssW($R, spacing) // ditto - , top: sC.inset.top + getPaneSize("north", true) // TODO: what if no North pane? - //, top: $.layout.cssNum($Ps["center"], "top") - }); - } - - // remove hover classes - removeHover( o, $R ); - - 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 (isStr(togAlign)) { - switch (togAlign) { - case "top": - case "left": offset = 0; - break; - case "bottom": - case "right": offset = paneLen - togLen; - break; - case "middle": - case "center": - default: offset = round((paneLen - togLen) / 2); // 'default' catches typos - } - } - else { // togAlign = number - var x = parseInt(togAlign, 10); // - if (togAlign >= 0) offset = x; - else offset = paneLen - togLen + x; // NOTE: x is negative! - } - } - - if (dir === "horz") { // north/south - var width = cssW($T, togLen); - $T.css({ - width: width // account for borders & padding - , height: cssH($T, spacing) // ditto - , left: offset // TODO: VERIFY that toggler positions correctly for ALL values - , top: 0 - }); - // CENTER the toggler content SPAN -// $T.children(".content").each(function(){ -// $T.children(".ui-content").each(function(){ // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 -// $TC = $(this); -// $TC.css("marginLeft", round((width-$TC.outerWidth())/2)); // could be negative -// }); - // ThinkGem 以上4行代码不需要,因为在css里会自动居中 - } - else { // east/west - var height = cssH($T, togLen); - $T.css({ - height: height // account for borders & padding - , width: cssW($T, spacing) // ditto - , top: offset // POSITION the toggler - , left: 0 - }); - // CENTER the toggler content SPAN -// $T.children(".content").each(function(){ - $T.children(".ui-content").each(function(){ // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 - $TC = $(this); - $TC.css("marginTop", round((height-$TC.outerHeight())/2)); // could be negative - }); - } - - // remove ALL hover classes - removeHover( 0, $T ); - } - - // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now - if (!state.initialized && (o.initHidden || s.isHidden)) { - $R.hide(); - if ($T) $T.hide(); - } - }); - } - - - /** - * @param {(string|Object)} evt_or_pane - */ -, enableClosable = function (evt_or_pane) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $T = $Ts[pane] - , o = options[pane] - ; - if (!$T) return; - o.closable = true; - $T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); }) - .css("visibility", "visible") - .css("cursor", "pointer") - .attr("title", state[pane].isClosed ? o.tips.Open : o.tips.Close) // may be blank - .show(); - } - /** - * @param {(string|Object)} evt_or_pane - * @param {boolean=} [hide=false] - */ -, disableClosable = function (evt_or_pane, hide) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $T = $Ts[pane] - ; - if (!$T) return; - options[pane].closable = false; - // is closable is disable, then pane MUST be open! - if (state[pane].isClosed) open(pane, false, true); - $T .unbind("."+ sID) - .css("visibility", hide ? "hidden" : "visible") // instead of hide(), which creates logic issues - .css("cursor", "default") - .attr("title", ""); - } - - - /** - * @param {(string|Object)} evt_or_pane - */ -, enableSlidable = function (evt_or_pane) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $R = $Rs[pane] - ; - if (!$R || !$R.data('draggable')) return; - options[pane].slidable = true; - if (state[pane].isClosed) - bindStartSlidingEvents(pane, true); - } - /** - * @param {(string|Object)} evt_or_pane - */ -, disableSlidable = function (evt_or_pane) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $R = $Rs[pane] - ; - if (!$R) return; - options[pane].slidable = false; - if (state[pane].isSliding) - close(pane, false, true); - else { - bindStartSlidingEvents(pane, false); - $R .css("cursor", "default") - .attr("title", ""); - removeHover(null, $R[0]); // in case currently hovered - } - } - - - /** - * @param {(string|Object)} evt_or_pane - */ -, enableResizable = function (evt_or_pane) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $R = $Rs[pane] - , o = options[pane] - ; - if (!$R || !$R.data('draggable')) return; - o.resizable = true; - $R.draggable("enable"); - if (!state[pane].isClosed) - $R .css("cursor", o.resizerCursor) - .attr("title", o.tips.Resize); - } - /** - * @param {(string|Object)} evt_or_pane - */ -, disableResizable = function (evt_or_pane) { - if (!isInitialized()) return; - var pane = evtPane.call(this, evt_or_pane) - , $R = $Rs[pane] - ; - if (!$R || !$R.data('draggable')) return; - options[pane].resizable = false; - $R .draggable("disable") - .css("cursor", "default") - .attr("title", ""); - removeHover(null, $R[0]); // in case currently hovered - } - - - /** - * Move a pane from source-side (eg, west) to target-side (eg, east) - * If pane exists on target-side, move that to source-side, ie, 'swap' the panes - * - * @param {(string|Object)} evt_or_pane1 The pane/edge being swapped - * @param {string} pane2 ditto - */ -, swapPanes = function (evt_or_pane1, pane2) { - if (!isInitialized()) return; - var pane1 = evtPane.call(this, evt_or_pane1); - // change state.edge NOW so callbacks can know where pane is headed... - state[pane1].edge = pane2; - state[pane2].edge = pane1; - // run these even if NOT state.initialized - if (false === _runCallbacks("onswap_start", pane1) - || false === _runCallbacks("onswap_start", pane2) - ) { - state[pane1].edge = pane1; // reset - state[pane2].edge = pane2; - return; - } - - var - oPane1 = copy( pane1 ) - , oPane2 = copy( pane2 ) - , sizes = {} - ; - sizes[pane1] = oPane1 ? oPane1.state.size : 0; - sizes[pane2] = oPane2 ? oPane2.state.size : 0; - - // clear pointers & state - $Ps[pane1] = false; - $Ps[pane2] = false; - state[pane1] = {}; - state[pane2] = {}; - - // ALWAYS remove the resizer & toggler elements - if ($Ts[pane1]) $Ts[pane1].remove(); - if ($Ts[pane2]) $Ts[pane2].remove(); - if ($Rs[pane1]) $Rs[pane1].remove(); - if ($Rs[pane2]) $Rs[pane2].remove(); - $Rs[pane1] = $Rs[pane2] = $Ts[pane1] = $Ts[pane2] = false; - - // transfer element pointers and data to NEW Layout keys - move( oPane1, pane2 ); - move( oPane2, pane1 ); - - // cleanup objects - oPane1 = oPane2 = sizes = null; - - // make panes 'visible' again - if ($Ps[pane1]) $Ps[pane1].css(_c.visible); - if ($Ps[pane2]) $Ps[pane2].css(_c.visible); - - // fix any size discrepancies caused by swap - resizeAll(); - - // run these even if NOT state.initialized - _runCallbacks("onswap_end", pane1); - _runCallbacks("onswap_end", pane2); - - return; - - function copy (n) { // n = pane - var - $P = $Ps[n] - , $C = $Cs[n] - ; - return !$P ? false : { - pane: n - , P: $P ? $P[0] : false - , C: $C ? $C[0] : false - , state: $.extend(true, {}, state[n]) - , options: $.extend(true, {}, options[n]) - } - }; - - function move (oPane, pane) { - if (!oPane) return; - var - P = oPane.P - , C = oPane.C - , oldPane = oPane.pane - , c = _c[pane] - // save pane-options that should be retained - , s = $.extend(true, {}, state[pane]) - , o = options[pane] - // RETAIN side-specific FX Settings - more below - , fx = { resizerCursor: o.resizerCursor } - , re, size, pos - ; - $.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) { - fx[k +"_open"] = o[k +"_open"]; - fx[k +"_close"] = o[k +"_close"]; - fx[k +"_size"] = o[k +"_size"]; - }); - - // update object pointers and attributes - $Ps[pane] = $(P) - .data({ - layoutPane: Instance[pane] // NEW pointer to pane-alias-object - , layoutEdge: pane - }) - .css(_c.hidden) - .css(c.cssReq) - ; - $Cs[pane] = C ? $(C) : false; - - // set options and state - options[pane] = $.extend(true, {}, oPane.options, fx); - state[pane] = $.extend(true, {}, oPane.state); - - // change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west - re = new RegExp(o.paneClass +"-"+ oldPane, "g"); - P.className = P.className.replace(re, o.paneClass +"-"+ pane); - - // ALWAYS regenerate the resizer & toggler elements - initHandles(pane); // create the required resizer & toggler - - // if moving to different orientation, then keep 'target' pane size - if (c.dir != _c[oldPane].dir) { - size = sizes[pane] || 0; - setSizeLimits(pane); // update pane-state - size = max(size, state[pane].minSize); - // use manualSizePane to disable autoResize - not useful after panes are swapped - manualSizePane(pane, size, true, true); // true/true = skipCallback/noAnimation - } - else // move the resizer here - $Rs[pane].css(c.side, sC.inset[c.side] + (state[pane].isVisible ? getPaneSize(pane) : 0)); - - - // ADD CLASSNAMES & SLIDE-BINDINGS - if (oPane.state.isVisible && !s.isVisible) - setAsOpen(pane, true); // true = skipCallback - else { - setAsClosed(pane); - bindStartSlidingEvents(pane, true); // will enable events IF option is set - } - - // DESTROY the object - oPane = null; - }; - } - - - /** - * INTERNAL method to sync pin-buttons when pane is opened or closed - * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes - * - * @see open(), setAsOpen(), setAsClosed() - * @param {string} pane These are the params returned to callbacks by layout() - * @param {boolean} doPin True means set the pin 'down', False means 'up' - */ -, syncPinBtns = function (pane, doPin) { - if ($.layout.plugins.buttons) - $.each(state[pane].pins, function (i, selector) { - $.layout.buttons.setPinState(Instance, $(selector), pane, doPin); - }); - } - -; // END var DECLARATIONS - - /** - * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed - * - * @see 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 - $.ui.keyCode.UP - , 40: "south" // Down Cursor - $.ui.keyCode.DOWN - , 37: "west" // Left Cursor - $.ui.keyCode.LEFT - , 39: "east" // Right Cursor - $.ui.keyCode.RIGHT - } - , ALT = evt.altKey // no worky! - , SHIFT = evt.shiftKey - , CTRL = evt.ctrlKey - , CURSOR = (CTRL && code >= 37 && code <= 40) - , o, k, m, pane - ; - - if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey - pane = PANE[code]; - else if (CTRL || SHIFT) // check to see if this matches a custom-hotkey - $.each(_c.borderPanes, 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 - } - } - }); - - // validate pane - if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden) - return true; - - toggle(pane); - - evt.stopPropagation(); - evt.returnValue = false; // CANCEL key - return false; - }; - - -/* - * ###################################### - * UTILITY METHODS - * called externally or by initButtons - * ###################################### - */ - - /** - * Change/reset a pane overflow setting & zIndex to allow popups/drop-downs to work - * - * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event - */ - function allowOverflow (el) { - if (!isInitialized()) return; - if (this && this.tagName) el = this; // BOUND to element - var $P; - if (isStr(el)) - $P = $Ps[el]; - else if ($(el).data("layoutRole")) - $P = $(el); - else - $(el).parents().each(function(){ - if ($(this).data("layoutRole")) { - $P = $(this); - return false; // BREAK - } - }); - if (!$P || !$P.length) return; // INVALID - - var - pane = $P.data("layoutEdge") - , 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 its closed, then abort - if (s.isSliding || s.isResizing || s.isClosed) { - s.cssSaved = false; - return; - } - - var - newCSS = { zIndex: (options.zIndexes.resizer_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.match(/(visible|auto)/)) { - curCSS.overflowX = ofX; - newCSS.overflowX = "visible"; - } - if (ofY && !ofY.match(/(visible|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, function(i, p) { - if (p != pane) resetOverflow(p); - }); - - }; - /** - * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event - */ - function resetOverflow (el) { - if (!isInitialized()) return; - if (this && this.tagName) el = this; // BOUND to element - var $P; - if (isStr(el)) - $P = $Ps[el]; - else if ($(el).data("layoutRole")) - $P = $(el); - else - $(el).parents().each(function(){ - if ($(this).data("layoutRole")) { - $P = $(this); - return false; // BREAK - } - }); - if (!$P || !$P.length) return; // INVALID - - var - pane = $P.data("layoutEdge") - , s = state[pane] - , CSS = s.cssSaved || {} - ; - // reset the zIndex - if (!s.isSliding && !s.isResizing) - $P.css("zIndex", options.zIndexes.pane_normal); - - // reset Overflow - if necessary - $P.css( CSS ); - - // clear var - s.cssSaved = false; - }; - -/* - * ##################### - * CREATE/RETURN LAYOUT - * ##################### - */ - - // validate that container exists - var $N = $(this).eq(0); // FIRST matching Container element - if (!$N.length) { - return _log( options.errors.containerMissing ); - }; - - // Users retrieve Instance of a layout with: $N.layout() OR $N.data("layout") - // return the Instance-pointer if layout has already been initialized - if ($N.data("layoutContainer") && $N.data("layout")) - return $N.data("layout"); // cached pointer - - // init global vars - var - $Ps = {} // Panes x5 - set in initPanes() - , $Cs = {} // Content x5 - set in initPanes() - , $Rs = {} // Resizers x4 - set in initHandles() - , $Ts = {} // Togglers x4 - set in initHandles() - , $Ms = $([]) // Masks - up to 2 masks per pane (IFRAME + DIV) - // aliases for code brevity - , sC = state.container // alias for easy access to 'container dimensions' - , sID = state.id // alias for unique layout ID/namespace - eg: "layout435" - ; - - // create Instance object to expose data & option Properties, and primary action Methods - var Instance = { - // layout data - options: options // property - options hash - , state: state // property - dimensions hash - // object pointers - , container: $N // property - object pointers for layout container - , panes: $Ps // property - object pointers for ALL Panes: panes.north, panes.center - , contents: $Cs // property - object pointers for ALL Content: contents.north, contents.center - , resizers: $Rs // property - object pointers for ALL Resizers, eg: resizers.north - , togglers: $Ts // property - object pointers for ALL Togglers, eg: togglers.north - // border-pane open/close - , hide: hide // method - ditto - , show: show // method - ditto - , toggle: toggle // method - pass a 'pane' ("north", "west", etc) - , open: open // method - ditto - , close: close // method - ditto - , slideOpen: slideOpen // method - ditto - , slideClose: slideClose // method - ditto - , slideToggle: slideToggle // method - ditto - // pane actions - , setSizeLimits: setSizeLimits // method - pass a 'pane' - update state min/max data - , _sizePane: sizePane // method -intended for user by plugins only! - , sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto' - , sizeContent: sizeContent // method - pass a 'pane' - , swapPanes: swapPanes // method - pass TWO 'panes' - will swap them - , showMasks: showMasks // method - pass a 'pane' OR list of panes - default = all panes with mask option set - , hideMasks: hideMasks // method - ditto' - // pane element methods - , initContent: initContent // method - ditto - , addPane: addPane // method - pass a 'pane' - , removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem - , createChildren: createChildren // method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].children - , refreshChildren: refreshChildren // method - pass a 'pane' and a layout-instance - // special pane option setting - , enableClosable: enableClosable // method - pass a 'pane' - , disableClosable: disableClosable // method - ditto - , enableSlidable: enableSlidable // method - ditto - , disableSlidable: disableSlidable // method - ditto - , enableResizable: enableResizable // method - ditto - , disableResizable: disableResizable// method - ditto - // utility methods for panes - , allowOverflow: allowOverflow // utility - pass calling element (this) - , resetOverflow: resetOverflow // utility - ditto - // layout control - , destroy: destroy // method - no parameters - , initPanes: isInitialized // method - no parameters - , resizeAll: resizeAll // method - no parameters - // callback triggering - , runCallbacks: _runCallbacks // method - pass evtName & pane (if a pane-event), eg: trigger("onopen", "west") - // alias collections of options, state and children - created in addPane and extended elsewhere - , hasParentLayout: false // set by initContainer() - , children: children // pointers to child-layouts, eg: Instance.children.west.layoutName - , north: false // alias group: { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], children: children[pane] } - , south: false // ditto - , west: false // ditto - , east: false // ditto - , center: false // ditto - }; - - // create the border layout NOW - if (_create() === 'cancel') // onload_start callback returned false to CANCEL layout creation - return null; - else // true OR false -- if layout-elements did NOT init (hidden or do not exist), can auto-init later - return Instance; // return the Instance object - -} - - -})( jQuery ); - - - - -/** - * jquery.layout.state 1.2 - * $Date: 2014-08-30 08:00:00 (Sat, 30 Aug 2014) $ - * - * Copyright (c) 2014 - * Kevin Dalman (http://allpro.net) - * - * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) - * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. - * - * @requires: UI Layout 1.4.0 or higher - * @requires: $.ui.cookie (above) - * - * @see: http://groups.google.com/group/jquery-ui-layout - */ -;(function ($) { - -if (!$.layout) return; - - -/** - * UI COOKIE UTILITY - * - * A $.cookie OR $.ui.cookie namespace *should be standard*, but until then... - * This creates $.ui.cookie so Layout does not need the cookie.jquery.js plugin - * NOTE: This utility is REQUIRED by the layout.state plugin - * - * Cookie methods in Layout are created as part of State Management - */ -if (!$.ui) $.ui = {}; -$.ui.cookie = { - - // cookieEnabled is not in DOM specs, but DOES works in all browsers,including IE6 - acceptsCookies: !!navigator.cookieEnabled - -, read: function (name) { - var - c = document.cookie - , cs = c ? c.split(';') : [] - , pair, data, i - ; - for (i=0; pair=cs[i]; i++) { - data = $.trim(pair).split('='); // name=value => [ name, value ] - if (data[0] == name) // found the layout cookie - return decodeURIComponent(data[1]); - } - return null; - } - -, write: function (name, val, cookieOpts) { - var params = "" - , date = "" - , clear = false - , o = cookieOpts || {} - , x = o.expires || null - , t = $.type(x) - ; - if (t === "date") - date = x; - else if (t === "string" && x > 0) { - x = parseInt(x,10); - t = "number"; - } - if (t === "number") { - date = new Date(); - if (x > 0) - date.setDate(date.getDate() + x); - else { - date.setFullYear(1970); - clear = true; - } - } - if (date) params += ";expires="+ date.toUTCString(); - if (o.path) params += ";path="+ o.path; - if (o.domain) params += ";domain="+ o.domain; - if (o.secure) params += ";secure"; - document.cookie = name +"="+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie - } - -, clear: function (name) { - $.ui.cookie.write(name, "", {expires: -1}); - } - -}; -// if cookie.jquery.js is not loaded, create an alias to replicate it -// this may be useful to other plugins or code dependent on that plugin -if (!$.cookie) $.cookie = function (k, v, o) { - var C = $.ui.cookie; - if (v === null) - C.clear(k); - else if (v === undefined) - return C.read(k); - else - C.write(k, v, o); -}; - - - -/** - * State-management options stored in options.stateManagement, which includes a .cookie hash - * Default options saves ALL KEYS for ALL PANES, ie: pane.size, pane.isClosed, pane.isHidden - * - * // STATE/COOKIE OPTIONS - * @example $(el).layout({ - stateManagement: { - enabled: true - , stateKeys: "east.size,west.size,east.isClosed,west.isClosed" - , cookie: { name: "appLayout", path: "/" } - } - }) - * @example $(el).layout({ stateManagement__enabled: true }) // enable auto-state-management using cookies - * @example $(el).layout({ stateManagement__cookie: { name: "appLayout", path: "/" } }) - * @example $(el).layout({ stateManagement__cookie__name: "appLayout", stateManagement__cookie__path: "/" }) - * - * // STATE/COOKIE METHODS - * @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} ); - * @example myLayout.loadCookie(); - * @example myLayout.deleteCookie(); - * @example var JSON = myLayout.readState(); // CURRENT Layout State - * @example var JSON = myLayout.readCookie(); // SAVED Layout State (from cookie) - * @example var JSON = myLayout.state.stateData; // LAST LOADED Layout State (cookie saved in layout.state hash) - * - * CUSTOM STATE-MANAGEMENT (eg, saved in a database) - * @example var JSON = myLayout.readState( "west.isClosed,north.size,south.isHidden" ); - * @example myLayout.loadState( JSON ); - */ - -// tell Layout that the state plugin is available -$.layout.plugins.stateManagement = true; - -// Add State-Management options to layout.defaults -$.layout.defaults.stateManagement = { - enabled: false // true = enable state-management, even if not using cookies -, autoSave: true // Save a state-cookie when page exits? -, autoLoad: true // Load the state-cookie when Layout inits? -, animateLoad: true // animate panes when loading state into an active layout -, includeChildren: true // recurse into child layouts to include their state as well - // List state-data to save - must be pane-specific -, stateKeys: "north.size,south.size,east.size,west.size,"+ - "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+ - "north.isHidden,south.isHidden,east.isHidden,west.isHidden" -, cookie: { - name: "" // If not specified, will use Layout.name, else just "Layout" - , domain: "" // blank = current domain - , path: "" // blank = current page, "/" = entire website - , expires: "" // 'days' to keep cookie - leave blank for 'session cookie' - , secure: false - } -}; - -// Set stateManagement as a 'layout-option', NOT a 'pane-option' -$.layout.optionsMap.layout.push("stateManagement"); -// Update config so layout does not move options into the pane-default branch (panes) -$.layout.config.optionRootKeys.push("stateManagement"); - -/* - * State Management methods - */ -$.layout.state = { - - /** - * Get the current layout state and save it to a cookie - * - * myLayout.saveCookie( keys, cookieOpts ) - * - * @param {Object} inst - * @param {(string|Array)=} keys - * @param {Object=} cookieOpts - */ - saveCookie: function (inst, keys, cookieOpts) { - var o = inst.options - , sm = o.stateManagement - , oC = $.extend(true, {}, sm.cookie, cookieOpts || null) - , data = inst.state.stateData = inst.readState( keys || sm.stateKeys ) // read current panes-state - ; - $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC ); - return $.extend(true, {}, data); // return COPY of state.stateData data - } - - /** - * Remove the state cookie - * - * @param {Object} inst - */ -, deleteCookie: function (inst) { - var o = inst.options; - $.ui.cookie.clear( o.stateManagement.cookie.name || o.name || "Layout" ); - } - - /** - * Read & return data from the cookie - as JSON - * - * @param {Object} inst - */ -, readCookie: function (inst) { - var o = inst.options; - var c = $.ui.cookie.read( o.stateManagement.cookie.name || o.name || "Layout" ); - // convert cookie string back to a hash and return it - return c ? $.layout.state.decodeJSON(c) : {}; - } - - /** - * Get data from the cookie and USE IT to loadState - * - * @param {Object} inst - */ -, loadCookie: function (inst) { - var c = $.layout.state.readCookie(inst); // READ the cookie - if (c && !$.isEmptyObject( c )) { - inst.state.stateData = $.extend(true, {}, c); // SET state.stateData - inst.loadState(c); // LOAD the retrieved state - } - return c; - } - - /** - * Update layout options from the cookie, if one exists - * - * @param {Object} inst - * @param {Object=} stateData - * @param {boolean=} animate - */ -, loadState: function (inst, data, opts) { - if (!$.isPlainObject( data ) || $.isEmptyObject( data )) return; - - // normalize data & cache in the state object - data = inst.state.stateData = $.layout.transformData( data ); // panes = default subkey - - // add missing/default state-restore options - var smo = inst.options.stateManagement; - opts = $.extend({ - animateLoad: false //smo.animateLoad - , includeChildren: smo.includeChildren - }, opts ); - - if (!inst.state.initialized) { - /* - * layout NOT initialized, so just update its options - */ - // MUST remove pane.children keys before applying to options - // use a copy so we don't remove keys from original data - var o = $.extend(true, {}, data); - //delete o.center; // center has no state-data - only children - $.each($.layout.config.allPanes, function (idx, pane) { - if (o[pane]) delete o[pane].children; - }); - // update CURRENT layout-options with saved state data - $.extend(true, inst.options, o); - } - else { - /* - * layout already initialized, so modify layout's configuration - */ - var noAnimate = !opts.animateLoad - , o, c, h, state, open - ; - $.each($.layout.config.borderPanes, function (idx, pane) { - o = data[ pane ]; - if (!$.isPlainObject( o )) return; // no key, skip pane - - s = o.size; - c = o.initClosed; - h = o.initHidden; - ar = o.autoResize - state = inst.state[pane]; - open = state.isVisible; - - // reset autoResize - if (ar) - state.autoResize = ar; - // resize BEFORE opening - if (!open) - inst._sizePane(pane, s, false, false, false); // false=skipCallback/noAnimation/forceResize - // open/close as necessary - DO NOT CHANGE THIS ORDER! - if (h === true) inst.hide(pane, noAnimate); - else if (c === true) inst.close(pane, false, noAnimate); - else if (c === false) inst.open (pane, false, noAnimate); - else if (h === false) inst.show (pane, false, noAnimate); - // resize AFTER any other actions - if (open) - inst._sizePane(pane, s, false, false, noAnimate); // animate resize if option passed - }); - - /* - * RECURSE INTO CHILD-LAYOUTS - */ - if (opts.includeChildren) { - var paneStateChildren, childState; - $.each(inst.children, function (pane, paneChildren) { - paneStateChildren = data[pane] ? data[pane].children : 0; - if (paneStateChildren && paneChildren) { - $.each(paneChildren, function (stateKey, child) { - childState = paneStateChildren[stateKey]; - if (child && childState) - child.loadState( childState ); - }); - } - }); - } - } - } - - /** - * Get the *current layout state* and return it as a hash - * - * @param {Object=} inst // Layout instance to get state for - * @param {object=} [opts] // State-Managements override options - */ -, readState: function (inst, opts) { - // backward compatility - if ($.type(opts) === 'string') opts = { keys: opts }; - if (!opts) opts = {}; - var sm = inst.options.stateManagement - , ic = opts.includeChildren - , recurse = ic !== undefined ? ic : sm.includeChildren - , keys = opts.stateKeys || sm.stateKeys - , alt = { isClosed: 'initClosed', isHidden: 'initHidden' } - , state = inst.state - , panes = $.layout.config.allPanes - , data = {} - , pair, pane, key, val - , ps, pC, child, array, count, branch - ; - if ($.isArray(keys)) keys = keys.join(","); - // convert keys to an array and change delimiters from '__' to '.' - keys = keys.replace(/__/g, ".").split(','); - // loop keys and create a data hash - for (var i=0, n=keys.length; i < n; i++) { - pair = keys[i].split("."); - pane = pair[0]; - key = pair[1]; - if ($.inArray(pane, panes) < 0) continue; // bad pane! - val = state[ pane ][ key ]; - if (val == undefined) continue; - if (key=="isClosed" && state[pane]["isSliding"]) - val = true; // if sliding, then *really* isClosed - ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val; - } - - // recurse into the child-layouts for each pane - if (recurse) { - $.each(panes, function (idx, pane) { - pC = inst.children[pane]; - ps = state.stateData[pane]; - if ($.isPlainObject( pC ) && !$.isEmptyObject( pC )) { - // ensure a key exists for this 'pane', eg: branch = data.center - branch = data[pane] || (data[pane] = {}); - if (!branch.children) branch.children = {}; - $.each( pC, function (key, child) { - // ONLY read state from an initialize layout - if ( child.state.initialized ) - branch.children[ key ] = $.layout.state.readState( child ); - // if we have PREVIOUS (onLoad) state for this child-layout, KEEP IT! - else if ( ps && ps.children && ps.children[ key ] ) { - branch.children[ key ] = $.extend(true, {}, ps.children[ key ] ); - } - }); - } - }); - } - - return data; - } - - /** - * Stringify a JSON hash so can save in a cookie or db-field - */ -, encodeJSON: function (json) { - var local = window.JSON || {}; - return (local.stringify || stringify)(json); - - function stringify (h) { - var D=[], i=0, k, v, t // k = key, v = value - , a = $.isArray(h) - ; - for (k in h) { - v = h[k]; - t = typeof v; - if (t == 'string') // STRING - add quotes - v = '"'+ v +'"'; - else if (t == 'object') // SUB-KEY - recurse into it - v = parse(v); - D[i++] = (!a ? '"'+ k +'":' : '') + v; - } - return (a ? '[' : '{') + D.join(',') + (a ? ']' : '}'); - }; - } - - /** - * Convert stringified JSON back to a hash object - * @see $.parseJSON(), adding in jQuery 1.4.1 - */ -, decodeJSON: function (str) { - try { return $.parseJSON ? $.parseJSON(str) : window["eval"]("("+ str +")") || {}; } - catch (e) { return {}; } - } - - -, _create: function (inst) { - var s = $.layout.state - , o = inst.options - , sm = o.stateManagement - ; - // ADD State-Management plugin methods to inst - $.extend( inst, { - // readCookie - update options from cookie - returns hash of cookie data - readCookie: function () { return s.readCookie(inst); } - // deleteCookie - , deleteCookie: function () { s.deleteCookie(inst); } - // saveCookie - optionally pass keys-list and cookie-options (hash) - , saveCookie: function (keys, cookieOpts) { return s.saveCookie(inst, keys, cookieOpts); } - // loadCookie - readCookie and use to loadState() - returns hash of cookie data - , loadCookie: function () { return s.loadCookie(inst); } - // loadState - pass a hash of state to use to update options - , loadState: function (stateData, opts) { s.loadState(inst, stateData, opts); } - // readState - returns hash of current layout-state - , readState: function (keys) { return s.readState(inst, keys); } - // add JSON utility methods too... - , encodeJSON: s.encodeJSON - , decodeJSON: s.decodeJSON - }); - - // init state.stateData key, even if plugin is initially disabled - inst.state.stateData = {}; - - // autoLoad MUST BE one of: data-array, data-hash, callback-function, or TRUE - if ( !sm.autoLoad ) return; - - // When state-data exists in the autoLoad key USE IT, - // even if stateManagement.enabled == false - if ($.isPlainObject( sm.autoLoad )) { - if (!$.isEmptyObject( sm.autoLoad )) { - inst.loadState( sm.autoLoad ); - } - } - else if ( sm.enabled ) { - // update the options from cookie or callback - // if options is a function, call it to get stateData - if ($.isFunction( sm.autoLoad )) { - var d = {}; - try { - d = sm.autoLoad( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn - } catch (e) {} - if (d && $.isPlainObject( d ) && !$.isEmptyObject( d )) - inst.loadState(d); - } - else // any other truthy value will trigger loadCookie - inst.loadCookie(); - } - } - -, _unload: function (inst) { - var sm = inst.options.stateManagement; - if (sm.enabled && sm.autoSave) { - // if options is a function, call it to save the stateData - if ($.isFunction( sm.autoSave )) { - try { - sm.autoSave( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn - } catch (e) {} - } - else // any truthy value will trigger saveCookie - inst.saveCookie(); - } - } - -}; - -// add state initialization method to Layout's onCreate array of functions -$.layout.onCreate.push( $.layout.state._create ); -$.layout.onUnload.push( $.layout.state._unload ); - -})( jQuery ); - - - -/** - * @preserve jquery.layout.buttons 1.0 - * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $ - * - * Copyright (c) 2011 - * Kevin Dalman (http://allpro.net) - * - * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) - * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. - * - * @dependancies: UI Layout 1.3.0.rc30.1 or higher - * - * @support: http://groups.google.com/group/jquery-ui-layout - * - * Docs: [ to come ] - * Tips: [ to come ] - */ -;(function ($) { - -if (!$.layout) return; - - -// tell Layout that the state plugin is available -$.layout.plugins.buttons = true; - -// Add State-Management options to layout.defaults -$.layout.defaults.autoBindCustomButtons = false; -// Set stateManagement as a layout-option, NOT a pane-option -$.layout.optionsMap.layout.push("autoBindCustomButtons"); - -/* - * Button methods - */ -$.layout.buttons = { - // set data used by multiple methods below - config: { - borderPanes: "north,south,west,east" - } - - /** - * Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons - * - * @see _create() - */ -, init: function (inst) { - var pre = "ui-layout-button-" - , layout = inst.options.name || "" - , name; - $.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) { - $.each($.layout.buttons.config.borderPanes.split(","), function (ii, pane) { - $("."+pre+action+"-"+pane).each(function(){ - // if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name' - name = $(this).data("layoutName") || $(this).attr("layoutName"); - if (name == undefined || name === layout) - inst.bindButton(this, action, pane); - }); - }); - }); - } - - /** - * Helper function to validate params received by addButton utilities - * - * Two classes are added to the element, based on the buttonClass... - * The type of button is appended to create the 2nd className: - * - ui-layout-button-pin - * - ui-layout-pane-button-toggle - * - ui-layout-pane-button-open - * - ui-layout-pane-button-close - * - * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" - * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. - * @return {Array.} If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise returns null - */ -, get: function (inst, selector, pane, action) { - var $E = $(selector) - , o = inst.options - //, err = o.showErrorMessages - ; - if ($E.length && $.layout.buttons.config.borderPanes.indexOf(pane) >= 0) { - var btn = o[pane].buttonClass +"-"+ action; - $E .addClass( btn +" "+ btn +"-"+ pane ) - .data("layoutName", o.name); // add layout identifier - even if blank! - } - return $E; - } - - - /** - * NEW syntax for binding layout-buttons - will eventually replace addToggle, addOpen, etc. - * - * @param {(string|!Object)} sel jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" - * @param {string} action - * @param {string} pane - */ -, bind: function (inst, sel, action, pane) { - var _ = $.layout.buttons; - switch (action.toLowerCase()) { - case "toggle": _.addToggle (inst, sel, pane); break; - case "open": _.addOpen (inst, sel, pane); break; - case "close": _.addClose (inst, sel, pane); break; - case "pin": _.addPin (inst, sel, pane); break; - case "toggle-slide": _.addToggle (inst, sel, pane, true); break; - case "open-slide": _.addOpen (inst, sel, pane, true); break; - } - return inst; - } - - /** - * Add a custom Toggler button for a pane - * - * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" - * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. - * @param {boolean=} slide true = slide-open, false = pin-open - */ -, addToggle: function (inst, selector, pane, slide) { - $.layout.buttons.get(inst, selector, pane, "toggle") - .click(function(evt){ - inst.toggle(pane, !!slide); - evt.stopPropagation(); - }); - return inst; - } - - /** - * Add a custom Open button for a pane - * - * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" - * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. - * @param {boolean=} slide true = slide-open, false = pin-open - */ -, addOpen: function (inst, selector, pane, slide) { - $.layout.buttons.get(inst, selector, pane, "open") - .attr("title", inst.options[pane].tips.Open) - .click(function (evt) { - inst.open(pane, !!slide); - evt.stopPropagation(); - }); - return inst; - } - - /** - * Add a custom Close button for a pane - * - * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" - * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. - */ -, addClose: function (inst, selector, pane) { - $.layout.buttons.get(inst, selector, pane, "close") - .attr("title", inst.options[pane].tips.Close) - .click(function (evt) { - inst.close(pane); - evt.stopPropagation(); - }); - return inst; - } - - /** - * 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|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" - * @param {string} pane Name of the pane the pin is for: 'north', 'south', etc. - */ -, addPin: function (inst, selector, pane) { - var $E = $.layout.buttons.get(inst, selector, pane, "pin"); - if ($E.length) { - var s = inst.state[pane]; - $E.click(function (evt) { - $.layout.buttons.setPinState(inst, $(this), pane, (s.isSliding || s.isClosed)); - if (s.isSliding || s.isClosed) inst.open( pane ); // change from sliding to open - else inst.close( pane ); // slide-closed - evt.stopPropagation(); - }); - // add up/down pin attributes and classes - $.layout.buttons.setPinState(inst, $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 - s.pins.push( selector ); // just save the selector string - } - return inst; - } - - /** - * Change the class of the pin button to make it look 'up' or 'down' - * - * @see addPin(), syncPins() - * @param {Array.} $Pin The pin-span element in a jQuery wrapper - * @param {string} pane These are the params returned to callbacks by layout() - * @param {boolean} doPin true = set the pin 'down', false = set it 'up' - */ -, setPinState: function (inst, $Pin, pane, doPin) { - var updown = $Pin.attr("pin"); - if (updown && doPin === (updown=="down")) return; // already in correct state - var - po = inst.options[pane] - , lang = po.tips - , pin = po.buttonClass +"-pin" - , side = pin +"-"+ pane - , UP = pin +"-up "+ side +"-up" - , DN = pin +"-down "+side +"-down" - ; - $Pin - .attr("pin", doPin ? "down" : "up") // logic - .attr("title", doPin ? lang.Unpin : lang.Pin) - .removeClass( doPin ? UP : DN ) - .addClass( doPin ? DN : UP ) - ; - } - - /** - * 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 - * - * @see open(), close() - * @param {string} pane These are the params returned to callbacks by layout() - * @param {boolean} doPin True means set the pin 'down', False means 'up' - */ -, syncPinBtns: function (inst, pane, doPin) { - // REAL METHOD IS _INSIDE_ LAYOUT - THIS IS HERE JUST FOR REFERENCE - $.each(state[pane].pins, function (i, selector) { - $.layout.buttons.setPinState(inst, $(selector), pane, doPin); - }); - } - - -, _load: function (inst) { - // ADD Button methods to Layout Instance - $.extend( inst, { - bindButton: function (selector, action, pane) { return $.layout.buttons.bind(inst, selector, action, pane); } - // DEPRECATED METHODS... - , addToggleBtn: function (selector, pane, slide) { return $.layout.buttons.addToggle(inst, selector, pane, slide); } - , addOpenBtn: function (selector, pane, slide) { return $.layout.buttons.addOpen(inst, selector, pane, slide); } - , addCloseBtn: function (selector, pane) { return $.layout.buttons.addClose(inst, selector, pane); } - , addPinBtn: function (selector, pane) { return $.layout.buttons.addPin(inst, selector, pane); } - }); - - // init state array to hold pin-buttons - for (var i=0; i<4; i++) { - var pane = $.layout.buttons.config.borderPanes[i]; - inst.state[pane].pins = []; - } - - // auto-init buttons onLoad if option is enabled - if ( inst.options.autoBindCustomButtons ) - $.layout.buttons.init(inst); - } - -, _unload: function (inst) { - // TODO: unbind all buttons??? - } - -}; - -// add initialization method to Layout's onLoad array of functions -$.layout.onLoad.push( $.layout.buttons._load ); -//$.layout.onUnload.push( $.layout.buttons._unload ); - -})( jQuery ); - - - - -/** - * jquery.layout.browserZoom 1.0 - * $Date: 2011-12-29 08:00:00 (Thu, 29 Dec 2011) $ - * - * Copyright (c) 2012 - * Kevin Dalman (http://allpro.net) - * - * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) - * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. - * - * @requires: UI Layout 1.3.0.rc30.1 or higher - * - * @see: http://groups.google.com/group/jquery-ui-layout - * - * TODO: Extend logic to handle other problematic zooming in browsers - * TODO: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event - */ -(function ($) { - -// tell Layout that the plugin is available -$.layout.plugins.browserZoom = true; - -$.layout.defaults.browserZoomCheckInterval = 1000; -$.layout.optionsMap.layout.push("browserZoomCheckInterval"); - -/* - * browserZoom methods - */ -$.layout.browserZoom = { - - _init: function (inst) { - // abort if browser does not need this check - if ($.layout.browserZoom.ratio() !== false) - $.layout.browserZoom._setTimer(inst); - } - -, _setTimer: function (inst) { - // abort if layout destroyed or browser does not need this check - if (inst.destroyed) return; - var o = inst.options - , s = inst.state - // don't need check if inst has parentLayout, but check occassionally in case parent destroyed! - // MINIMUM 100ms interval, for performance - , ms = inst.hasParentLayout ? 5000 : Math.max( o.browserZoomCheckInterval, 100 ) - ; - // set the timer - setTimeout(function(){ - if (inst.destroyed || !o.resizeWithWindow) return; - var d = $.layout.browserZoom.ratio(); - if (d !== s.browserZoom) { - s.browserZoom = d; - inst.resizeAll(); - } - // set a NEW timeout - $.layout.browserZoom._setTimer(inst); - } - , ms ); - } - -, ratio: function () { - var w = window - , s = screen - , d = document - , dE = d.documentElement || d.body - , b = $.layout.browser - , v = b.version - , r, sW, cW - ; - // we can ignore all browsers that fire window.resize event onZoom - if (!b.msie || v > 8) - return false; // don't need to track zoom - if (s.deviceXDPI && s.systemXDPI) // syntax compiler hack - return calc(s.deviceXDPI, s.systemXDPI); - // everything below is just for future reference! - if (b.webkit && (r = d.body.getBoundingClientRect)) - return calc((r.left - r.right), d.body.offsetWidth); - if (b.webkit && (sW = w.outerWidth)) - return calc(sW, w.innerWidth); - if ((sW = s.width) && (cW = dE.clientWidth)) - return calc(sW, cW); - return false; // no match, so cannot - or don't need to - track zoom - - function calc (x,y) { return (parseInt(x,10) / parseInt(y,10) * 100).toFixed(); } - } - -}; -// add initialization method to Layout's onLoad array of functions -$.layout.onReady.push( $.layout.browserZoom._init ); - - -})( jQuery ); - - - - -/** - * UI Layout Plugin: Slide-Offscreen Animation - * - * Prevent panes from being 'hidden' so that an iframes/objects - * does not reload/refresh when pane 'opens' again. - * This plug-in adds a new animation called "slideOffscreen". - * It is identical to the normal "slide" effect, but avoids hiding the element - * - * Requires Layout 1.3.0.RC30.1 or later for Close offscreen - * Requires Layout 1.3.0.RC30.5 or later for Hide, initClosed & initHidden offscreen - * - * Version: 1.1 - 2012-11-18 - * Author: Kevin Dalman (kevin@jquery-dev.com) - * @preserve jquery.layout.slideOffscreen-1.1.js - */ -;(function ($) { - -// Add a new "slideOffscreen" effect -if ($.effects) { - - // add an option so initClosed and initHidden will work - $.layout.defaults.panes.useOffscreenClose = false; // user must enable when needed - /* set the new animation as the default for all panes - $.layout.defaults.panes.fxName = "slideOffscreen"; - */ - - if ($.layout.plugins) - $.layout.plugins.effects.slideOffscreen = true; - - // dupe 'slide' effect defaults as new effect defaults - $.layout.effects.slideOffscreen = $.extend(true, {}, $.layout.effects.slide); - - // add new effect to jQuery UI - $.effects.slideOffscreen = function(o) { - return this.queue(function(){ - - var fx = $.effects - , opt = o.options - , $el = $(this) - , pane = $el.data('layoutEdge') - , state = $el.data('parentLayout').state - , dist = state[pane].size - , s = this.style - , props = ['top','bottom','left','right'] - // Set options - , mode = fx.setMode($el, opt.mode || 'show') // Set Mode - , show = (mode == 'show') - , dir = opt.direction || 'left' // Default Direction - , ref = (dir == 'up' || dir == 'down') ? 'top' : 'left' - , pos = (dir == 'up' || dir == 'left') - , offscrn = $.layout.config.offscreenCSS || {} - , keyLR = $.layout.config.offscreenReset - , keyTB = 'offscreenResetTop' // only used internally - , animation = {} - ; - // Animation settings - animation[ref] = (show ? (pos ? '+=' : '-=') : (pos ? '-=' : '+=')) + dist; - - if (show) { // show() animation, so save top/bottom but retain left/right set when 'hidden' - $el.data(keyTB, { top: s.top, bottom: s.bottom }); - - // set the top or left offset in preparation for animation - // Note: ALL animations work by shifting the top or left edges - if (pos) { // top (north) or left (west) - $el.css(ref, isNaN(dist) ? "-" + dist : -dist); // Shift outside the left/top edge - } - else { // bottom (south) or right (east) - shift all the way across container - if (dir === 'right') - $el.css({ left: state.container.layoutWidth, right: 'auto' }); - else // dir === bottom - $el.css({ top: state.container.layoutHeight, bottom: 'auto' }); - } - // restore the left/right setting if is a top/bottom animation - if (ref === 'top') - $el.css( $el.data( keyLR ) || {} ); - } - else { // hide() animation, so save ALL CSS - $el.data(keyTB, { top: s.top, bottom: s.bottom }); - $el.data(keyLR, { left: s.left, right: s.right }); - } - - // Animate - $el.show().animate(animation, { queue: false, duration: o.duration, easing: opt.easing, complete: function(){ - // Restore top/bottom - if ($el.data( keyTB )) - $el.css($el.data( keyTB )).removeData( keyTB ); - if (show) // Restore left/right too - $el.css($el.data( keyLR ) || {}).removeData( keyLR ); - else // Move the pane off-screen (left: -99999, right: 'auto') - $el.css( offscrn ); - - if (o.callback) o.callback.apply(this, arguments); // Callback - $el.dequeue(); - }}); - - }); - }; - -} - -})( jQuery ); +/** + * @preserve + * jquery.layout 1.4.4 + * $Date: 2014-11-29 08:00:00 (Sat, 29 November 2014) $ + * $Rev: 1.0404 $ + * + * Copyright (c) 2014 Kevin Dalman (http://jquery-dev.com) + * Based on work by Fabrizio Balliano (http://www.fabrizioballiano.net) + * + * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) + * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. + * + * SEE: http://layout.jquery-dev.com/LICENSE.txt + * + * Changelog: http://layout.jquery-dev.com/changelog.cfm + * + * Docs: http://layout.jquery-dev.com/documentation.html + * Tips: http://layout.jquery-dev.com/tips.html + * Help: http://groups.google.com/group/jquery-ui-layout + */ + +//
      +//
      +//
      西
      +//
      +//
      +// + +/* JavaDoc Info: http://code.google.com/closure/compiler/docs/js-for-compiler.html + * {!Object} non-nullable type (never NULL) + * {?string} nullable type (sometimes NULL) - default for {Object} + * {number=} optional parameter + * {*} ALL types + */ +/* TODO for jQ 2.x + * check $.fn.disableSelection - this is in jQuery UI 1.9.x + */ + +// NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars + +;(function ($) { + +// alias Math methods - used a lot! +var min = Math.min +, max = Math.max +, round = Math.floor + +, isStr = function (v) { return $.type(v) === "string"; } + + /** + * @param {!Object} Instance + * @param {Array.} a_fn + */ +, runPluginCallbacks = function (Instance, a_fn) { + if ($.isArray(a_fn)) + for (var i=0, c=a_fn.length; i').appendTo("body") + , d = { width: $c.outerWidth - $c[0].clientWidth, height: 100 - $c[0].clientHeight }; + $c.remove(); + window.scrollbarWidth = d.width; + window.scrollbarHeight = d.height; + return dim.match(/^(width|height)$/) ? d[dim] : d; + } + + +, disableTextSelection: function () { + var $d = $(document) + , s = 'textSelectionDisabled' + , x = 'textSelectionInitialized' + ; + if ($.fn.disableSelection) { + if (!$d.data(x)) // document hasn't been initialized yet + $d.on('mouseup', $.layout.enableTextSelection ).data(x, true); + if (!$d.data(s)) + $d.disableSelection().data(s, true); + } + } +, enableTextSelection: function () { + var $d = $(document) + , s = 'textSelectionDisabled'; + if ($.fn.enableSelection && $d.data(s)) + $d.enableSelection().data(s, false); + } + + + /** + * Returns hash container 'display' and 'visibility' + * + * @see $.swap() - swaps CSS, runs callback, resets CSS + * @param {!Object} $E jQuery element + * @param {boolean=} [force=false] Run even if display != none + * @return {!Object} Returns current style props, if applicable + */ +, showInvisibly: function ($E, force) { + if ($E && $E.length && (force || $E.css("display") === "none")) { // only if not *already hidden* + var s = $E[0].style + // save ONLY the 'style' props because that is what we must restore + , CSS = { display: s.display || '', visibility: s.visibility || '' }; + // show element 'invisibly' so can be measured + $E.css({ display: "block", visibility: "hidden" }); + return CSS; + } + return {}; + } + + /** + * Returns data for setting size of an element (container or a pane). + * + * @see _create(), onWindowResize() for container, plus others for pane + * @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc + */ +, getElementDimensions: function ($E, inset) { + var + // dimensions hash - start with current data IF passed + d = { css: {}, inset: {} } + , x = d.css // CSS hash + , i = { bottom: 0 } // TEMP insets (bottom = complier hack) + , N = $.layout.cssNum + , R = Math.round + , off = $E.offset() + , b, p, ei // TEMP border, padding + ; + d.offsetLeft = off.left; + d.offsetTop = off.top; + + if (!inset) inset = {}; // simplify logic below + + $.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge + b = x["border" + e] = $.layout.borderWidth($E, e); + p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e); + ei = e.toLowerCase(); + d.inset[ei] = inset[ei] >= 0 ? inset[ei] : p; // any missing insetX value = paddingX + i[ei] = d.inset[ei] + b; // total offset of content from outer side + }); + + x.width = R($E.width()); + x.height = R($E.height()); + x.top = N($E,"top",true); + x.bottom = N($E,"bottom",true); + x.left = N($E,"left",true); + x.right = N($E,"right",true); + + d.outerWidth = R($E.outerWidth()); + d.outerHeight = R($E.outerHeight()); + // calc the TRUE inner-dimensions, even in quirks-mode! + d.innerWidth = max(0, d.outerWidth - i.left - i.right); + d.innerHeight = max(0, d.outerHeight - i.top - i.bottom); + // layoutWidth/Height is used in calcs for manual resizing + // layoutW/H only differs from innerW/H when in quirks-mode - then is like outerW/H + d.layoutWidth = R($E.innerWidth()); + d.layoutHeight = R($E.innerHeight()); + + //if ($E.prop('tagName') === 'BODY') { debugData( d, $E.prop('tagName') ); } // DEBUG + + //d.visible = $E.is(":visible");// && x.width > 0 && x.height > 0; + + return d; + } + +, getElementStyles: function ($E, list) { + var + CSS = {} + , style = $E[0].style + , props = list.split(",") + , sides = "Top,Bottom,Left,Right".split(",") + , attrs = "Color,Style,Width".split(",") + , p, s, a, i, j, k + ; + for (i=0; i < props.length; i++) { + p = props[i]; + if (p.match(/(border|padding|margin)$/)) + for (j=0; j < 4; j++) { + s = sides[j]; + if (p === "border") + for (k=0; k < 3; k++) { + a = attrs[k]; + CSS[p+s+a] = style[p+s+a]; + } + else + CSS[p+s] = style[p+s]; + } + else + CSS[p] = style[p]; + }; + return CSS + } + + /** + * Return the innerWidth for the current browser/doctype + * + * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles() + * @param {Array.} $E Must pass a jQuery object - first element is processed + * @param {number=} outerWidth (optional) Can pass a width, allowing calculations BEFORE element is resized + * @return {number} Returns the innerWidth of the elem by subtracting padding and borders + */ +, cssWidth: function ($E, outerWidth) { + // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed + if (outerWidth <= 0) return 0; + + var lb = $.layout.browser + , bs = !lb.boxModel ? "border-box" : lb.boxSizing ? $E.css("boxSizing") : "content-box" + , b = $.layout.borderWidth + , n = $.layout.cssNum + , W = outerWidth + ; + // strip border and/or padding from outerWidth to get CSS Width + if (bs !== "border-box") + W -= (b($E, "Left") + b($E, "Right")); + if (bs === "content-box") + W -= (n($E, "paddingLeft") + n($E, "paddingRight")); + return max(0,W); + } + + /** + * Return the innerHeight for the current browser/doctype + * + * @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles() + * @param {Array.} $E Must pass a jQuery object - first element is processed + * @param {number=} outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized + * @return {number} Returns the innerHeight of the elem by subtracting padding and borders + */ +, cssHeight: function ($E, outerHeight) { + // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed + if (outerHeight <= 0) return 0; + + var lb = $.layout.browser + , bs = !lb.boxModel ? "border-box" : lb.boxSizing ? $E.css("boxSizing") : "content-box" + , b = $.layout.borderWidth + , n = $.layout.cssNum + , H = outerHeight + ; + // strip border and/or padding from outerHeight to get CSS Height + if (bs !== "border-box") + H -= (b($E, "Top") + b($E, "Bottom")); + if (bs === "content-box") + H -= (n($E, "paddingTop") + n($E, "paddingBottom")); + return max(0,H); + } + + /** + * Returns the 'current CSS numeric value' for a CSS property - 0 if property does not exist + * + * @see Called by many methods + * @param {Array.} $E Must pass a jQuery object - first element is processed + * @param {string} prop The name of the CSS property, eg: top, width, etc. + * @param {boolean=} [allowAuto=false] true = return 'auto' if that is value; false = return 0 + * @return {(string|number)} Usually used to get an integer value for position (top, left) or size (height, width) + */ +, cssNum: function ($E, prop, allowAuto) { + if (!$E.jquery) $E = $($E); + var CSS = $.layout.showInvisibly($E) + , p = $.css($E[0], prop, true) + , v = allowAuto && p=="auto" ? p : Math.round(parseFloat(p) || 0); + $E.css( CSS ); // RESET + return v; + } + +, borderWidth: function (el, side) { + if (el.jquery) el = el[0]; + var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left + return $.css(el, b+"Style", true) === "none" ? 0 : Math.round(parseFloat($.css(el, b+"Width", true)) || 0); + } + + /** + * Mouse-tracking utility - FUTURE REFERENCE + * + * init: if (!window.mouse) { + * window.mouse = { x: 0, y: 0 }; + * $(document).mousemove( $.layout.trackMouse ); + * } + * + * @param {Object} evt + * +, trackMouse: function (evt) { + window.mouse = { x: evt.clientX, y: evt.clientY }; + } + */ + + /** + * SUBROUTINE for preventPrematureSlideClose option + * + * @param {Object} evt + * @param {Object=} el + */ +, isMouseOverElem: function (evt, el) { + var + $E = $(el || this) + , d = $E.offset() + , T = d.top + , L = d.left + , R = L + $E.outerWidth() + , B = T + $E.outerHeight() + , x = evt.pageX // evt.clientX ? + , y = evt.pageY // evt.clientY ? + ; + // if X & Y are < 0, probably means is over an open SELECT + return ($.layout.browser.msie && x < 0 && y < 0) || ((x >= L && x <= R) && (y >= T && y <= B)); + } + + /** + * Message/Logging Utility + * + * @example $.layout.msg("My message"); // log text + * @example $.layout.msg("My message", true); // alert text + * @example $.layout.msg({ foo: "bar" }, "Title"); // log hash-data, with custom title + * @example $.layout.msg({ foo: "bar" }, true, "Title", { sort: false }); -OR- + * @example $.layout.msg({ foo: "bar" }, "Title", { sort: false, display: true }); // alert hash-data + * + * @param {(Object|string)} info String message OR Hash/Array + * @param {(Boolean|string|Object)=} [popup=false] True means alert-box - can be skipped + * @param {(Object|string)=} [debugTitle=""] Title for Hash data - can be skipped + * @param {Object=} [debugOpts] Extra options for debug output + */ +, msg: function (info, popup, debugTitle, debugOpts) { + if ($.isPlainObject(info) && window.debugData) { + if (typeof popup === "string") { + debugOpts = debugTitle; + debugTitle = popup; + } + else if (typeof debugTitle === "object") { + debugOpts = debugTitle; + debugTitle = null; + } + var t = debugTitle || "log( )" + , o = $.extend({ sort: false, returnHTML: false, display: false }, debugOpts); + if (popup === true || o.display) + debugData( info, t, o ); + else if (window.console) + console.log(debugData( info, t, o )); + } + else if (popup) + alert(info); + else if (window.console) + console.log(info); + else { + var id = "#layoutLogger" + , $l = $(id); + if (!$l.length) + $l = createLog(); + $l.children("ul").append('
    • '+ info.replace(/\/g,">") +'
    • '); + } + + function createLog () { + var pos = $.support.fixedPosition ? 'fixed' : 'absolute' + , $e = $('
      ' + + '
      ' + + 'XLayout console.log
      ' + + '
        ' + + '
        ' + ).appendTo("body"); + $e.css('left', $(window).width() - $e.outerWidth() - 5) + if ($.ui.draggable) $e.draggable({ handle: ':first-child' }); + return $e; + }; + } + +}; + + +/* + * $.layout.browser REPLACES removed $.browser, with extra data + * Parsing code here adapted from jQuery 1.8 $.browse + */ +(function(){ + var u = navigator.userAgent.toLowerCase() + , m = /(chrome)[ \/]([\w.]+)/.exec( u ) + || /(webkit)[ \/]([\w.]+)/.exec( u ) + || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( u ) + || /(msie) ([\w.]+)/.exec( u ) + || u.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( u ) + || [] + , b = m[1] || "" + , v = m[2] || 0 + , ie = b === "msie" + , cm = document.compatMode + , $s = $.support + , bs = $s.boxSizing !== undefined ? $s.boxSizing : $s.boxSizingReliable + , bm = !ie || !cm || cm === "CSS1Compat" || $s.boxModel || false + , lb = $.layout.browser = { + version: v + , safari: b === "webkit" // webkit (NOT chrome) = safari + , webkit: b === "chrome" // chrome = webkit + , msie: ie + , isIE6: ie && v == 6 + // ONLY IE reverts to old box-model - Note that compatMode was deprecated as of IE8 + , boxModel: bm + , boxSizing: !!(typeof bs === "function" ? bs() : bs) + }; + ; + if (b) lb[b] = true; // set CURRENT browser + /* OLD versions of jQuery only set $.support.boxModel after page is loaded + * so if this is IE, use support.boxModel to test for quirks-mode (ONLY IE changes boxModel) */ + if (!bm && !cm) $(function(){ lb.boxModel = $s.boxModel; }); +})(); + + +// DEFAULT OPTIONS +$.layout.defaults = { +/* + * LAYOUT & LAYOUT-CONTAINER OPTIONS + * - none of these options are applicable to individual panes + */ + name: "" // Not required, but useful for buttons and used for the state-cookie +, containerClass: "ui-layout-container" // layout-container element +, inset: null // custom container-inset values (override padding) +, scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark) +, resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event +, resizeWithWindowDelay: 50 // delay calling resizeAll because makes window resizing very jerky +, resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized +, maskPanesEarly: false // true = create pane-masks on resizer.mouseDown instead of waiting for resizer.dragstart +, onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific +, onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific +, onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements +, onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized +, onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload +, onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload +, initPanes: true // false = DO NOT initialize the panes onLoad - will init later +, showErrorMessages: true // enables fatal error messages to warn developers of common errors +, showDebugMessages: false // display console-and-alert debug msgs - IF this Layout version _has_ debugging code! +// Changing this zIndex value will cause other zIndex values to automatically change +, zIndex: null // the PANE zIndex - resizers and masks will be +1 +// DO NOT CHANGE the zIndex values below unless you clearly understand their relationships +, zIndexes: { // set _default_ z-index values here... + pane_normal: 0 // normal z-index for panes + , content_mask: 1 // applied to overlays used to mask content INSIDE panes during resizing + , resizer_normal: 2 // normal z-index for resizer-bars + , pane_sliding: 100 // applied to *BOTH* the pane and its resizer when a pane is 'slid open' + , pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer + , resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged' + } +, errors: { + pane: "pane" // description of "layout pane element" - used only in error messages + , selector: "selector" // description of "jQuery-selector" - used only in error messages + , addButtonError: "Error Adding Button\nInvalid " + , containerMissing: "UI Layout Initialization Error\nThe specified layout-container does not exist." + , centerPaneMissing: "UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element." + , noContainerHeight: "UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!" + , callbackError: "UI Layout Callback Error\nThe EVENT callback is not a valid function." + } +/* + * PANE DEFAULT SETTINGS + * - settings under the 'panes' key become the default settings for *all panes* + * - ALL pane-options can also be set specifically for each panes, which will override these 'default values' + */ +, panes: { // default options for 'all panes' - will be overridden by 'per-pane settings' + applyDemoStyles: false // NOTE: renamed from applyDefaultStyles for clarity + , 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 + , initClosed: false // true = init pane as 'closed' + , initHidden: false // true = init pane as 'hidden' - no resizer-bar/spacing + // SELECTORS + //, paneSelector: "" // MUST be pane-specific - jQuery selector for pane + , contentSelector: ".ui-layout-content" // ThinkGem 自动高度的内容类。 // INNER div/element to auto-size so only it scrolls, not the entire pane! + , contentIgnoreSelector: ".ui-layout-ignore" // element(s) to 'ignore' when measuring 'content' + , findNestedContent: true // ThinkGem 设置 contentSelector 自动高度,默认false // true = $P.find(contentSelector), false = $P.children(contentSelector) + // GENERIC ROOT-CLASSES - for auto-generated classNames + , paneClass: "ui-layout-pane" // Layout Pane + , resizerClass: "ui-layout-resizer" // Resizer Bar + , togglerClass: "ui-layout-toggler" // Toggler Button + , buttonClass: "ui-layout-button" // CUSTOM Buttons - eg: '[ui-layout-button]-toggle/-open/-close/-pin' + // ELEMENT SIZE & SPACING + //, size: 100 // MUST be pane-specific -initial size of pane + , minSize: 0 // when manually resizing a pane + , maxSize: 0 // ditto, 0 = no limit + , spacing_open: 8 // ThinkGem 分隔符宽度,默认6 // space between pane and adjacent panes - when pane is 'open' + , spacing_closed: 8 // ThinkGem 分隔符宽度,默认6 // ditto - when pane is 'closed' + , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south sides - HEIGHT on east/west sides + , 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 + , togglerContent_open: "" // ThinkGem 设置展开折叠图标,默认空"" // text or HTML to put INSIDE the toggler + , togglerContent_closed: "" // ThinkGem 设置展开折叠图标,默认空"" // ditto + // RESIZING OPTIONS + , resizerDblClickToggle: true // + , autoResize: true // IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes + , autoReopen: true // IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed + , resizerDragOpacity: 1 // option for ui.draggable + //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar + , maskContents: false // true = add DIV-mask over-or-inside this pane so can 'drag' over IFRAMES + , maskObjects: false // true = add IFRAME-mask over-or-inside this pane to cover objects/applets - content-mask will overlay this mask + , maskZindex: null // will override zIndexes.content_mask if specified - not applicable to iframe-panes + , resizingGrid: false // grid size that the resizers will snap-to during resizing, eg: [20,20] + , livePaneResizing: false // true = LIVE Resizing as resizer is dragged + , liveContentResizing: false // true = re-measure header/footer heights as resizer is dragged + , liveResizingTolerance: 1 // how many px change before pane resizes, to control performance + // SLIDING OPTIONS + , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding' + , slideTrigger_open: "mouseenter"// ThinkGem 设置侧边栏自动展开动作,默认click // click, dblclick, mouseenter + , slideTrigger_close: "mouseleave"// ThinkGem 设置侧边栏自动展开动作,默认mouseleave // click, mouseleave + , slideDelay_open: 100 // ThinkGem 设置鼠标放到侧边栏的时候自动展开的延迟时间,默认300 //applies only for mouseenter event - 0 = instant open + , slideDelay_close: 500 // ThinkGem 设置鼠标放到侧边栏的时候自动展开的延迟时间,默认300 //applies only for mouseleave event (300ms is the minimum!) + , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show? + , preventQuickSlideClose: $.layout.browser.webkit // Chrome triggers slideClosed as it is opening + , preventPrematureSlideClose: false // handle incorrect mouseleave trigger, like when over a SELECT-list in IE + // PANE-SPECIFIC TIPS & MESSAGES + , tips: { + Open: "展开" // ThinkGem 汉化,默认Open // eg: "Open Pane" + , Close: "折叠" // ThinkGem 汉化,默认Close + , Resize: "调整大小/双击折叠" // ThinkGem 汉化,默认Resize + , Slide: "鼠标停留自动展开" // ThinkGem 汉化,默认Slide Open + , Pin: "Pin" + , Unpin: "Un-Pin" + , noRoomToOpen: "Not enough room to show this panel." // alert if user tries to open a pane that cannot + , minSizeWarning: "Panel has reached its minimum size" // displays in browser statusbar + , maxSizeWarning: "Panel has reached its maximum size" // ditto + } + // HOT-KEYS & MISC + , 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' + // PANE ANIMATION + // NOTE: fxSss_open, fxSss_close & fxSss_size options (eg: fxName_open) are auto-generated if not passed + , fxName: "slide" // ('none' or blank), slide, drop, scale -- only relevant to 'open' & 'close', NOT 'size' + , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration + , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 } + , fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation + , animatePaneSizing: false // true = animate resizing after dragging resizer-bar OR sizePane() is called + /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set: + fxName_open: "slide" // 'Open' pane animation + fnName_close: "slide" // 'Close' pane animation + fxName_size: "slide" // 'Size' pane animation - when animatePaneSizing = true + fxSpeed_open: null + fxSpeed_close: null + fxSpeed_size: null + fxSettings_open: {} + fxSettings_close: {} + fxSettings_size: {} + */ + // CHILD/NESTED LAYOUTS + , children: null // Layout-options for nested/child layout - even {} is valid as options + , containerSelector: '' // if child is NOT 'directly nested', a selector to find it/them (can have more than one child layout!) + , initChildren: true // true = child layout will be created as soon as _this_ layout completes initialization + , destroyChildren: true // true = destroy child-layout if this pane is destroyed + , resizeChildren: true // true = trigger child-layout.resizeAll() when this pane is resized + // EVENT TRIGGERING + , triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes + , triggerEventsDuringLiveResize: true // true = trigger onresize callback REPEATEDLY if livePaneResizing==true + // PANE CALLBACKS + , onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start + , onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end + , onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start + , onhide_end: null // CALLBACK when pane ENDS being Closed - AFTER onclose_end + , onopen_start: null // CALLBACK when pane STARTS to Open + , onopen_end: null // CALLBACK when pane ENDS being Opened + , onclose_start: null // CALLBACK when pane STARTS to Close + , onclose_end: null // CALLBACK when pane ENDS being Closed + , onresize_start: null // CALLBACK when pane STARTS being Resized ***FOR ANY REASON*** + , onresize_end: null // CALLBACK when pane ENDS being Resized ***FOR ANY REASON*** + , onsizecontent_start: null // CALLBACK when sizing of content-element STARTS + , onsizecontent_end: null // CALLBACK when sizing of content-element ENDS + , onswap_start: null // CALLBACK when pane STARTS to Swap + , onswap_end: null // CALLBACK when pane ENDS being Swapped + , ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized + , ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized + } +/* + * PANE-SPECIFIC SETTINGS + * - options listed below MUST be specified per-pane - they CANNOT be set under 'panes' + * - all options under the 'panes' key can also be set specifically for any pane + * - most options under the 'panes' key apply only to 'border-panes' - NOT the the center-pane + */ +, north: { + paneSelector: ".ui-layout-north" + , size: "auto" // eg: "auto", "30%", .30, 200 + , resizerCursor: "n-resize" // custom = url(myCursor.cur) + , customHotkey: "" // EITHER a charCode (43) OR a character ("o") + } +, south: { + paneSelector: ".ui-layout-south" + , size: "auto" + , resizerCursor: "s-resize" + , customHotkey: "" + } +, east: { + paneSelector: ".ui-layout-east" + , size: 200 + , resizerCursor: "e-resize" + , customHotkey: "" + } +, west: { + paneSelector: ".ui-layout-west" + , size: 200 + , resizerCursor: "w-resize" + , customHotkey: "" + } +, center: { + paneSelector: ".ui-layout-center" + , minWidth: 0 + , minHeight: 0 + } +}; + +$.layout.optionsMap = { + // layout/global options - NOT pane-options + layout: ("name,instanceKey,stateManagement,effects,inset,zIndexes,errors," + + "zIndex,scrollToBookmarkOnLoad,showErrorMessages,maskPanesEarly," + + "outset,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay," + + "onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end").split(",") +// borderPanes: [ ALL options that are NOT specified as 'layout' ] + // default.panes options that apply to the center-pane (most options apply _only_ to border-panes) +, center: ("paneClass,contentSelector,contentIgnoreSelector,findNestedContent,applyDemoStyles,triggerEventsOnLoad," + + "showOverflowOnHover,maskContents,maskObjects,liveContentResizing," + + "containerSelector,children,initChildren,resizeChildren,destroyChildren," + + "onresize,onresize_start,onresize_end,onsizecontent,onsizecontent_start,onsizecontent_end").split(",") + // options that MUST be specifically set 'per-pane' - CANNOT set in the panes (defaults) key +, noDefault: ("paneSelector,resizerCursor,customHotkey").split(",") +}; + +/** + * Processes options passed in converts flat-format data into subkey (JSON) format + * In flat-format, subkeys are _currently_ separated with 2 underscores, like north__optName + * Plugins may also call this method so they can transform their own data + * + * @param {!Object} hash Data/options passed by user - may be a single level or nested levels + * @param {boolean=} [addKeys=false] Should the primary layout.options keys be added if they do not exist? + * @return {Object} Returns hash of minWidth & minHeight + */ +$.layout.transformData = function (hash, addKeys) { + var json = addKeys ? { panes: {}, center: {} } : {} // init return object + , branch, optKey, keys, key, val, i, c; + + if (typeof hash !== "object") return json; // no options passed + + // convert all 'flat-keys' to 'sub-key' format + for (optKey in hash) { + branch = json; + val = hash[ optKey ]; + keys = optKey.split("__"); // eg: west__size or north__fxSettings__duration + c = keys.length - 1; + // convert underscore-delimited to subkeys + for (i=0; i <= c; i++) { + key = keys[i]; + if (i === c) { // last key = value + if ($.isPlainObject( val )) + branch[key] = $.layout.transformData( val ); // RECURSE + else + branch[key] = val; + } + else { + if (!branch[key]) + branch[key] = {}; // create the subkey + // recurse to sub-key for next loop - if not done + branch = branch[key]; + } + } + } + return json; +}; + +// INTERNAL CONFIG DATA - DO NOT CHANGE THIS! +$.layout.backwardCompatibility = { + // data used by renameOldOptions() + map: { + // OLD Option Name: NEW Option Name + applyDefaultStyles: "applyDemoStyles" + // CHILD/NESTED LAYOUTS + , childOptions: "children" + , initChildLayout: "initChildren" + , destroyChildLayout: "destroyChildren" + , resizeChildLayout: "resizeChildren" + , resizeNestedLayout: "resizeChildren" + // MISC Options + , resizeWhileDragging: "livePaneResizing" + , resizeContentWhileDragging: "liveContentResizing" + , triggerEventsWhileDragging: "triggerEventsDuringLiveResize" + , maskIframesOnResize: "maskContents" + // STATE MANAGEMENT + , useStateCookie: "stateManagement.enabled" + , "cookie.autoLoad": "stateManagement.autoLoad" + , "cookie.autoSave": "stateManagement.autoSave" + , "cookie.keys": "stateManagement.stateKeys" + , "cookie.name": "stateManagement.cookie.name" + , "cookie.domain": "stateManagement.cookie.domain" + , "cookie.path": "stateManagement.cookie.path" + , "cookie.expires": "stateManagement.cookie.expires" + , "cookie.secure": "stateManagement.cookie.secure" + // OLD Language options + , noRoomToOpenTip: "tips.noRoomToOpen" + , togglerTip_open: "tips.Close" // open = Close + , togglerTip_closed: "tips.Open" // closed = Open + , resizerTip: "tips.Resize" + , sliderTip: "tips.Slide" + } + +/** +* @param {Object} opts +*/ +, renameOptions: function (opts) { + var map = $.layout.backwardCompatibility.map + , oldData, newData, value + ; + for (var itemPath in map) { + oldData = getBranch( itemPath ); + value = oldData.branch[ oldData.key ]; + if (value !== undefined) { + newData = getBranch( map[itemPath], true ); + newData.branch[ newData.key ] = value; + delete oldData.branch[ oldData.key ]; + } + } + + /** + * @param {string} path + * @param {boolean=} [create=false] Create path if does not exist + */ + function getBranch (path, create) { + var a = path.split(".") // split keys into array + , c = a.length - 1 + , D = { branch: opts, key: a[c] } // init branch at top & set key (last item) + , i = 0, k, undef; + for (; i 0) { + if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) { + $E.show().data('autoHidden', false); + if (!browser.mozilla) // FireFox refreshes iframes - IE does not + // make hidden, then visible to 'refresh' display after animation + $E.css(_c.hidden).css(_c.visible); + } + } + else if (autoHide && !$E.data('autoHidden')) + $E.hide().data('autoHidden', true); + } + + /** + * @param {(string|!Object)} el + * @param {number=} outerHeight + * @param {boolean=} [autoHide=false] + */ +, setOuterHeight = function (el, outerHeight, autoHide) { + var $E = el, h; + if (isStr(el)) $E = $Ps[el]; // west + else if (!el.jquery) $E = $(el); + h = cssH($E, outerHeight); + $E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent + if (h > 0 && $E.innerWidth() > 0) { + if (autoHide && $E.data('autoHidden')) { + $E.show().data('autoHidden', false); + if (!browser.mozilla) // FireFox refreshes iframes - IE does not + $E.css(_c.hidden).css(_c.visible); + } + } + else if (autoHide && !$E.data('autoHidden')) + $E.hide().data('autoHidden', true); + } + + + /** + * Converts any 'size' params to a pixel/integer size, if not already + * If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated + * + /** + * @param {string} pane + * @param {(string|number)=} size + * @param {string=} [dir] + * @return {number} + */ +, _parseSize = function (pane, size, dir) { + if (!dir) dir = _c[pane].dir; + + if (isStr(size) && size.match(/%/)) + size = (size === '100%') ? -1 : parseInt(size, 10) / 100; // convert % to decimal + + if (size === 0) + return 0; + else if (size >= 1) + return parseInt(size, 10); + + var o = options, avail = 0; + if (dir=="horz") // north or south or center.minHeight + avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0); + else if (dir=="vert") // east or west or center.minWidth + avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0); + + if (size === -1) // -1 == 100% + return avail; + else if (size > 0) // percentage, eg: .25 + return round(avail * size); + else if (pane=="center") + return 0; + else { // size < 0 || size=='auto' || size==Missing || size==Invalid + // auto-size the pane + var dim = (dir === "horz" ? "height" : "width") + , $P = $Ps[pane] + , $C = dim === 'height' ? $Cs[pane] : false + , vis = $.layout.showInvisibly($P) // show pane invisibly if hidden + , szP = $P.css(dim) // SAVE current pane size + , szC = $C ? $C.css(dim) : 0 // SAVE current content size + ; + $P.css(dim, "auto"); + if ($C) $C.css(dim, "auto"); + size = (dim === "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE + $P.css(dim, szP).css(vis); // RESET size & visibility + if ($C) $C.css(dim, szC); + return size; + } + } + + /** + * Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added + * + * @param {(string|!Object)} pane + * @param {boolean=} [inclSpace=false] + * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes + */ +, 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; + } + + /** + * Calculate min/max pane dimensions and limits for resizing + * + * @param {string} pane + * @param {boolean=} [slide=false] + */ +, setSizeLimits = function (pane, slide) { + if (!isInitialized()) return; + var + o = options[pane] + , s = state[pane] + , c = _c[pane] + , dir = c.dir + , type = c.sizeType.toLowerCase() + , isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param + , $P = $Ps[pane] + , paneSpacing = o.spacing_open + // measure the pane on the *opposite side* from this pane + , altPane = _c.oppositeEdge[pane] + , altS = state[altPane] + , $altP = $Ps[altPane] + , altPaneSize = (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth())) + , altPaneSpacing = ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0) + // limitSize prevents this pane from 'overlapping' opposite pane + , containerSize = (dir=="horz" ? sC.innerHeight : sC.innerWidth) + , minCenterDims = cssMinDims("center") + , minCenterSize = dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth) + // if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them + , limitSize = (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing))) + , minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize ) + , maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize ) + , r = s.resizerPosition = {} // used to set resizing limits + , top = sC.inset.top + , left = sC.inset.left + , W = sC.innerWidth + , H = sC.innerHeight + , rW = o.spacing_open // subtract resizer-width to get top/left position for south/east + ; + switch (pane) { + case "north": r.min = top + minSize; + r.max = top + maxSize; + break; + case "west": r.min = left + minSize; + r.max = left + maxSize; + break; + case "south": r.min = top + H - maxSize - rW; + r.max = top + H - minSize - rW; + break; + case "east": r.min = left + W - maxSize - rW; + r.max = left + W - minSize - rW; + break; + }; + } + + /** + * Returns data for setting the size/position of center pane. Also used to set Height for east/west panes + * + * @return JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height + */ +, calcNewCenterPaneDims = function () { + var d = { + top: getPaneSize("north", true) // true = include 'spacing' value for pane + , bottom: getPaneSize("south", true) + , left: getPaneSize("west", true) + , right: getPaneSize("east", true) + , width: 0 + , height: 0 + }; + + // NOTE: sC = state.container + // calc center-pane outer dimensions + d.width = sC.innerWidth - d.left - d.right; // outerWidth + d.height = sC.innerHeight - d.bottom - d.top; // outerHeight + // add the 'container border/padding' to get final positions relative to the container + d.top += sC.inset.top; + d.bottom += sC.inset.bottom; + d.left += sC.inset.left; + d.right += sC.inset.right; + + return d; + } + + + /** + * @param {!Object} el + * @param {boolean=} [allStates=false] + */ +, getHoverClasses = function (el, allStates) { + var + $El = $(el) + , type = $El.data("layoutRole") + , pane = $El.data("layoutEdge") + , o = options[pane] + , root = o[type +"Class"] + , _pane = "-"+ pane // eg: "-west" + , _open = "-open" + , _closed = "-closed" + , _slide = "-sliding" + , _hover = "-hover " // NOTE the trailing space + , _state = $El.hasClass(root+_closed) ? _closed : _open + , _alt = _state === _closed ? _open : _closed + , classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover) + ; + if (allStates) // when 'removing' classes, also remove alternate-state classes + classes += (root+_alt+_hover) + (root+_pane+_alt+_hover); + + if (type=="resizer" && $El.hasClass(root+_slide)) + classes += (root+_slide+_hover) + (root+_pane+_slide+_hover); + + return $.trim(classes); + } +, addHover = function (evt, el) { + var $E = $(el || this); + if (evt && $E.data("layoutRole") === "toggler") + evt.stopPropagation(); // prevent triggering 'slide' on Resizer-bar + $E.addClass( getHoverClasses($E) ); + } +, removeHover = function (evt, el) { + var $E = $(el || this); + $E.removeClass( getHoverClasses($E, true) ); + } + +, onResizerEnter = function (evt) { // ALSO called by toggler.mouseenter + var pane = $(this).data("layoutEdge") + , s = state[pane] + , $d = $(document) + ; + // ignore closed-panes and mouse moving back & forth over resizer! + // also ignore if ANY pane is currently resizing + if ( s.isResizing || state.paneResizing ) return; + + if (options.maskPanesEarly) + showMasks( pane, { resizing: true }); + } +, onResizerLeave = function (evt, el) { + var e = el || this // el is only passed when called by the timer + , pane = $(e).data("layoutEdge") + , name = pane +"ResizerLeave" + , $d = $(document) + ; + timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set + timer.clear(name); // cancel enableSelection timer - may re/set below + // this method calls itself on a timer because it needs to allow + // enough time for dragging to kick-in and set the isResizing flag + // dragging has a 100ms delay set, so this delay must be >100 + if (!el) // 1st call - mouseleave event + timer.set(name, function(){ onResizerLeave(evt, e); }, 200); + // if user is resizing, dragStop will reset everything, so skip it here + else if (options.maskPanesEarly && !state.paneResizing) // 2nd call - by timer + hideMasks(); + } + +/* + * ########################### + * INITIALIZATION METHODS + * ########################### + */ + + /** + * Initialize the layout - called automatically whenever an instance of layout is created + * + * @see none - triggered onInit + * @return mixed true = fully initialized | false = panes not initialized (yet) | 'cancel' = abort + */ +, _create = function () { + // initialize config/options + initOptions(); + var o = options + , s = state; + + // TEMP state so isInitialized returns true during init process + s.creatingLayout = true; + + // init plugins for this layout, if there are any (eg: stateManagement) + runPluginCallbacks( Instance, $.layout.onCreate ); + + // options & state have been initialized, so now run beforeLoad callback + // onload will CANCEL layout creation if it returns false + if (false === _runCallbacks("onload_start")) + return 'cancel'; + + // initialize the container element + _initContainer(); + + // bind hotkey function - keyDown - if required + initHotkeys(); + + // bind window.onunload + $(window).bind("unload."+ sID, unload); + + // init plugins for this layout, if there are any (eg: customButtons) + runPluginCallbacks( Instance, $.layout.onLoad ); + + // if layout elements are hidden, then layout WILL NOT complete initialization! + // initLayoutElements will set initialized=true and run the onload callback IF successful + if (o.initPanes) _initLayoutElements(); + + delete s.creatingLayout; + + return state.initialized; + } + + /** + * Initialize the layout IF not already + * + * @see All methods in Instance run this test + * @return boolean true = layoutElements have been initialized | false = panes are not initialized (yet) + */ +, isInitialized = function () { + if (state.initialized || state.creatingLayout) return true; // already initialized + else return _initLayoutElements(); // try to init panes NOW + } + + /** + * Initialize the layout - called automatically whenever an instance of layout is created + * + * @see _create() & isInitialized + * @param {boolean=} [retry=false] // indicates this is a 2nd try + * @return An object pointer to the instance created + */ +, _initLayoutElements = function (retry) { + // initialize config/options + var o = options; + // CANNOT init panes inside a hidden container! + if (!$N.is(":visible")) { + // handle Chrome bug where popup window 'has no height' + // if layout is BODY element, try again in 50ms + // SEE: http://layout.jquery-dev.com/samples/test_popup_window.html + if ( !retry && browser.webkit && $N[0].tagName === "BODY" ) + setTimeout(function(){ _initLayoutElements(true); }, 50); + return false; + } + + // a center pane is required, so make sure it exists + if (!getPane("center").length) { + return _log( o.errors.centerPaneMissing ); + } + + // TEMP state so isInitialized returns true during init process + state.creatingLayout = true; + + // update Container dims + $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values + + // initialize all layout elements + initPanes(); // size & position panes - calls initHandles() - which calls initResizable() + + if (o.scrollToBookmarkOnLoad) { + var l = self.location; + if (l.hash) l.replace( l.hash ); // scrollTo Bookmark + } + + // check to see if this layout 'nested' inside a pane + if (Instance.hasParentLayout) + o.resizeWithWindow = false; + // bind resizeAll() for 'this layout instance' to window.resize event + else if (o.resizeWithWindow) + $(window).bind("resize."+ sID, windowResize); + + delete state.creatingLayout; + state.initialized = true; + + // init plugins for this layout, if there are any + runPluginCallbacks( Instance, $.layout.onReady ); + + // now run the onload callback, if exists + _runCallbacks("onload_end"); + + return true; // elements initialized successfully + } + + /** + * Initialize nested layouts for a specific pane - can optionally pass layout-options + * + * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west + * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].children + * @return An object pointer to the layout instance created - or null + */ +, createChildren = function (evt_or_pane, opts) { + var pane = evtPane.call(this, evt_or_pane) + , $P = $Ps[pane] + ; + if (!$P) return; + var $C = $Cs[pane] + , s = state[pane] + , o = options[pane] + , sm = options.stateManagement || {} + , cos = opts ? (o.children = opts) : o.children + ; + if ( $.isPlainObject( cos ) ) + cos = [ cos ]; // convert a hash to a 1-elem array + else if (!cos || !$.isArray( cos )) + return; + + $.each( cos, function (idx, co) { + if ( !$.isPlainObject( co ) ) return; + + // determine which element is supposed to be the 'child container' + // if pane has a 'containerSelector' OR a 'content-div', use those instead of the pane + var $containers = co.containerSelector ? $P.find( co.containerSelector ) : ($C || $P); + + $containers.each(function(){ + var $cont = $(this) + , child = $cont.data("layout") // see if a child-layout ALREADY exists on this element + ; + // if no layout exists, but children are set, try to create the layout now + if (!child) { + // TODO: see about moving this to the stateManagement plugin, as a method + // set a unique child-instance key for this layout, if not already set + setInstanceKey({ container: $cont, options: co }, s ); + // If THIS layout has a hash in stateManagement.autoLoad, + // then see if it also contains state-data for this child-layout + // If so, copy the stateData to child.options.stateManagement.autoLoad + if ( sm.includeChildren && state.stateData[pane] ) { + // THIS layout's state was cached when its state was loaded + var paneChildren = state.stateData[pane].children || {} + , childState = paneChildren[ co.instanceKey ] + , co_sm = co.stateManagement || (co.stateManagement = { autoLoad: true }) + ; + // COPY the stateData into the autoLoad key + if ( co_sm.autoLoad === true && childState ) { + co_sm.autoSave = false; // disable autoSave because saving handled by parent-layout + co_sm.includeChildren = true; // cascade option - FOR NOW + co_sm.autoLoad = $.extend(true, {}, childState); // COPY the state-hash + } + } + + // create the layout + child = $cont.layout( co ); + + // if successful, update data + if (child) { + // add the child and update all layout-pointers + // MAY have already been done by child-layout calling parent.refreshChildren() + refreshChildren( pane, child ); + } + } + }); + }); + } + +, setInstanceKey = function (child, parentPaneState) { + // create a named key for use in state and instance branches + var $c = child.container + , o = child.options + , sm = o.stateManagement + , key = o.instanceKey || $c.data("layoutInstanceKey") + ; + if (!key) key = (sm && sm.cookie ? sm.cookie.name : '') || o.name; // look for a name/key + if (!key) key = "layout"+ (++parentPaneState.childIdx); // if no name/key found, generate one + else key = key.replace(/[^\w-]/gi, '_').replace(/_{2,}/g, '_'); // ensure is valid as a hash key + o.instanceKey = key; + $c.data("layoutInstanceKey", key); // useful if layout is destroyed and then recreated + return key; + } + + /** + * @param {string} pane The pane being opened, ie: north, south, east, or west + * @param {Object=} newChild New child-layout Instance to add to this pane + */ +, refreshChildren = function (pane, newChild) { + var $P = $Ps[pane] + , pC = children[pane] + , s = state[pane] + , o + ; + // check for destroy()ed layouts and update the child pointers & arrays + if ($.isPlainObject( pC )) { + $.each( pC, function (key, child) { + if (child.destroyed) delete pC[key] + }); + // if no more children, remove the children hash + if ($.isEmptyObject( pC )) + pC = children[pane] = null; // clear children hash + } + + // see if there is a directly-nested layout inside this pane + // if there is, then there can be only ONE child-layout, so check that... + if (!newChild && !pC) { + newChild = $P.data("layout"); + } + + // if a newChild instance was passed, add it to children[pane] + if (newChild) { + // update child.state + newChild.hasParentLayout = true; // set parent-flag in child + // instanceKey is a key-name used in both state and children + o = newChild.options; + // set a unique child-instance key for this layout, if not already set + setInstanceKey( newChild, s ); + // add pointer to pane.children hash + if (!pC) pC = children[pane] = {}; // create an empty children hash + pC[ o.instanceKey ] = newChild.container.data("layout"); // add childLayout instance + } + + // ALWAYS refresh the pane.children alias, even if null + Instance[pane].children = children[pane]; + + // if newChild was NOT passed - see if there is a child layout NOW + if (!newChild) { + createChildren(pane); // MAY create a child and re-call this method + } + } + +, windowResize = function () { + var o = options + , delay = Number(o.resizeWithWindowDelay); + if (delay < 10) delay = 100; // MUST have a delay! + // resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway + timer.clear("winResize"); // if already running + timer.set("winResize", function(){ + timer.clear("winResize"); + timer.clear("winResizeRepeater"); + var dims = elDims( $N, o.inset ); + // only trigger resizeAll() if container has changed size + if (dims.innerWidth !== sC.innerWidth || dims.innerHeight !== sC.innerHeight) + resizeAll(); + }, delay); + // ALSO set fixed-delay timer, if not already running + if (!timer.data["winResizeRepeater"]) setWindowResizeRepeater(); + } + +, setWindowResizeRepeater = function () { + var delay = Number(options.resizeWithWindowMaxDelay); + if (delay > 0) + timer.set("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay); + } + +, unload = function () { + var o = options; + + _runCallbacks("onunload_start"); + + // trigger plugin callabacks for this layout (eg: stateManagement) + runPluginCallbacks( Instance, $.layout.onUnload ); + + _runCallbacks("onunload_end"); + } + + /** + * Validate and initialize container CSS and events + * + * @see _create() + */ +, _initContainer = function () { + var + N = $N[0] + , $H = $("html") + , tag = sC.tagName = N.tagName + , id = sC.id = N.id + , cls = sC.className = N.className + , o = options + , name = o.name + , props = "position,margin,padding,border" + , css = "layoutCSS" + , CSS = {} + , hid = "hidden" // used A LOT! + // see if this container is a 'pane' inside an outer-layout + , parent = $N.data("parentLayout") // parent-layout Instance + , pane = $N.data("layoutEdge") // pane-name in parent-layout + , isChild = parent && pane + , num = $.layout.cssNum + , $parent, n + ; + // sC = state.container + sC.selector = $N.selector.split(".slice")[0]; + sC.ref = (o.name ? o.name +' layout / ' : '') + tag + (id ? "#"+id : cls ? '.['+cls+']' : ''); // used in messages + sC.isBody = (tag === "BODY"); + + // try to find a parent-layout + if (!isChild && !sC.isBody) { + $parent = $N.closest("."+ $.layout.defaults.panes.paneClass); + parent = $parent.data("parentLayout"); + pane = $parent.data("layoutEdge"); + isChild = parent && pane; + } + + $N .data({ + layout: Instance + , layoutContainer: sID // FLAG to indicate this is a layout-container - contains unique internal ID + }) + .addClass(o.containerClass) + ; + var layoutMethods = { + destroy: '' + , initPanes: '' + , resizeAll: 'resizeAll' + , resize: 'resizeAll' + }; + // loop hash and bind all methods - include layoutID namespacing + for (name in layoutMethods) { + $N.bind("layout"+ name.toLowerCase() +"."+ sID, Instance[ layoutMethods[name] || name ]); + } + + // if this container is another layout's 'pane', then set child/parent pointers + if (isChild) { + // update parent flag + Instance.hasParentLayout = true; + // set pointers to THIS child-layout (Instance) in parent-layout + parent.refreshChildren( pane, Instance ); + } + + // SAVE original container CSS for use in destroy() + if (!$N.data(css)) { + // handle props like overflow different for BODY & HTML - has 'system default' values + if (sC.isBody) { + // SAVE CSS + $N.data(css, $.extend( styles($N, props), { + height: $N.css("height") + , overflow: $N.css("overflow") + , overflowX: $N.css("overflowX") + , overflowY: $N.css("overflowY") + })); + // ALSO SAVE CSS + $H.data(css, $.extend( styles($H, 'padding'), { + height: "auto" // FF would return a fixed px-size! + , overflow: $H.css("overflow") + , overflowX: $H.css("overflowX") + , overflowY: $H.css("overflowY") + })); + } + else // handle props normally for non-body elements + $N.data(css, styles($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY") ); + } + + try { + // common container CSS + CSS = { + overflow: hid + , overflowX: hid + , overflowY: hid + }; + $N.css( CSS ); + + if (o.inset && !$.isPlainObject(o.inset)) { + // can specify a single number for equal outset all-around + n = parseInt(o.inset, 10) || 0 + o.inset = { + top: n + , bottom: n + , left: n + , right: n + }; + } + + // format html & body if this is a full page layout + if (sC.isBody) { + // if HTML has padding, use this as an outer-spacing around BODY + if (!o.outset) { + // use padding from parent-elem (HTML) as outset + o.outset = { + top: num($H, "paddingTop") + , bottom: num($H, "paddingBottom") + , left: num($H, "paddingLeft") + , right: num($H, "paddingRight") + }; + } + else if (!$.isPlainObject(o.outset)) { + // can specify a single number for equal outset all-around + n = parseInt(o.outset, 10) || 0 + o.outset = { + top: n + , bottom: n + , left: n + , right: n + }; + } + // HTML + $H.css( CSS ).css({ + height: "100%" + , border: "none" // no border or padding allowed when using height = 100% + , padding: 0 // ditto + , margin: 0 + }); + // BODY + if (browser.isIE6) { + // IE6 CANNOT use the trick of setting absolute positioning on all 4 sides - must have 'height' + $N.css({ + width: "100%" + , height: "100%" + , border: "none" // no border or padding allowed when using height = 100% + , padding: 0 // ditto + , margin: 0 + , position: "relative" + }); + // convert body padding to an inset option - the border cannot be measured in IE6! + if (!o.inset) o.inset = elDims( $N ).inset; + } + else { // use absolute positioning for BODY to allow borders & padding without overflow + $N.css({ + width: "auto" + , height: "auto" + , margin: 0 + , position: "absolute" // allows for border and padding on BODY + }); + // apply edge-positioning created above + $N.css( o.outset ); + } + // set current layout-container dimensions + $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values + } + else { + // container MUST have 'position' + var p = $N.css("position"); + if (!p || !p.match(/(fixed|absolute|relative)/)) + $N.css("position","relative"); + + // set current layout-container dimensions + if ( $N.is(":visible") ) { + $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT change insetX (padding) values + if (sC.innerHeight < 1) // container has no 'height' - warn developer + _log( o.errors.noContainerHeight.replace(/CONTAINER/, sC.ref) ); + } + } + + // if container has min-width/height, then enable scrollbar(s) + if ( num($N, "minWidth") ) $N.parent().css("overflowX","auto"); + if ( num($N, "minHeight") ) $N.parent().css("overflowY","auto"); + + } catch (ex) {} + } + + /** + * Bind layout hotkeys - if options enabled + * + * @see _create() and addPane() + * @param {string=} [panes=""] The edge(s) to process + */ +, initHotkeys = function (panes) { + panes = panes ? panes.split(",") : _c.borderPanes; + // bind keyDown to capture hotkeys, if option enabled for ANY pane + $.each(panes, function (i, pane) { + var o = options[pane]; + if (o.enableCursorHotkey || o.customHotkey) { + $(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE + return false; // BREAK - binding was done + } + }); + } + + /** + * Build final OPTIONS data + * + * @see _create() + */ +, initOptions = function () { + var data, d, pane, key, val, i, c, o; + + // reprocess user's layout-options to have correct options sub-key structure + opts = $.layout.transformData( opts, true ); // panes = default subkey + + // auto-rename old options for backward compatibility + opts = $.layout.backwardCompatibility.renameAllOptions( opts ); + + // if user-options has 'panes' key (pane-defaults), clean it... + if (!$.isEmptyObject(opts.panes)) { + // REMOVE any pane-defaults that MUST be set per-pane + data = $.layout.optionsMap.noDefault; + for (i=0, c=data.length; i 0) { + z.pane_normal = zo; + z.content_mask = max(zo+1, z.content_mask); // MIN = +1 + z.resizer_normal = max(zo+2, z.resizer_normal); // MIN = +2 + } + + // DELETE 'panes' key now that we are done - values were copied to EACH pane + delete options.panes; + + + function createFxOptions ( pane ) { + var o = options[pane] + , d = options.panes; + // ensure fxSettings key to avoid errors + if (!o.fxSettings) o.fxSettings = {}; + if (!d.fxSettings) d.fxSettings = {}; + + $.each(["_open","_close","_size"], function (i,n) { + var + sName = "fxName"+ n + , sSpeed = "fxSpeed"+ n + , sSettings = "fxSettings"+ n + // recalculate fxName according to specificity rules + , fxName = o[sName] = + o[sName] // options.west.fxName_open + || d[sName] // options.panes.fxName_open + || o.fxName // options.west.fxName + || d.fxName // options.panes.fxName + || "none" // MEANS $.layout.defaults.panes.fxName == "" || false || null || 0 + , fxExists = $.effects && ($.effects[fxName] || ($.effects.effect && $.effects.effect[fxName])) + ; + // validate fxName to ensure is valid effect - MUST have effect-config data in options.effects + if (fxName === "none" || !options.effects[fxName] || !fxExists) + fxName = o[sName] = "none"; // effect not loaded OR unrecognized fxName + + // set vars for effects subkeys to simplify logic + var fx = options.effects[fxName] || {} // effects.slide + , fx_all = fx.all || null // effects.slide.all + , fx_pane = fx[pane] || null // effects.slide.west + ; + // create fxSpeed[_open|_close|_size] + o[sSpeed] = + o[sSpeed] // options.west.fxSpeed_open + || d[sSpeed] // options.west.fxSpeed_open + || o.fxSpeed // options.west.fxSpeed + || d.fxSpeed // options.panes.fxSpeed + || null // DEFAULT - let fxSetting.duration control speed + ; + // create fxSettings[_open|_close|_size] + o[sSettings] = $.extend( + true + , {} + , fx_all // effects.slide.all + , fx_pane // effects.slide.west + , d.fxSettings // options.panes.fxSettings + , o.fxSettings // options.west.fxSettings + , d[sSettings] // options.panes.fxSettings_open + , o[sSettings] // options.west.fxSettings_open + ); + }); + + // DONE creating action-specific-settings for this pane, + // so DELETE generic options - are no longer meaningful + delete o.fxName; + delete o.fxSpeed; + delete o.fxSettings; + } + } + + /** + * Initialize module objects, styling, size and position for all panes + * + * @see _initElements() + * @param {string} pane The pane to process + */ +, getPane = function (pane) { + var sel = options[pane].paneSelector + if (sel.substr(0,1)==="#") // ID selector + // NOTE: elements selected 'by ID' DO NOT have to be 'children' + return $N.find(sel).eq(0); + else { // class or other selector + var $P = $N.children(sel).eq(0); + // look for the pane nested inside a 'form' element + return $P.length ? $P : $N.children("form:first").children(sel).eq(0); + } + } + + /** + * @param {Object=} evt + */ +, initPanes = function (evt) { + // stopPropagation if called by trigger("layoutinitpanes") - use evtPane utility + evtPane(evt); + + // NOTE: do north & south FIRST so we can measure their height - do center LAST + $.each(_c.allPanes, function (idx, pane) { + addPane( pane, true ); + }); + + // init the pane-handles NOW in case we have to hide or close the pane below + initHandles(); + + // now that all panes have been initialized and initially-sized, + // make sure there is really enough space available for each pane + $.each(_c.borderPanes, function (i, pane) { + if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN + setSizeLimits(pane); + makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit() + } + }); + // size center-pane AGAIN in case we 'closed' a border-pane in loop above + sizeMidPanes("center"); + + // Chrome/Webkit sometimes fires callbacks BEFORE it completes resizing! + // Before RC30.3, there was a 10ms delay here, but that caused layout + // to load asynchrously, which is BAD, so try skipping delay for now + + // process pane contents and callbacks, and init/resize child-layout if exists + $.each(_c.allPanes, function (idx, pane) { + afterInitPane(pane); + }); + } + + /** + * Add a pane to the layout - subroutine of initPanes() + * + * @see initPanes() + * @param {string} pane The pane to process + * @param {boolean=} [force=false] Size content after init + */ +, addPane = function (pane, force) { + if ( !force && !isInitialized() ) return; + var + o = options[pane] + , s = state[pane] + , c = _c[pane] + , dir = c.dir + , fx = s.fx + , spacing = o.spacing_open || 0 + , isCenter = (pane === "center") + , CSS = {} + , $P = $Ps[pane] + , size, minSize, maxSize, child + ; + // if pane-pointer already exists, remove the old one first + if ($P) + removePane( pane, false, true, false ); + else + $Cs[pane] = false; // init + + $P = $Ps[pane] = getPane(pane); + if (!$P.length) { + $Ps[pane] = false; // logic + return; + } + + // SAVE original Pane CSS + if (!$P.data("layoutCSS")) { + var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border"; + $P.data("layoutCSS", styles($P, props)); + } + + // create alias for pane data in Instance - initHandles will add more + Instance[pane] = { + name: pane + , pane: $Ps[pane] + , content: $Cs[pane] + , options: options[pane] + , state: state[pane] + , children: children[pane] + }; + + // add classes, attributes & events + $P .data({ + parentLayout: Instance // pointer to Layout Instance + , layoutPane: Instance[pane] // NEW pointer to pane-alias-object + , layoutEdge: pane + , layoutRole: "pane" + }) + .css(c.cssReq).css("zIndex", options.zIndexes.pane_normal) + .css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles + .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector' + .bind("mouseenter."+ sID, addHover ) + .bind("mouseleave."+ sID, removeHover ) + ; + var paneMethods = { + hide: '' + , show: '' + , toggle: '' + , close: '' + , open: '' + , slideOpen: '' + , slideClose: '' + , slideToggle: '' + , size: 'sizePane' + , sizePane: 'sizePane' + , sizeContent: '' + , sizeHandles: '' + , enableClosable: '' + , disableClosable: '' + , enableSlideable: '' + , disableSlideable: '' + , enableResizable: '' + , disableResizable: '' + , swapPanes: 'swapPanes' + , swap: 'swapPanes' + , move: 'swapPanes' + , removePane: 'removePane' + , remove: 'removePane' + , createChildren: '' + , resizeChildren: '' + , resizeAll: 'resizeAll' + , resizeLayout: 'resizeAll' + } + , name; + // loop hash and bind all methods - include layoutID namespacing + for (name in paneMethods) { + $P.bind("layoutpane"+ name.toLowerCase() +"."+ sID, Instance[ paneMethods[name] || name ]); + } + + // see if this pane has a 'scrolling-content element' + initContent(pane, false); // false = do NOT sizeContent() - called later + + if (!isCenter) { + // call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden) + // if o.size is auto or not valid, then MEASURE the pane and use that as its 'size' + size = s.size = _parseSize(pane, o.size); + minSize = _parseSize(pane,o.minSize) || 1; + maxSize = _parseSize(pane,o.maxSize) || 100000; + if (size > 0) size = max(min(size, maxSize), minSize); + s.autoResize = o.autoResize; // used with percentage sizes + + // state for border-panes + 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! + + // array for 'pin buttons' whose classNames are auto-updated on pane-open/-close + if (!s.pins) s.pins = []; + } + // states common to ALL panes + s.tagName = $P[0].tagName; + s.edge = pane; // useful if pane is (or about to be) 'swapped' - easy find out where it is (or is going) + s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically + s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic + + // init pane positioning + setPanePosition( pane ); + + // if pane is not visible, + if (dir === "horz") // north or south pane + CSS.height = cssH($P, size); + else if (dir === "vert") // east or west pane + CSS.width = cssW($P, size); + //else if (isCenter) {} + + $P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes + if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback + + // if manually adding a pane AFTER layout initialization, then... + if (state.initialized) { + initHandles( pane ); + initHotkeys( pane ); + } + + // close or hide the pane if specified in settings + if (o.initClosed && o.closable && !o.initHidden) + close(pane, true, true); // true, true = force, noAnimation + else if (o.initHidden || o.initClosed) + hide(pane); // will be completely invisible - no resizer or spacing + else if (!s.noRoom) + // make the pane visible - in case was initially hidden + $P.css("display","block"); + // ELSE setAsOpen() - called later by initHandles() + + // RESET visibility now - pane will appear IF display:block + $P.css("visibility","visible"); + + // check option for auto-handling of pop-ups & drop-downs + if (o.showOverflowOnHover) + $P.hover( allowOverflow, resetOverflow ); + + // if manually adding a pane AFTER layout initialization, then... + if (state.initialized) { + afterInitPane( pane ); + } + } + +, afterInitPane = function (pane) { + var $P = $Ps[pane] + , s = state[pane] + , o = options[pane] + ; + if (!$P) return; + + // see if there is a directly-nested layout inside this pane + if ($P.data("layout")) + refreshChildren( pane, $P.data("layout") ); + + // process pane contents and callbacks, and init/resize child-layout if exists + if (s.isVisible) { // pane is OPEN + if (state.initialized) // this pane was added AFTER layout was created + resizeAll(); // will also sizeContent + else + sizeContent(pane); + + if (o.triggerEventsOnLoad) + _runCallbacks("onresize_end", pane); + else // automatic if onresize called, otherwise call it specifically + // resize child - IF inner-layout already exists (created before this layout) + resizeChildren(pane, true); // a previously existing childLayout + } + + // init childLayouts - even if pane is not visible + if (o.initChildren && o.children) + createChildren(pane); + } + + /** + * @param {string=} panes The pane(s) to process + */ +, setPanePosition = function (panes) { + panes = panes ? panes.split(",") : _c.borderPanes; + + // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV + $.each(panes, function (i, pane) { + var $P = $Ps[pane] + , $R = $Rs[pane] + , o = options[pane] + , s = state[pane] + , side = _c[pane].side + , CSS = {} + ; + if (!$P) return; // pane does not exist - skip + + // set css-position to account for container borders & padding + switch (pane) { + case "north": CSS.top = sC.inset.top; + CSS.left = sC.inset.left; + CSS.right = sC.inset.right; + break; + case "south": CSS.bottom = sC.inset.bottom; + CSS.left = sC.inset.left; + CSS.right = sC.inset.right; + break; + case "west": CSS.left = sC.inset.left; // top, bottom & height set by sizeMidPanes() + break; + case "east": CSS.right = sC.inset.right; // ditto + break; + case "center": // top, left, width & height set by sizeMidPanes() + } + // apply position + $P.css(CSS); + + // update resizer position + if ($R && s.isClosed) + $R.css(side, sC.inset[side]); + else if ($R && !s.isHidden) + $R.css(side, sC.inset[side] + getPaneSize(pane)); + }); + } + + /** + * Initialize module objects, styling, size and position for all resize bars and toggler buttons + * + * @see _create() + * @param {string=} [panes=""] The edge(s) to process + */ +, initHandles = function (panes) { + panes = panes ? panes.split(",") : _c.borderPanes; + + // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV + $.each(panes, function (i, pane) { + var $P = $Ps[pane]; + $Rs[pane] = false; // INIT + $Ts[pane] = false; + if (!$P) return; // pane does not exist - skip + + var o = options[pane] + , s = state[pane] + , c = _c[pane] + , paneId = o.paneSelector.substr(0,1) === "#" ? o.paneSelector.substr(1) : "" + , rClass = o.resizerClass + , tClass = o.togglerClass + , spacing = (s.isVisible ? o.spacing_open : o.spacing_closed) + , _pane = "-"+ pane // used for classNames + , _state = (s.isVisible ? "-open" : "-closed") // used for classNames + , I = Instance[pane] + // INIT RESIZER BAR + , $R = I.resizer = $Rs[pane] = $("
        ") + // INIT TOGGLER BUTTON + , $T = I.toggler = (o.closable ? $Ts[pane] = $("
        ") : false) + ; + + //if (s.isVisible && o.resizable) ... handled by initResizable + if (!s.isVisible && o.slidable) + $R.attr("title", o.tips.Slide).css("cursor", o.sliderCursor); + + $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer" + .attr("id", paneId ? paneId +"-resizer" : "" ) + .data({ + parentLayout: Instance + , layoutPane: Instance[pane] // NEW pointer to pane-alias-object + , layoutEdge: pane + , layoutRole: "resizer" + }) + .css(_c.resizers.cssReq).css("zIndex", options.zIndexes.resizer_normal) + .css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles + .addClass(rClass +" "+ rClass+_pane) + .hover(addHover, removeHover) // ALWAYS add hover-classes, even if resizing is not enabled - handle with CSS instead + .hover(onResizerEnter, onResizerLeave) // ALWAYS NEED resizer.mouseleave to balance toggler.mouseenter + .mousedown($.layout.disableTextSelection) // prevent text-selection OUTSIDE resizer + .mouseup($.layout.enableTextSelection) // not really necessary, but just in case + .appendTo($N) // append DIV to container + ; + if ($.fn.disableSelection) + $R.disableSelection(); // prevent text-selection INSIDE resizer + if (o.resizerDblClickToggle) + $R.bind("dblclick."+ sID, toggle ); + + if ($T) { + $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler" + .attr("id", paneId ? paneId +"-toggler" : "" ) + .data({ + parentLayout: Instance + , layoutPane: Instance[pane] // NEW pointer to pane-alias-object + , layoutEdge: pane + , layoutRole: "toggler" + }) + .css(_c.togglers.cssReq) // add base/required styles + .css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles + .addClass(tClass +" "+ tClass+_pane) + .hover(addHover, removeHover) // ALWAYS add hover-classes, even if toggling is not enabled - handle with CSS instead + .bind("mouseenter", onResizerEnter) // NEED toggler.mouseenter because mouseenter MAY NOT fire on resizer + .appendTo($R) // append SPAN to resizer DIV + ; + // ADD INNER-SPANS TO TOGGLER + if (o.togglerContent_open) // ui-layout-open + $(""+ o.togglerContent_open +"") + .data({ + layoutEdge: pane + , layoutRole: "togglerContent" + }) + .data("layoutRole", "togglerContent") + .data("layoutEdge", pane) +// .addClass("content content-open") + .addClass("ui-content content-open") // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 + .css("display","none") + .appendTo( $T ) + //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-open instead! + ; + if (o.togglerContent_closed) // ui-layout-closed + $(""+ o.togglerContent_closed +"") + .data({ + layoutEdge: pane + , layoutRole: "togglerContent" + }) +// .addClass("content content-closed") + .addClass("ui-content content-closed") // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 + .css("display","none") + .appendTo( $T ) + //.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-closed instead! + ; + // ADD TOGGLER.click/.hover + enableClosable(pane); + } + + // add Draggable events + initResizable(pane); + + // ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open" + if (s.isVisible) + setAsOpen(pane); // onOpen will be called, but NOT onResize + else { + setAsClosed(pane); // onClose will be called + bindStartSlidingEvents(pane, true); // will enable events IF option is set + } + + }); + + // SET ALL HANDLE DIMENSIONS + sizeHandles(); + } + + + /** + * Initialize scrolling ui-layout-content div - if exists + * + * @see initPane() - or externally after an Ajax injection + * @param {string} pane The pane to process + * @param {boolean=} [resize=true] Size content after init + */ +, initContent = function (pane, resize) { + if (!isInitialized()) return; + var + o = options[pane] + , sel = o.contentSelector + , I = Instance[pane] + , $P = $Ps[pane] + , $C + ; + if (sel) $C = I.content = $Cs[pane] = (o.findNestedContent) + ? $P.find(sel).eq(0) // match 1-element only + : $P.children(sel).eq(0) + ; + if ($C && $C.length) { + $C.data("layoutRole", "content"); + // SAVE original Content CSS + if (!$C.data("layoutCSS")) + $C.data("layoutCSS", styles($C, "height")); + $C.css( _c.content.cssReq ); + if (o.applyDemoStyles) { + $C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div + $P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane + } + // ensure no vertical scrollbar on pane - will mess up measurements + if ($P.css("overflowX").match(/(scroll|auto)/)) { + $P.css("overflow", "hidden"); + } + state[pane].content = {}; // init content state + if (resize !== false) sizeContent(pane); + // sizeContent() is called AFTER init of all elements + } + else + I.content = $Cs[pane] = false; + } + + + /** + * Add resize-bars to all panes that specify it in options + * -dependancy: $.fn.resizable - will skip if not found + * + * @see _create() + * @param {string=} [panes=""] The edge(s) to process + */ +, initResizable = function (panes) { + var draggingAvailable = $.layout.plugins.draggable + , side // set in start() + ; + panes = panes ? panes.split(",") : _c.borderPanes; + + $.each(panes, function (idx, pane) { + var o = options[pane]; + if (!draggingAvailable || !$Ps[pane] || !o.resizable) { + o.resizable = false; + return true; // skip to next + } + + var s = state[pane] + , z = options.zIndexes + , c = _c[pane] + , side = c.dir=="horz" ? "top" : "left" + , $P = $Ps[pane] + , $R = $Rs[pane] + , base = o.resizerClass + , lastPos = 0 // used when live-resizing + , r, live // set in start because may change + // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process + , resizerClass = base+"-drag" // resizer-drag + , resizerPaneClass = base+"-"+pane+"-drag" // resizer-north-drag + // 'helper' class is applied to the CLONED resizer-bar while it is being dragged + , helperClass = base+"-dragging" // resizer-dragging + , helperPaneClass = base+"-"+pane+"-dragging" // resizer-north-dragging + , helperLimitClass = base+"-dragging-limit" // resizer-drag + , helperPaneLimitClass = base+"-"+pane+"-dragging-limit" // resizer-north-drag + , helperClassesSet = false // logic var + ; + + if (!s.isClosed) + $R.attr("title", o.tips.Resize) + .css("cursor", o.resizerCursor); // n-resize, s-resize, etc + + $R.draggable({ + containment: $N[0] // limit resizing to layout container + , axis: (c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis + , delay: 0 + , distance: 1 + , grid: o.resizingGrid + // basic format for helper - style it using class: .ui-draggable-dragging + , helper: "clone" + , opacity: o.resizerDragOpacity + , addClasses: false // avoid ui-state-disabled class when disabled + //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed + , zIndex: z.resizer_drag + + , iframeFix: true // ThinkGem 修复有iframe时拖拽不平滑问题 + + , start: function (e, ui) { + // REFRESH options & state pointers in case we used swapPanes + o = options[pane]; + s = state[pane]; + // re-read options + live = o.livePaneResizing; + + // ondrag_start callback - will CANCEL hide if returns false + // TODO: dragging CANNOT be cancelled like this, so see if there is a way? + if (false === _runCallbacks("ondrag_start", pane)) return false; + + s.isResizing = true; // prevent pane from closing while resizing + state.paneResizing = pane; // easy to see if ANY pane is resizing + timer.clear(pane+"_closeSlider"); // just in case already triggered + + // SET RESIZER LIMITS - used in drag() + setSizeLimits(pane); // update pane/resizer state + r = s.resizerPosition; + lastPos = ui.position[ side ] + + $R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes + helperClassesSet = false; // reset logic var - see drag() + + // MASK PANES CONTAINING IFRAMES, APPLETS OR OTHER TROUBLESOME ELEMENTS + showMasks( pane, { resizing: true }); + } + + , drag: function (e, ui) { + if (!helperClassesSet) { // can only add classes after clone has been added to the DOM + //$(".ui-draggable-dragging") + ui.helper + .addClass( helperClass +" "+ helperPaneClass ) // add helper classes + .css({ right: "auto", bottom: "auto" }) // fix dir="rtl" issue + .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar + ; + helperClassesSet = true; + // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane! + if (s.isSliding) $Ps[pane].css("zIndex", z.pane_sliding); + } + // CONTAIN RESIZER-BAR TO RESIZING LIMITS + var limit = 0; + if (ui.position[side] < r.min) { + ui.position[side] = r.min; + limit = -1; + } + else if (ui.position[side] > r.max) { + ui.position[side] = r.max; + limit = 1; + } + // ADD/REMOVE dragging-limit CLASS + if (limit) { + ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit + window.defaultStatus = (limit>0 && pane.match(/(north|west)/)) || (limit<0 && pane.match(/(south|east)/)) ? o.tips.maxSizeWarning : o.tips.minSizeWarning; + } + else { + ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit + window.defaultStatus = ""; + } + // DYNAMICALLY RESIZE PANES IF OPTION ENABLED + // won't trigger unless resizer has actually moved! + if (live && Math.abs(ui.position[side] - lastPos) >= o.liveResizingTolerance) { + lastPos = ui.position[side]; + resizePanes(e, ui, pane) + } + } + + , stop: function (e, ui) { + $('body').enableSelection(); // RE-ENABLE TEXT SELECTION + window.defaultStatus = ""; // clear 'resizing limit' message from statusbar + $R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer + s.isResizing = false; + state.paneResizing = false; // easy to see if ANY pane is resizing + resizePanes(e, ui, pane, true); // true = resizingDone + } + + }); + }); + + /** + * resizePanes + * + * Sub-routine called from stop() - and drag() if livePaneResizing + * + * @param {!Object} evt + * @param {!Object} ui + * @param {string} pane + * @param {boolean=} [resizingDone=false] + */ + var resizePanes = function (evt, ui, pane, resizingDone) { + var dragPos = ui.position + , c = _c[pane] + , o = options[pane] + , s = state[pane] + , resizerPos + ; + switch (pane) { + case "north": resizerPos = dragPos.top; break; + case "west": resizerPos = dragPos.left; break; + case "south": resizerPos = sC.layoutHeight - dragPos.top - o.spacing_open; break; + case "east": resizerPos = sC.layoutWidth - dragPos.left - o.spacing_open; break; + }; + // remove container margin from resizer position to get the pane size + var newSize = resizerPos - sC.inset[c.side]; + + // Disable OR Resize Mask(s) created in drag.start + if (!resizingDone) { + // ensure we meet liveResizingTolerance criteria + if (Math.abs(newSize - s.size) < o.liveResizingTolerance) + return; // SKIP resize this time + // resize the pane + manualSizePane(pane, newSize, false, true); // true = noAnimation + sizeMasks(); // resize all visible masks + } + else { // resizingDone + // ondrag_end callback + if (false !== _runCallbacks("ondrag_end", pane)) + manualSizePane(pane, newSize, false, true); // true = noAnimation + hideMasks(true); // true = force hiding all masks even if one is 'sliding' + if (s.isSliding) // RE-SHOW 'object-masks' so objects won't show through sliding pane + showMasks( pane, { resizing: true }); + } + }; + } + + /** + * sizeMask + * + * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane + * Called when mask created, and during livePaneResizing + */ +, sizeMask = function () { + var $M = $(this) + , pane = $M.data("layoutMask") // eg: "west" + , s = state[pane] + ; + // only masks over an IFRAME-pane need manual resizing + if (s.tagName == "IFRAME" && s.isVisible) // no need to mask closed/hidden panes + $M.css({ + top: s.offsetTop + , left: s.offsetLeft + , width: s.outerWidth + , height: s.outerHeight + }); + /* ALT Method... + var $P = $Ps[pane]; + $M.css( $P.position() ).css({ width: $P[0].offsetWidth, height: $P[0].offsetHeight }); + */ + } +, sizeMasks = function () { + $Ms.each( sizeMask ); // resize all 'visible' masks + } + + /** + * @param {string} pane The pane being resized, animated or isSliding + * @param {Object=} [args] (optional) Options: which masks to apply, and to which panes + */ +, showMasks = function (pane, args) { + var c = _c[pane] + , panes = ["center"] + , z = options.zIndexes + , a = $.extend({ + objectsOnly: false + , animation: false + , resizing: true + , sliding: state[pane].isSliding + }, args ) + , o, s + ; + if (a.resizing) + panes.push( pane ); + if (a.sliding) + panes.push( _c.oppositeEdge[pane] ); // ADD the oppositeEdge-pane + + if (c.dir === "horz") { + panes.push("west"); + panes.push("east"); + } + + $.each(panes, function(i,p){ + s = state[p]; + o = options[p]; + if (s.isVisible && ( o.maskObjects || (!a.objectsOnly && o.maskContents) )) { + getMasks(p).each(function(){ + sizeMask.call(this); + this.style.zIndex = s.isSliding ? z.pane_sliding+1 : z.pane_normal+1 + this.style.display = "block"; + }); + } + }); + } + + /** + * @param {boolean=} force Hide masks even if a pane is sliding + */ +, hideMasks = function (force) { + // ensure no pane is resizing - could be a timing issue + if (force || !state.paneResizing) { + $Ms.hide(); // hide ALL masks + } + // if ANY pane is sliding, then DO NOT remove masks from panes with maskObjects enabled + else if (!force && !$.isEmptyObject( state.panesSliding )) { + var i = $Ms.length - 1 + , p, $M; + for (; i >= 0; i--) { + $M = $Ms.eq(i); + p = $M.data("layoutMask"); + if (!options[p].maskObjects) { + $M.hide(); + } + } + } + } + + /** + * @param {string} pane + */ +, getMasks = function (pane) { + var $Masks = $([]) + , $M, i = 0, c = $Ms.length + ; + for (; i CSS + if (sC.tagName === "BODY" && ($N = $("html")).data(css)) // RESET CSS + $N.css( $N.data(css) ).removeData(css); + + // trigger plugins for this layout, if there are any + runPluginCallbacks( Instance, $.layout.onDestroy ); + + // trigger state-management and onunload callback + unload(); + + // clear the Instance of everything except for container & options (so could recreate) + // RE-CREATE: myLayout = myLayout.container.layout( myLayout.options ); + for (var n in Instance) + if (!n.match(/^(container|options)$/)) delete Instance[ n ]; + // add a 'destroyed' flag to make it easy to check + Instance.destroyed = true; + + // if this is a child layout, CLEAR the child-pointer in the parent + /* for now the pointer REMAINS, but with only container, options and destroyed keys + if (parentPane) { + var layout = parentPane.pane.data("parentLayout") + , key = layout.options.instanceKey || 'error'; + // THIS SYNTAX MAY BE WRONG! + parentPane.children[key] = layout.children[ parentPane.name ].children[key] = null; + } + */ + + return Instance; // for coding convenience + } + + /** + * Remove a pane from the layout - subroutine of destroy() + * + * @see destroy() + * @param {(string|Object)} evt_or_pane The pane to process + * @param {boolean=} [remove=false] Remove the DOM element? + * @param {boolean=} [skipResize=false] Skip calling resizeAll()? + * @param {boolean=} [destroyChild=true] Destroy Child-layouts? If not passed, obeys options setting + */ +, removePane = function (evt_or_pane, remove, skipResize, destroyChild) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $P = $Ps[pane] + , $C = $Cs[pane] + , $R = $Rs[pane] + , $T = $Ts[pane] + ; + // NOTE: elements can still exist even after remove() + // so check for missing data(), which is cleared by removed() + if ($P && $.isEmptyObject( $P.data() )) $P = false; + if ($C && $.isEmptyObject( $C.data() )) $C = false; + if ($R && $.isEmptyObject( $R.data() )) $R = false; + if ($T && $.isEmptyObject( $T.data() )) $T = false; + + if ($P) $P.stop(true, true); + + var o = options[pane] + , s = state[pane] + , d = "layout" + , css = "layoutCSS" + , pC = children[pane] + , hasChildren = $.isPlainObject( pC ) && !$.isEmptyObject( pC ) + , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildren + ; + // FIRST destroy the child-layout(s) + if (hasChildren && destroy) { + $.each( pC, function (key, child) { + if (!child.destroyed) + child.destroy(true);// tell child-layout to destroy ALL its child-layouts too + if (child.destroyed) // destroy was successful + delete pC[key]; + }); + // if no more children, remove the children hash + if ($.isEmptyObject( pC )) { + pC = children[pane] = null; // clear children hash + hasChildren = false; + } + } + + // Note: can't 'remove' a pane element with non-destroyed children + if ($P && remove && !hasChildren) + $P.remove(); // remove the pane-element and everything inside it + else if ($P && $P[0]) { + // create list of ALL pane-classes that need to be removed + var root = o.paneClass // default="ui-layout-pane" + , pRoot = root +"-"+ pane // eg: "ui-layout-pane-west" + , _open = "-open" + , _sliding= "-sliding" + , _closed = "-closed" + , classes = [ root, root+_open, root+_closed, root+_sliding, // generic classes + pRoot, pRoot+_open, pRoot+_closed, pRoot+_sliding ] // pane-specific classes + ; + $.merge(classes, getHoverClasses($P, true)); // ADD hover-classes + // remove all Layout classes from pane-element + $P .removeClass( classes.join(" ") ) // remove ALL pane-classes + .removeData("parentLayout") + .removeData("layoutPane") + .removeData("layoutRole") + .removeData("layoutEdge") + .removeData("autoHidden") // in case set + .unbind("."+ sID) // remove ALL Layout events + // TODO: remove these extra unbind commands when jQuery is fixed + //.unbind("mouseenter"+ sID) + //.unbind("mouseleave"+ sID) + ; + // do NOT reset CSS if this pane/content is STILL the container of a nested layout! + // the nested layout will reset its 'container' CSS when/if it is destroyed + if (hasChildren && $C) { + // a content-div may not have a specific width, so give it one to contain the Layout + $C.width( $C.width() ); + $.each( pC, function (key, child) { + child.resizeAll(); // resize the Layout + }); + } + else if ($C) + $C.css( $C.data(css) ).removeData(css).removeData("layoutRole"); + // remove pane AFTER content in case there was a nested layout + if (!$P.data(d)) + $P.css( $P.data(css) ).removeData(css); + } + + // REMOVE pane resizer and toggler elements + if ($T) $T.remove(); + if ($R) $R.remove(); + + // CLEAR all pointers and state data + Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = false; + s = { removed: true }; + + if (!skipResize) + resizeAll(); + } + + +/* + * ########################### + * ACTION METHODS + * ########################### + */ + + /** + * @param {string} pane + */ +, _hidePane = function (pane) { + var $P = $Ps[pane] + , o = options[pane] + , s = $P[0].style + ; + if (o.useOffscreenClose) { + if (!$P.data(_c.offscreenReset)) + $P.data(_c.offscreenReset, { left: s.left, right: s.right }); + $P.css( _c.offscreenCSS ); + } + else + $P.hide().removeData(_c.offscreenReset); + } + + /** + * @param {string} pane + */ +, _showPane = function (pane) { + var $P = $Ps[pane] + , o = options[pane] + , off = _c.offscreenCSS + , old = $P.data(_c.offscreenReset) + , s = $P[0].style + ; + $P .show() // ALWAYS show, just in case + .removeData(_c.offscreenReset); + if (o.useOffscreenClose && old) { + if (s.left == off.left) + s.left = old.left; + if (s.right == off.right) + s.right = old.right; + } + } + + + /** + * 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|Object)} evt_or_pane The pane being hidden, ie: north, south, east, or west + * @param {boolean=} [noAnimation=false] + */ +, hide = function (evt_or_pane, noAnimation) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , o = options[pane] + , s = state[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + ; + if (pane === "center" || !$P || s.isHidden) return; // pane does not exist OR is already hidden + + // onhide_start callback - will CANCEL hide if returns false + if (state.initialized && false === _runCallbacks("onhide_start", pane)) return; + + s.isSliding = false; // just in case + delete state.panesSliding[pane]; + + // now hide the elements + if ($R) $R.hide(); // hide resizer-bar + if (!state.initialized || s.isClosed) { + s.isClosed = true; // to trigger open-animation on show() + s.isHidden = true; + s.isVisible = false; + if (!state.initialized) + _hidePane(pane); // no animation when loading page + sizeMidPanes(_c[pane].dir === "horz" ? "" : "center"); + if (state.initialized || o.triggerEventsOnLoad) + _runCallbacks("onhide_end", pane); + } + else { + s.isHiding = true; // used by onclose + close(pane, false, noAnimation); // adjust all panes to fit + } + } + + /** + * Show a hidden pane - show as 'closed' by default unless openPane = true + * + * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west + * @param {boolean=} [openPane=false] + * @param {boolean=} [noAnimation=false] + * @param {boolean=} [noAlert=false] + */ +, show = function (evt_or_pane, openPane, noAnimation, noAlert) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , o = options[pane] + , s = state[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + ; + if (pane === "center" || !$P || !s.isHidden) return; // pane does not exist OR is not hidden + + // onshow_start callback - will CANCEL show if returns false + if (false === _runCallbacks("onshow_start", pane)) return; + + s.isShowing = true; // used by onopen/onclose + //s.isHidden = false; - will be set by open/close - if not cancelled + s.isSliding = false; // just in case + delete state.panesSliding[pane]; + + // now show the elements + //if ($R) $R.show(); - will be shown by open/close + if (openPane === false) + close(pane, true); // true = force + else + open(pane, false, noAnimation, noAlert); // adjust all panes to fit + } + + + /** + * Toggles a pane open/closed by calling either open or close + * + * @param {(string|Object)} evt_or_pane The pane being toggled, ie: north, south, east, or west + * @param {boolean=} [slide=false] + */ +, toggle = function (evt_or_pane, slide) { + if (!isInitialized()) return; + var evt = evtObj(evt_or_pane) + , pane = evtPane.call(this, evt_or_pane) + , s = state[pane] + ; + if (evt) // called from to $R.dblclick OR triggerPaneEvent + evt.stopImmediatePropagation(); + if (s.isHidden) + show(pane); // will call 'open' after unhiding it + else if (s.isClosed) + open(pane, !!slide); + else + close(pane); + } + + + /** + * Utility method used during init or other auto-processes + * + * @param {string} pane The pane being closed + * @param {boolean=} [setHandles=false] + */ +, _closePane = function (pane, setHandles) { + var + $P = $Ps[pane] + , s = state[pane] + ; + _hidePane(pane); + s.isClosed = true; + s.isVisible = false; + if (setHandles) setAsClosed(pane); + } + + /** + * Close the specified pane (animation optional), and resize all other panes as needed + * + * @param {(string|Object)} evt_or_pane The pane being closed, ie: north, south, east, or west + * @param {boolean=} [force=false] + * @param {boolean=} [noAnimation=false] + * @param {boolean=} [skipCallback=false] + */ +, close = function (evt_or_pane, force, noAnimation, skipCallback) { + var pane = evtPane.call(this, evt_or_pane); + if (pane === "center") return; // validate + // if pane has been initialized, but NOT the complete layout, close pane instantly + if (!state.initialized && $Ps[pane]) { + _closePane(pane, true); // INIT pane as closed + return; + } + if (!isInitialized()) return; + + var + $P = $Ps[pane] + , $R = $Rs[pane] + , $T = $Ts[pane] + , o = options[pane] + , s = state[pane] + , c = _c[pane] + , doFX, isShowing, isHiding, wasSliding; + + // QUEUE in case another action/animation is in progress + $N.queue(function( queueNext ){ + + if ( !$P + || (!o.closable && !s.isShowing && !s.isHiding) // invalid request // (!o.resizable && !o.closable) ??? + || (!force && s.isClosed && !s.isShowing) // already closed + ) return queueNext(); + + // onclose_start callback - will CANCEL hide if returns false + // SKIP if just 'showing' a hidden pane as 'closed' + var abort = !s.isShowing && false === _runCallbacks("onclose_start", pane); + + // transfer logic vars to temp vars + isShowing = s.isShowing; + isHiding = s.isHiding; + wasSliding = s.isSliding; + // now clear the logic vars (REQUIRED before aborting) + delete s.isShowing; + delete s.isHiding; + + if (abort) return queueNext(); + + doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none"); + s.isMoving = true; + s.isClosed = true; + s.isVisible = false; + // update isHidden BEFORE sizing panes + if (isHiding) s.isHidden = true; + else if (isShowing) s.isHidden = false; + + if (s.isSliding) // pane is being closed, so UNBIND trigger events + bindStopSlidingEvents(pane, false); // will set isSliding=false + else // resize panes adjacent to this one + sizeMidPanes(_c[pane].dir === "horz" ? "" : "center", false); // false = NOT skipCallback + + // if this pane has a resizer bar, move it NOW - before animation + setAsClosed(pane); + + // CLOSE THE PANE + if (doFX) { // animate the close + 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) close_2(); + queueNext(); + }); + } + else { // hide the pane without animation + _hidePane(pane); + close_2(); + queueNext(); + }; + }); + + // SUBROUTINE + function close_2 () { + s.isMoving = false; + bindStartSlidingEvents(pane, true); // will enable if o.slidable = true + + // if opposite-pane was autoClosed, see if it can be autoOpened now + var altPane = _c.oppositeEdge[pane]; + if (state[ altPane ].noRoom) { + setSizeLimits( altPane ); + makePaneFit( altPane ); + } + + if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) { + // onclose callback - UNLESS just 'showing' a hidden pane as 'closed' + if (!isShowing) _runCallbacks("onclose_end", pane); + // onhide OR onshow callback + if (isShowing) _runCallbacks("onshow_end", pane); + if (isHiding) _runCallbacks("onhide_end", pane); + } + } + } + + /** + * @param {string} pane The pane just closed, ie: north, south, east, or west + */ +, setAsClosed = function (pane) { + if (!$Rs[pane]) return; // handles not initialized yet! + var + $P = $Ps[pane] + , $R = $Rs[pane] + , $T = $Ts[pane] + , o = options[pane] + , s = state[pane] + , side = _c[pane].side + , rClass = o.resizerClass + , tClass = o.togglerClass + , _pane = "-"+ pane // used for classNames + , _open = "-open" + , _sliding= "-sliding" + , _closed = "-closed" + ; + $R + .css(side, sC.inset[side]) // move the resizer + .removeClass( rClass+_open +" "+ rClass+_pane+_open ) + .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding ) + .addClass( rClass+_closed +" "+ rClass+_pane+_closed ) + ; + // handle already-hidden panes in case called by swap() or a similar method + if (s.isHidden) $R.hide(); // hide resizer-bar + + // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvents? + if (o.resizable && $.layout.plugins.draggable) + $R + .draggable("disable") + .removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here + .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.tips.Open) // may be blank + ; + // toggler-content - if exists + $T.children(".content-open").hide(); + $T.children(".content-closed").css("display","block"); + } + + // sync any 'pin buttons' + syncPinBtns(pane, false); + + if (state.initialized) { + // resize 'length' and position togglers for adjacent panes + sizeHandles(); + } + } + + /** + * Open the specified pane (animation optional), and resize all other panes as needed + * + * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west + * @param {boolean=} [slide=false] + * @param {boolean=} [noAnimation=false] + * @param {boolean=} [noAlert=false] + */ +, open = function (evt_or_pane, slide, noAnimation, noAlert) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $P = $Ps[pane] + , $R = $Rs[pane] + , $T = $Ts[pane] + , o = options[pane] + , s = state[pane] + , c = _c[pane] + , doFX, isShowing + ; + if (pane === "center") return; // validate + // QUEUE in case another action/animation is in progress + $N.queue(function( queueNext ){ + + if ( !$P + || (!o.resizable && !o.closable && !s.isShowing) // invalid request + || (s.isVisible && !s.isSliding) // already open + ) return queueNext(); + + // pane can ALSO be unhidden by just calling show(), so handle this scenario + if (s.isHidden && !s.isShowing) { + queueNext(); // call before show() because it needs the queue free + show(pane, true); + return; + } + + if (s.autoResize && s.size != o.size) // resize pane to original size set in options + sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize + else + // make sure there is enough space available to open the pane + setSizeLimits(pane, slide); + + // onopen_start callback - will CANCEL open if returns false + var cbReturn = _runCallbacks("onopen_start", pane); + + if (cbReturn === "abort") + return queueNext(); + + // update pane-state again in case options were changed in onopen_start + if (cbReturn !== "NC") // NC = "No Callback" + setSizeLimits(pane, slide); + + if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN! + syncPinBtns(pane, false); // make sure pin-buttons are reset + if (!noAlert && o.tips.noRoomToOpen) + alert(o.tips.noRoomToOpen); + return queueNext(); // ABORT + } + + if (slide) // START Sliding - will set isSliding=true + bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane + else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead + bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false + else if (o.slidable) + bindStartSlidingEvents(pane, false); // UNBIND trigger events + + s.noRoom = false; // will be reset by makePaneFit if 'noRoom' + makePaneFit(pane); + + // transfer logic var to temp var + isShowing = s.isShowing; + // now clear the logic var + delete s.isShowing; + + doFX = !noAnimation && s.isClosed && (o.fxName_open != "none"); + s.isMoving = true; + s.isVisible = true; + s.isClosed = false; + // update isHidden BEFORE sizing panes - WHY??? Old? + if (isShowing) s.isHidden = false; + + if (doFX) { // ANIMATE + // mask adjacent panes with objects + 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.isVisible) open_2(); // continue + queueNext(); + }); + } + else { // no animation + _showPane(pane);// just show pane and... + open_2(); // continue + queueNext(); + }; + }); + + // SUBROUTINE + function open_2 () { + s.isMoving = false; + + // cure iframe display issues + _fixIframe(pane); + + // 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" : "", false); // false = NOT skipCallback + } + + // set classes, position handles and execute callbacks... + setAsOpen(pane); + }; + + } + + /** + * @param {string} pane The pane just opened, ie: north, south, east, or west + * @param {boolean=} [skipCallback=false] + */ +, setAsOpen = function (pane, skipCallback) { + var + $P = $Ps[pane] + , $R = $Rs[pane] + , $T = $Ts[pane] + , o = options[pane] + , s = state[pane] + , side = _c[pane].side + , rClass = o.resizerClass + , tClass = o.togglerClass + , _pane = "-"+ pane // used for classNames + , _open = "-open" + , _closed = "-closed" + , _sliding= "-sliding" + ; + $R + .css(side, sC.inset[side] + getPaneSize(pane)) // move the resizer + .removeClass( rClass+_closed +" "+ rClass+_pane+_closed ) + .addClass( rClass+_open +" "+ rClass+_pane+_open ) + ; + if (s.isSliding) + $R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding ) + else // in case 'was sliding' + $R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding ) + + removeHover( 0, $R ); // remove hover classes + if (o.resizable && $.layout.plugins.draggable) + $R .draggable("enable") + .css("cursor", o.resizerCursor) + .attr("title", o.tips.Resize); + else if (!s.isSliding) + $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.tips.Close); // may be blank + removeHover( 0, $T ); // remove hover classes + // toggler-content - if exists + $T.children(".content-closed").hide(); + $T.children(".content-open").css("display","block"); + } + + // sync any 'pin buttons' + syncPinBtns(pane, !s.isSliding); + + // update pane-state dimensions - BEFORE resizing content + $.extend(s, elDims($P)); + + if (state.initialized) { + // resize resizer & toggler sizes for all panes + sizeHandles(); + // resize content every time pane opens - to be sure + sizeContent(pane, true); // true = remeasure headers/footers, even if 'pane.isMoving' + } + + if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) { + // onopen callback + _runCallbacks("onopen_end", pane); + // onshow callback - TODO: should this be here? + if (s.isShowing) _runCallbacks("onshow_end", pane); + + // ALSO call onresize because layout-size *may* have changed while pane was closed + if (state.initialized) + _runCallbacks("onresize_end", pane); + } + + // TODO: Somehow sizePane("north") is being called after this point??? + } + + + /** + * slideOpen / slideClose / slideToggle + * + * Pass-though methods for sliding + */ +, slideOpen = function (evt_or_pane) { + if (!isInitialized()) return; + var evt = evtObj(evt_or_pane) + , pane = evtPane.call(this, evt_or_pane) + , s = state[pane] + , delay = options[pane].slideDelay_open + ; + if (pane === "center") return; // validate + // prevent event from triggering on NEW resizer binding created below + if (evt) evt.stopImmediatePropagation(); + + if (s.isClosed && evt && evt.type === "mouseenter" && delay > 0) + // trigger = mouseenter - use a delay + timer.set(pane+"_openSlider", open_NOW, delay); + else + open_NOW(); // will unbind events if is already open + + /** + * SUBROUTINE for timed open + */ + function open_NOW () { + if (!s.isClosed) // skip if no longer closed! + bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane + else if (!s.isMoving) + open(pane, true); // true = slide - open() will handle binding + }; + } + +, slideClose = function (evt_or_pane) { + if (!isInitialized()) return; + var evt = evtObj(evt_or_pane) + , pane = evtPane.call(this, evt_or_pane) + , o = options[pane] + , s = state[pane] + , delay = s.isMoving ? 1000 : 300 // MINIMUM delay - option may override + ; + if (pane === "center") return; // validate + 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 if (o.preventQuickSlideClose && s.isMoving) + return; // handle Chrome quick-close on slide-open + else if (o.preventPrematureSlideClose && evt && $.layout.isMouseOverElem(evt, $Ps[pane])) + return; // handle incorrect mouseleave trigger, like when over a SELECT-list in IE + else if (evt) // trigger = mouseleave - use a delay + // 1 sec delay if 'opening', else .3 sec + timer.set(pane+"_closeSlider", close_NOW, max(o.slideDelay_close, delay)); + else // called programically + close_NOW(); + + /** + * SUBROUTINE for timed close + */ + function close_NOW () { + if (s.isClosed) // skip 'close' if already closed! + bindStopSlidingEvents(pane, false); // UNBIND trigger events - TODO: is this needed here? + else if (!s.isMoving) + close(pane); // close will handle unbinding + }; + } + + /** + * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west + */ +, slideToggle = function (evt_or_pane) { + var pane = evtPane.call(this, evt_or_pane); + toggle(pane, true); + } + + + /** + * 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 + */ +, lockPaneForFX = function (pane, doLock) { + var $P = $Ps[pane] + , s = state[pane] + , o = options[pane] + , z = options.zIndexes + ; + if (doLock) { + showMasks( pane, { animation: true, objectsOnly: true }); + $P.css({ zIndex: z.pane_animate }); // overlay all elements during animation + if (pane=="south") + $P.css({ top: sC.inset.top + sC.innerHeight - $P.outerHeight() }); + else if (pane=="east") + $P.css({ left: sC.inset.left + sC.innerWidth - $P.outerWidth() }); + } + else { // animation DONE - RESET CSS + hideMasks(); + $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) }); + if (pane=="south") + $P.css({ top: "auto" }); + // if pane is positioned 'off-screen', then DO NOT screw with it! + else if (pane=="east" && !$P.css("left").match(/\-99999/)) + $P.css({ left: "auto" }); + // fix anti-aliasing in IE - only needed for animations that change opacity + if (browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1) + $P[0].style.removeAttribute('filter'); + } + } + + + /** + * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger + * + * @see open(), close() + * @param {string} pane The pane to enable/disable, 'north', 'south', etc. + * @param {boolean} enable Enable or Disable sliding? + */ +, bindStartSlidingEvents = function (pane, enable) { + var o = options[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + , evtName = o.slideTrigger_open.toLowerCase() + ; + if (!$R || (enable && !o.slidable)) return; + + // make sure we have a valid event + if (evtName.match(/mouseover/)) + evtName = o.slideTrigger_open = "mouseenter"; + else if (!evtName.match(/(click|dblclick|mouseenter)/)) + evtName = o.slideTrigger_open = "click"; + + // must remove double-click-toggle when using dblclick-slide + if (o.resizerDblClickToggle && evtName.match(/click/)) { + $R[enable ? "unbind" : "bind"]('dblclick.'+ sID, toggle) + } + + $R + // add or remove event + [enable ? "bind" : "unbind"](evtName +'.'+ sID, slideOpen) + // set the appropriate cursor & title/tip + .css("cursor", enable ? o.sliderCursor : "default") + .attr("title", enable ? o.tips.Slide : "") + ; + } + + /** + * Add or remove 'mouseleave' events to 'slide close' when pane is 'sliding' open or closed + * Also increases zIndex when pane is sliding open + * See bindStartSlidingEvents for code to control 'slide open' + * + * @see slideOpen(), slideClose() + * @param {string} pane The pane to process, 'north', 'south', etc. + * @param {boolean} enable Enable or Disable events? + */ +, bindStopSlidingEvents = function (pane, enable) { + var o = options[pane] + , s = state[pane] + , c = _c[pane] + , z = options.zIndexes + , evtName = o.slideTrigger_close.toLowerCase() + , action = (enable ? "bind" : "unbind") + , $P = $Ps[pane] + , $R = $Rs[pane] + ; + timer.clear(pane+"_closeSlider"); // just in case + + if (enable) { + s.isSliding = true; + state.panesSliding[pane] = true; + // remove 'slideOpen' event from resizer + // ALSO will raise the zIndex of the pane & resizer + bindStartSlidingEvents(pane, false); + } + else { + s.isSliding = false; + delete state.panesSliding[pane]; + } + + // RE/SET zIndex - increases when pane is sliding-open, resets to normal when not + $P.css("zIndex", enable ? z.pane_sliding : z.pane_normal); + $R.css("zIndex", enable ? z.pane_sliding+2 : z.resizer_normal); // NOTE: mask = pane_sliding+1 + + // make sure we have a valid event + if (!evtName.match(/(click|mouseleave)/)) + evtName = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout' + + // add/remove slide triggers + $R[action](evtName, slideClose); // base event on resize + // need extra events for mouseleave + if (evtName === "mouseleave") { + // also close on pane.mouseleave + $P[action]("mouseleave."+ sID, slideClose); + // cancel timer when mouse moves between 'pane' and 'resizer' + $R[action]("mouseenter."+ sID, cancelMouseOut); + $P[action]("mouseenter."+ sID, cancelMouseOut); + } + + if (!enable) + timer.clear(pane+"_closeSlider"); + else if (evtName === "click" && !o.resizable) { + // IF pane is not resizable (which already has a cursor and tip) + // then set the a cursor & title/tip on resizer when sliding + $R.css("cursor", enable ? o.sliderCursor : "default"); + $R.attr("title", enable ? o.tips.Close : ""); // use Toggler-tip, eg: "Close Pane" + } + + // SUBROUTINE for mouseleave timer clearing + function cancelMouseOut (evt) { + timer.clear(pane+"_closeSlider"); + evt.stopPropagation(); + } + } + + + /** + * Hides/closes a pane if there is insufficient room - reverses this when there is room again + * MUST have already called setSizeLimits() before calling this method + * + * @param {string} pane The pane being resized + * @param {boolean=} [isOpening=false] Called from onOpen? + * @param {boolean=} [skipCallback=false] Should the onresize callback be run? + * @param {boolean=} [force=false] + */ +, makePaneFit = function (pane, isOpening, skipCallback, force) { + var o = options[pane] + , s = state[pane] + , c = _c[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + , isSidePane = c.dir==="vert" + , hasRoom = false + ; + // special handling for center & east/west panes + if (pane === "center" || (isSidePane && s.noVerticalRoom)) { + // see if there is enough room to display the pane + // ERROR: hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth); + hasRoom = (s.maxHeight >= 0); + if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now + _showPane(pane); + if ($R) $R.show(); + s.isVisible = true; + s.noRoom = false; + if (isSidePane) s.noVerticalRoom = false; + _fixIframe(pane); + } + else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now + _hidePane(pane); + if ($R) $R.hide(); + s.isVisible = false; + s.noRoom = true; + } + } + + // see if there is enough room to fit the border-pane + if (pane === "center") { + // ignore center in this block + } + else if (s.minSize <= s.maxSize) { // pane CAN fit + hasRoom = true; + if (s.size > s.maxSize) // pane is too big - shrink it + sizePane(pane, s.maxSize, skipCallback, true, force); // true = noAnimation + else if (s.size < s.minSize) // pane is too small - enlarge it + sizePane(pane, s.minSize, skipCallback, true, force); // true = noAnimation + // need s.isVisible because new pseudoClose method keeps pane visible, but off-screen + else if ($R && s.isVisible && $P.is(":visible")) { + // make sure resizer-bar is positioned correctly + // handles situation where nested layout was 'hidden' when initialized + var pos = s.size + sC.inset[c.side]; + if ($.layout.cssNum( $R, c.side ) != pos) $R.css( c.side, pos ); + } + + // if was previously hidden due to noRoom, then RESET because NOW there is room + if (s.noRoom) { + // s.noRoom state will be set by open or show + if (s.wasOpen && o.closable) { + if (o.autoReopen) + open(pane, false, true, true); // true = noAnimation, true = noAlert + else // leave the pane closed, so just update state + s.noRoom = false; + } + else + show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert + } + } + else { // !hasRoom - pane CANNOT fit + if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now... + s.noRoom = true; // update state + s.wasOpen = !s.isClosed && !s.isSliding; + if (s.isClosed){} // SKIP + else if (o.closable) // 'close' if possible + close(pane, true, true); // true = force, true = noAnimation + else // 'hide' pane if cannot just be closed + hide(pane, true); // true = noAnimation + } + } + } + + + /** + * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized' + * + * @param {(string|Object)} evt_or_pane The pane being resized + * @param {number} size The *desired* new size for this pane - will be validated + * @param {boolean=} [skipCallback=false] Should the onresize callback be run? + * @param {boolean=} [noAnimation=false] + * @param {boolean=} [force=false] Force resizing even if does not seem necessary + */ +, manualSizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , o = options[pane] + , s = state[pane] + // if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete... + , forceResize = force || (o.livePaneResizing && !s.isResizing) + ; + if (pane === "center") return; // validate + // ANY call to manualSizePane disables autoResize - ie, percentage sizing + s.autoResize = false; + // flow-through... + sizePane(pane, size, skipCallback, noAnimation, forceResize); // will animate resize if option enabled + } + + /** + * sizePane is called only by internal methods whenever a pane needs to be resized + * + * @param {(string|Object)} evt_or_pane The pane being resized + * @param {number} size The *desired* new size for this pane - will be validated + * @param {boolean=} [skipCallback=false] Should the onresize callback be run? + * @param {boolean=} [noAnimation=false] + * @param {boolean=} [force=false] Force resizing even if does not seem necessary + */ +, sizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) // probably NEVER called from event? + , o = options[pane] + , s = state[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + , side = _c[pane].side + , dimName = _c[pane].sizeType.toLowerCase() + , skipResizeWhileDragging = s.isResizing && !o.triggerEventsDuringLiveResize + , doFX = noAnimation !== true && o.animatePaneSizing + , oldSize, newSize + ; + if (pane === "center") return; // validate + // QUEUE in case another action/animation is in progress + $N.queue(function( queueNext ){ + // calculate 'current' min/max sizes + setSizeLimits(pane); // update pane-state + oldSize = s.size; + size = _parseSize(pane, size); // handle percentages & auto + size = max(size, _parseSize(pane, o.minSize)); + size = min(size, s.maxSize); + if (size < s.minSize) { // not enough room for pane! + queueNext(); // call before makePaneFit() because it needs the queue free + makePaneFit(pane, false, skipCallback); // will hide or close pane + return; + } + + // IF newSize is same as oldSize, then nothing to do - abort + if (!force && size === oldSize) + return queueNext(); + + s.newSize = size; + + // onresize_start callback CANNOT cancel resizing because this would break the layout! + if (!skipCallback && state.initialized && s.isVisible) + _runCallbacks("onresize_start", pane); + + // resize the pane, and make sure its visible + newSize = cssSize(pane, size); + + if (doFX && $P.is(":visible")) { // ANIMATE + var fx = $.layout.effects.size[pane] || $.layout.effects.size.all + , easing = o.fxSettings_size.easing || fx.easing + , z = options.zIndexes + , props = {}; + props[ dimName ] = newSize +'px'; + s.isMoving = true; + // overlay all elements during animation + $P.css({ zIndex: z.pane_animate }) + .show().animate( props, o.fxSpeed_size, easing, function(){ + // reset zIndex after animation + $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) }); + s.isMoving = false; + delete s.newSize; + sizePane_2(); // continue + queueNext(); + }); + } + else { // no animation + $P.css( dimName, newSize ); // resize pane + delete s.newSize; + // if pane is visible, then + if ($P.is(":visible")) + sizePane_2(); // continue + else { + // pane is NOT VISIBLE, so just update state data... + // when pane is *next opened*, it will have the new size + s.size = size; // update state.size + //$.extend(s, elDims($P)); // update state dimensions - CANNOT do this when not visible! } + } + queueNext(); + }; + + }); + + // SUBROUTINE + function sizePane_2 () { + /* Panes are sometimes not sized precisely in some browsers!? + * This code will resize the pane up to 3 times to nudge the pane to the correct size + */ + var actual = dimName==='width' ? $P.outerWidth() : $P.outerHeight() + , tries = [{ + pane: pane + , count: 1 + , target: size + , actual: actual + , correct: (size === actual) + , attempt: size + , cssSize: newSize + }] + , lastTry = tries[0] + , thisTry = {} + , msg = 'Inaccurate size after resizing the '+ pane +'-pane.' + ; + while ( !lastTry.correct ) { + thisTry = { pane: pane, count: lastTry.count+1, target: size }; + + if (lastTry.actual > size) + thisTry.attempt = max(0, lastTry.attempt - (lastTry.actual - size)); + else // lastTry.actual < size + thisTry.attempt = max(0, lastTry.attempt + (size - lastTry.actual)); + + thisTry.cssSize = cssSize(pane, thisTry.attempt); + $P.css( dimName, thisTry.cssSize ); + + thisTry.actual = dimName=='width' ? $P.outerWidth() : $P.outerHeight(); + thisTry.correct = (size === thisTry.actual); + + // log attempts and alert the user of this *non-fatal error* (if showDebugMessages) + if ( tries.length === 1) { + _log(msg, false, true); + _log(lastTry, false, true); + } + _log(thisTry, false, true); + // after 4 tries, is as close as its gonna get! + if (tries.length > 3) break; + + tries.push( thisTry ); + lastTry = tries[ tries.length - 1 ]; + } + // END TESTING CODE + + // update pane-state dimensions + s.size = size; + $.extend(s, elDims($P)); + + if (s.isVisible && $P.is(":visible")) { + // reposition the resizer-bar + if ($R) $R.css( side, size + sC.inset[side] ); + // resize the content-div + sizeContent(pane); + } + + if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible) + _runCallbacks("onresize_end", pane); + + // resize all the adjacent panes, and adjust their toggler buttons + // when skipCallback passed, it means the controlling method will handle 'other panes' + if (!skipCallback) { + // also no callback if live-resize is in progress and NOT triggerEventsDuringLiveResize + if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "" : "center", skipResizeWhileDragging, force); + sizeHandles(); + } + + // if opposite-pane was autoClosed, see if it can be autoOpened now + var altPane = _c.oppositeEdge[pane]; + if (size < oldSize && state[ altPane ].noRoom) { + setSizeLimits( altPane ); + makePaneFit( altPane, false, skipCallback ); + } + + // DEBUG - ALERT user/developer so they know there was a sizing problem + if (tries.length > 1) + _log(msg +'\nSee the Error Console for details.', true, true); + } + } + + /** + * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide() + * @param {(Array.|string)} panes The pane(s) being resized, comma-delmited string + * @param {boolean=} [skipCallback=false] Should the onresize callback be run? + * @param {boolean=} [force=false] + */ +, sizeMidPanes = function (panes, skipCallback, force) { + panes = (panes ? panes : "east,west,center").split(","); + + $.each(panes, function (i, pane) { + if (!$Ps[pane]) return; // NO PANE - skip + var + o = options[pane] + , s = state[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + , isCenter= (pane=="center") + , hasRoom = true + , CSS = {} + // if pane is not visible, show it invisibly NOW rather than for *each call* in this script + , visCSS = $.layout.showInvisibly($P) + + , newCenter = calcNewCenterPaneDims() + ; + + // update pane-state dimensions + $.extend(s, elDims($P)); + + if (pane === "center") { + if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight) { + $P.css(visCSS); + return true; // SKIP - pane already the correct size + } + // set state for makePaneFit() logic + $.extend(s, cssMinDims(pane), { + maxWidth: newCenter.width + , maxHeight: newCenter.height + }); + CSS = newCenter; + s.newWidth = CSS.width; + s.newHeight = CSS.height; + // convert OUTER width/height to CSS width/height + CSS.width = cssW($P, CSS.width); + // NEW - allow pane to extend 'below' visible area rather than hide it + CSS.height = cssH($P, CSS.height); + hasRoom = CSS.width >= 0 && CSS.height >= 0; // height >= 0 = ALWAYS TRUE NOW + + // during layout init, try to shrink east/west panes to make room for center + if (!state.initialized && o.minWidth > newCenter.width) { + var + reqPx = o.minWidth - s.outerWidth + , minE = options.east.minSize || 0 + , minW = options.west.minSize || 0 + , sizeE = state.east.size + , sizeW = state.west.size + , newE = sizeE + , newW = sizeW + ; + if (reqPx > 0 && state.east.isVisible && sizeE > minE) { + newE = max( sizeE-minE, sizeE-reqPx ); + reqPx -= sizeE-newE; + } + if (reqPx > 0 && state.west.isVisible && sizeW > minW) { + newW = max( sizeW-minW, sizeW-reqPx ); + reqPx -= sizeW-newW; + } + // IF we found enough extra space, then resize the border panes as calculated + if (reqPx === 0) { + if (sizeE && sizeE != minE) + sizePane('east', newE, true, true, force); // true = skipCallback/noAnimation - initPanes will handle when done + if (sizeW && sizeW != minW) + sizePane('west', newW, true, true, force); // true = skipCallback/noAnimation + // now start over! + sizeMidPanes('center', skipCallback, force); + $P.css(visCSS); + return; // abort this loop + } + } + } + else { // for east and west, set only the height, which is same as center height + // set state.min/maxWidth/Height for makePaneFit() logic + if (s.isVisible && !s.noVerticalRoom) + $.extend(s, elDims($P), cssMinDims(pane)) + if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight) { + $P.css(visCSS); + return true; // SKIP - pane already the correct size + } + // east/west have same top, bottom & height as center + CSS.top = newCenter.top; + CSS.bottom = newCenter.bottom; + s.newSize = newCenter.height + // NEW - allow pane to extend 'below' visible area rather than hide it + CSS.height = cssH($P, newCenter.height); + s.maxHeight = CSS.height; + hasRoom = (s.maxHeight >= 0); // ALWAYS TRUE NOW + if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic + } + + if (hasRoom) { + // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized + if (!skipCallback && state.initialized) + _runCallbacks("onresize_start", pane); + + $P.css(CSS); // apply the CSS to pane + if (pane !== "center") + sizeHandles(pane); // also update resizer length + if (s.noRoom && !s.isClosed && !s.isHidden) + makePaneFit(pane); // will re-open/show auto-closed/hidden pane + if (s.isVisible) { + $.extend(s, elDims($P)); // update pane dimensions + if (state.initialized) sizeContent(pane); // also resize the contents, if exists + } + } + else if (!s.noRoom && s.isVisible) // no room for pane + makePaneFit(pane); // will hide or close pane + + // reset visibility, if necessary + $P.css(visCSS); + + delete s.newSize; + delete s.newWidth; + delete s.newHeight; + + if (!s.isVisible) + return true; // DONE - next pane + + /* + * 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 + * ALSO required when pane is an IFRAME because will NOT default to 'full width' + * TODO: Can I use width:100% for a north/south iframe? + * TODO: Sounds like a job for $P.outerWidth( sC.innerWidth ) SETTER METHOD + */ + if (pane === "center") { // finished processing midPanes + var fix = browser.isIE6 || !browser.boxModel; + if ($Ps.north && (fix || state.north.tagName=="IFRAME")) + $Ps.north.css("width", cssW($Ps.north, sC.innerWidth)); + if ($Ps.south && (fix || state.south.tagName=="IFRAME")) + $Ps.south.css("width", cssW($Ps.south, sC.innerWidth)); + } + + // resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized + if (!skipCallback && state.initialized) + _runCallbacks("onresize_end", pane); + }); + } + + + /** + * @see window.onresize(), callbacks or custom code + * @param {(Object|boolean)=} evt_or_refresh If 'true', then also reset pane-positioning + */ +, resizeAll = function (evt_or_refresh) { + var oldW = sC.innerWidth + , oldH = sC.innerHeight + ; + // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility + evtPane(evt_or_refresh); + + // cannot size layout when 'container' is hidden or collapsed + if (!$N.is(":visible")) return; + + if (!state.initialized) { + _initLayoutElements(); + return; // no need to resize since we just initialized! + } + + if (evt_or_refresh === true && $.isPlainObject(options.outset)) { + // update container CSS in case outset option has changed + $N.css( options.outset ); + } + // UPDATE container dimensions + $.extend(sC, elDims( $N, options.inset )); + if (!sC.outerHeight) return; + + // if 'true' passed, refresh pane & handle positioning too + if (evt_or_refresh === true) { + setPanePosition(); + } + + // onresizeall_start will CANCEL resizing if returns false + // state.container has already been set, so user can access this info for calcuations + if (false === _runCallbacks("onresizeall_start")) return false; + + var // see if container is now 'smaller' than before + shrunkH = (sC.innerHeight < oldH) + , shrunkW = (sC.innerWidth < oldW) + , $P, o, s + ; + // NOTE special order for sizing: S-N-E-W + $.each(["south","north","east","west"], function (i, pane) { + if (!$Ps[pane]) return; // no pane - SKIP + o = options[pane]; + s = state[pane]; + if (s.autoResize && s.size != o.size) // resize pane to original size set in options + sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize + else { + setSizeLimits(pane); + makePaneFit(pane, false, true, true); // true=skipCallback/forceResize + } + }); + + sizeMidPanes("", true, true); // true=skipCallback/forceResize + sizeHandles(); // reposition the toggler elements + + // trigger all individual pane callbacks AFTER layout has finished resizing + $.each(_c.allPanes, function (i, pane) { + $P = $Ps[pane]; + if (!$P) return; // SKIP + if (state[pane].isVisible) // undefined for non-existent panes + _runCallbacks("onresize_end", pane); // callback - if exists + }); + + _runCallbacks("onresizeall_end"); + //_triggerLayoutEvent(pane, 'resizeall'); + } + + /** + * Whenever a pane resizes or opens that has a nested layout, trigger resizeAll + * + * @param {(string|Object)} evt_or_pane The pane just resized or opened + */ +, resizeChildren = function (evt_or_pane, skipRefresh) { + var pane = evtPane.call(this, evt_or_pane); + + if (!options[pane].resizeChildren) return; + + // ensure the pane-children are up-to-date + if (!skipRefresh) refreshChildren( pane ); + var pC = children[pane]; + if ($.isPlainObject( pC )) { + // resize one or more children + $.each( pC, function (key, child) { + if (!child.destroyed) child.resizeAll(); + }); + } + } + + /** + * IF pane has a content-div, then resize all elements inside pane to fit pane-height + * + * @param {(string|Object)} evt_or_panes The pane(s) being resized + * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured? + */ +, sizeContent = function (evt_or_panes, remeasure) { + if (!isInitialized()) return; + + var panes = evtPane.call(this, evt_or_panes); + panes = panes ? panes.split(",") : _c.allPanes; + + $.each(panes, function (idx, pane) { + var + $P = $Ps[pane] + , $C = $Cs[pane] + , o = options[pane] + , s = state[pane] + , m = s.content // m = measurements + ; + if (!$P || !$C || !$P.is(":visible")) return true; // NOT VISIBLE - skip + + // if content-element was REMOVED, update OR remove the pointer + if (!$C.length) { + initContent(pane, false); // false = do NOT sizeContent() - already there! + if (!$C) return; // no replacement element found - pointer have been removed + } + + // onsizecontent_start will CANCEL resizing if returns false + if (false === _runCallbacks("onsizecontent_start", pane)) return; + + // skip re-measuring offsets if live-resizing + if ((!s.isMoving && !s.isResizing) || o.liveContentResizing || remeasure || m.top == undefined) { + _measure(); + // if any footers are below pane-bottom, they may not measure correctly, + // so allow pane overflow and re-measure + if (m.hiddenFooters > 0 && $P.css("overflow") === "hidden") { + $P.css("overflow", "visible"); + _measure(); // remeasure while overflowing + $P.css("overflow", "hidden"); + } + } + // NOTE: spaceAbove/Below *includes* the pane paddingTop/Bottom, but not pane.borders + var newH = s.innerHeight - (m.spaceAbove - s.css.paddingTop) - (m.spaceBelow - s.css.paddingBottom); + + if (!$C.is(":visible") || m.height != newH) { + // size the Content element to fit new pane-size - will autoHide if not enough room + setOuterHeight($C, newH, true); // true=autoHide + m.height = newH; // save new height + }; + + if (state.initialized) + _runCallbacks("onsizecontent_end", pane); + + function _below ($E) { + return max(s.css.paddingBottom, (parseInt($E.css("marginBottom"), 10) || 0)); + }; + + function _measure () { + var + ignore = options[pane].contentIgnoreSelector + , $Fs = $C.nextAll().not(".ui-layout-mask").not(ignore || ":lt(0)") // not :lt(0) = ALL + , $Fs_vis = $Fs.filter(':visible') + , $F = $Fs_vis.filter(':last') + ; + m = { + top: $C[0].offsetTop + , height: $C.outerHeight() + , numFooters: $Fs.length + , hiddenFooters: $Fs.length - $Fs_vis.length + , spaceBelow: 0 // correct if no content footer ($E) + } + m.spaceAbove = m.top; // just for state - not used in calc + m.bottom = m.top + m.height; + if ($F.length) + //spaceBelow = (LastFooter.top + LastFooter.height) [footerBottom] - Content.bottom + max(LastFooter.marginBottom, pane.paddingBotom) + m.spaceBelow = ($F[0].offsetTop + $F.outerHeight()) - m.bottom + _below($F); + else // no footer - check marginBottom on Content element itself + m.spaceBelow = _below($C); + }; + }); + } + + + /** + * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary + * + * @see initHandles(), open(), close(), resizeAll() + * @param {(string|Object)=} evt_or_panes The pane(s) being resized + */ +, sizeHandles = function (evt_or_panes) { + var panes = evtPane.call(this, evt_or_panes) + panes = panes ? panes.split(",") : _c.borderPanes; + + $.each(panes, function (i, pane) { + var + o = options[pane] + , s = state[pane] + , $P = $Ps[pane] + , $R = $Rs[pane] + , $T = $Ts[pane] + , $TC + ; + if (!$P || !$R) return; + + var + dir = _c[pane].dir + , _state = (s.isClosed ? "_closed" : "_open") + , spacing = o["spacing"+ _state] + , togAlign = o["togglerAlign"+ _state] + , togLen = o["togglerLength"+ _state] + , paneLen + , left + , 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(); // s.outerWidth || + paneLen = sC.innerWidth; // handle offscreen-panes + s.resizerLength = paneLen; + left = $.layout.cssNum($P, "left") + $R.css({ + width: cssW($R, paneLen) // account for borders & padding + , height: cssH($R, spacing) // ditto + , left: left > -9999 ? left : sC.inset.left // handle offscreen-panes + }); + } + else { // east/west + paneLen = $P.outerHeight(); // s.outerHeight || + s.resizerLength = paneLen; + $R.css({ + height: cssH($R, paneLen) // account for borders & padding + , width: cssW($R, spacing) // ditto + , top: sC.inset.top + getPaneSize("north", true) // TODO: what if no North pane? + //, top: $.layout.cssNum($Ps["center"], "top") + }); + } + + // remove hover classes + removeHover( o, $R ); + + 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 (isStr(togAlign)) { + switch (togAlign) { + case "top": + case "left": offset = 0; + break; + case "bottom": + case "right": offset = paneLen - togLen; + break; + case "middle": + case "center": + default: offset = round((paneLen - togLen) / 2); // 'default' catches typos + } + } + else { // togAlign = number + var x = parseInt(togAlign, 10); // + if (togAlign >= 0) offset = x; + else offset = paneLen - togLen + x; // NOTE: x is negative! + } + } + + if (dir === "horz") { // north/south + var width = cssW($T, togLen); + $T.css({ + width: width // account for borders & padding + , height: cssH($T, spacing) // ditto + , left: offset // TODO: VERIFY that toggler positions correctly for ALL values + , top: 0 + }); + // CENTER the toggler content SPAN +// $T.children(".content").each(function(){ +// $T.children(".ui-content").each(function(){ // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 +// $TC = $(this); +// $TC.css("marginLeft", round((width-$TC.outerWidth())/2)); // could be negative +// }); + // ThinkGem 以上4行代码不需要,因为在css里会自动居中 + } + else { // east/west + var height = cssH($T, togLen); + $T.css({ + height: height // account for borders & padding + , width: cssW($T, spacing) // ditto + , top: offset // POSITION the toggler + , left: 0 + }); + // CENTER the toggler content SPAN +// $T.children(".content").each(function(){ + $T.children(".ui-content").each(function(){ // ThinkGem content 改为 ui-content 解决与AdminLTE类名冲突 + $TC = $(this); + $TC.css("marginTop", round((height-$TC.outerHeight())/2)); // could be negative + }); + } + + // remove ALL hover classes + removeHover( 0, $T ); + } + + // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now + if (!state.initialized && (o.initHidden || s.isHidden)) { + $R.hide(); + if ($T) $T.hide(); + } + }); + } + + + /** + * @param {(string|Object)} evt_or_pane + */ +, enableClosable = function (evt_or_pane) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $T = $Ts[pane] + , o = options[pane] + ; + if (!$T) return; + o.closable = true; + $T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); }) + .css("visibility", "visible") + .css("cursor", "pointer") + .attr("title", state[pane].isClosed ? o.tips.Open : o.tips.Close) // may be blank + .show(); + } + /** + * @param {(string|Object)} evt_or_pane + * @param {boolean=} [hide=false] + */ +, disableClosable = function (evt_or_pane, hide) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $T = $Ts[pane] + ; + if (!$T) return; + options[pane].closable = false; + // is closable is disable, then pane MUST be open! + if (state[pane].isClosed) open(pane, false, true); + $T .unbind("."+ sID) + .css("visibility", hide ? "hidden" : "visible") // instead of hide(), which creates logic issues + .css("cursor", "default") + .attr("title", ""); + } + + + /** + * @param {(string|Object)} evt_or_pane + */ +, enableSlidable = function (evt_or_pane) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $R = $Rs[pane] + ; + if (!$R || !$R.data('draggable')) return; + options[pane].slidable = true; + if (state[pane].isClosed) + bindStartSlidingEvents(pane, true); + } + /** + * @param {(string|Object)} evt_or_pane + */ +, disableSlidable = function (evt_or_pane) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $R = $Rs[pane] + ; + if (!$R) return; + options[pane].slidable = false; + if (state[pane].isSliding) + close(pane, false, true); + else { + bindStartSlidingEvents(pane, false); + $R .css("cursor", "default") + .attr("title", ""); + removeHover(null, $R[0]); // in case currently hovered + } + } + + + /** + * @param {(string|Object)} evt_or_pane + */ +, enableResizable = function (evt_or_pane) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $R = $Rs[pane] + , o = options[pane] + ; + if (!$R || !$R.data('draggable')) return; + o.resizable = true; + $R.draggable("enable"); + if (!state[pane].isClosed) + $R .css("cursor", o.resizerCursor) + .attr("title", o.tips.Resize); + } + /** + * @param {(string|Object)} evt_or_pane + */ +, disableResizable = function (evt_or_pane) { + if (!isInitialized()) return; + var pane = evtPane.call(this, evt_or_pane) + , $R = $Rs[pane] + ; + if (!$R || !$R.data('draggable')) return; + options[pane].resizable = false; + $R .draggable("disable") + .css("cursor", "default") + .attr("title", ""); + removeHover(null, $R[0]); // in case currently hovered + } + + + /** + * Move a pane from source-side (eg, west) to target-side (eg, east) + * If pane exists on target-side, move that to source-side, ie, 'swap' the panes + * + * @param {(string|Object)} evt_or_pane1 The pane/edge being swapped + * @param {string} pane2 ditto + */ +, swapPanes = function (evt_or_pane1, pane2) { + if (!isInitialized()) return; + var pane1 = evtPane.call(this, evt_or_pane1); + // change state.edge NOW so callbacks can know where pane is headed... + state[pane1].edge = pane2; + state[pane2].edge = pane1; + // run these even if NOT state.initialized + if (false === _runCallbacks("onswap_start", pane1) + || false === _runCallbacks("onswap_start", pane2) + ) { + state[pane1].edge = pane1; // reset + state[pane2].edge = pane2; + return; + } + + var + oPane1 = copy( pane1 ) + , oPane2 = copy( pane2 ) + , sizes = {} + ; + sizes[pane1] = oPane1 ? oPane1.state.size : 0; + sizes[pane2] = oPane2 ? oPane2.state.size : 0; + + // clear pointers & state + $Ps[pane1] = false; + $Ps[pane2] = false; + state[pane1] = {}; + state[pane2] = {}; + + // ALWAYS remove the resizer & toggler elements + if ($Ts[pane1]) $Ts[pane1].remove(); + if ($Ts[pane2]) $Ts[pane2].remove(); + if ($Rs[pane1]) $Rs[pane1].remove(); + if ($Rs[pane2]) $Rs[pane2].remove(); + $Rs[pane1] = $Rs[pane2] = $Ts[pane1] = $Ts[pane2] = false; + + // transfer element pointers and data to NEW Layout keys + move( oPane1, pane2 ); + move( oPane2, pane1 ); + + // cleanup objects + oPane1 = oPane2 = sizes = null; + + // make panes 'visible' again + if ($Ps[pane1]) $Ps[pane1].css(_c.visible); + if ($Ps[pane2]) $Ps[pane2].css(_c.visible); + + // fix any size discrepancies caused by swap + resizeAll(); + + // run these even if NOT state.initialized + _runCallbacks("onswap_end", pane1); + _runCallbacks("onswap_end", pane2); + + return; + + function copy (n) { // n = pane + var + $P = $Ps[n] + , $C = $Cs[n] + ; + return !$P ? false : { + pane: n + , P: $P ? $P[0] : false + , C: $C ? $C[0] : false + , state: $.extend(true, {}, state[n]) + , options: $.extend(true, {}, options[n]) + } + }; + + function move (oPane, pane) { + if (!oPane) return; + var + P = oPane.P + , C = oPane.C + , oldPane = oPane.pane + , c = _c[pane] + // save pane-options that should be retained + , s = $.extend(true, {}, state[pane]) + , o = options[pane] + // RETAIN side-specific FX Settings - more below + , fx = { resizerCursor: o.resizerCursor } + , re, size, pos + ; + $.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) { + fx[k +"_open"] = o[k +"_open"]; + fx[k +"_close"] = o[k +"_close"]; + fx[k +"_size"] = o[k +"_size"]; + }); + + // update object pointers and attributes + $Ps[pane] = $(P) + .data({ + layoutPane: Instance[pane] // NEW pointer to pane-alias-object + , layoutEdge: pane + }) + .css(_c.hidden) + .css(c.cssReq) + ; + $Cs[pane] = C ? $(C) : false; + + // set options and state + options[pane] = $.extend(true, {}, oPane.options, fx); + state[pane] = $.extend(true, {}, oPane.state); + + // change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west + re = new RegExp(o.paneClass +"-"+ oldPane, "g"); + P.className = P.className.replace(re, o.paneClass +"-"+ pane); + + // ALWAYS regenerate the resizer & toggler elements + initHandles(pane); // create the required resizer & toggler + + // if moving to different orientation, then keep 'target' pane size + if (c.dir != _c[oldPane].dir) { + size = sizes[pane] || 0; + setSizeLimits(pane); // update pane-state + size = max(size, state[pane].minSize); + // use manualSizePane to disable autoResize - not useful after panes are swapped + manualSizePane(pane, size, true, true); // true/true = skipCallback/noAnimation + } + else // move the resizer here + $Rs[pane].css(c.side, sC.inset[c.side] + (state[pane].isVisible ? getPaneSize(pane) : 0)); + + + // ADD CLASSNAMES & SLIDE-BINDINGS + if (oPane.state.isVisible && !s.isVisible) + setAsOpen(pane, true); // true = skipCallback + else { + setAsClosed(pane); + bindStartSlidingEvents(pane, true); // will enable events IF option is set + } + + // DESTROY the object + oPane = null; + }; + } + + + /** + * INTERNAL method to sync pin-buttons when pane is opened or closed + * Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes + * + * @see open(), setAsOpen(), setAsClosed() + * @param {string} pane These are the params returned to callbacks by layout() + * @param {boolean} doPin True means set the pin 'down', False means 'up' + */ +, syncPinBtns = function (pane, doPin) { + if ($.layout.plugins.buttons) + $.each(state[pane].pins, function (i, selector) { + $.layout.buttons.setPinState(Instance, $(selector), pane, doPin); + }); + } + +; // END var DECLARATIONS + + /** + * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed + * + * @see 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 - $.ui.keyCode.UP + , 40: "south" // Down Cursor - $.ui.keyCode.DOWN + , 37: "west" // Left Cursor - $.ui.keyCode.LEFT + , 39: "east" // Right Cursor - $.ui.keyCode.RIGHT + } + , ALT = evt.altKey // no worky! + , SHIFT = evt.shiftKey + , CTRL = evt.ctrlKey + , CURSOR = (CTRL && code >= 37 && code <= 40) + , o, k, m, pane + ; + + if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey + pane = PANE[code]; + else if (CTRL || SHIFT) // check to see if this matches a custom-hotkey + $.each(_c.borderPanes, 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 + } + } + }); + + // validate pane + if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden) + return true; + + toggle(pane); + + evt.stopPropagation(); + evt.returnValue = false; // CANCEL key + return false; + }; + + +/* + * ###################################### + * UTILITY METHODS + * called externally or by initButtons + * ###################################### + */ + + /** + * Change/reset a pane overflow setting & zIndex to allow popups/drop-downs to work + * + * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event + */ + function allowOverflow (el) { + if (!isInitialized()) return; + if (this && this.tagName) el = this; // BOUND to element + var $P; + if (isStr(el)) + $P = $Ps[el]; + else if ($(el).data("layoutRole")) + $P = $(el); + else + $(el).parents().each(function(){ + if ($(this).data("layoutRole")) { + $P = $(this); + return false; // BREAK + } + }); + if (!$P || !$P.length) return; // INVALID + + var + pane = $P.data("layoutEdge") + , 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 its closed, then abort + if (s.isSliding || s.isResizing || s.isClosed) { + s.cssSaved = false; + return; + } + + var + newCSS = { zIndex: (options.zIndexes.resizer_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.match(/(visible|auto)/)) { + curCSS.overflowX = ofX; + newCSS.overflowX = "visible"; + } + if (ofY && !ofY.match(/(visible|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, function(i, p) { + if (p != pane) resetOverflow(p); + }); + + }; + /** + * @param {Object=} [el] (optional) Can also be 'bound' to a click, mouseOver, or other event + */ + function resetOverflow (el) { + if (!isInitialized()) return; + if (this && this.tagName) el = this; // BOUND to element + var $P; + if (isStr(el)) + $P = $Ps[el]; + else if ($(el).data("layoutRole")) + $P = $(el); + else + $(el).parents().each(function(){ + if ($(this).data("layoutRole")) { + $P = $(this); + return false; // BREAK + } + }); + if (!$P || !$P.length) return; // INVALID + + var + pane = $P.data("layoutEdge") + , s = state[pane] + , CSS = s.cssSaved || {} + ; + // reset the zIndex + if (!s.isSliding && !s.isResizing) + $P.css("zIndex", options.zIndexes.pane_normal); + + // reset Overflow - if necessary + $P.css( CSS ); + + // clear var + s.cssSaved = false; + }; + +/* + * ##################### + * CREATE/RETURN LAYOUT + * ##################### + */ + + // validate that container exists + var $N = $(this).eq(0); // FIRST matching Container element + if (!$N.length) { + return _log( options.errors.containerMissing ); + }; + + // Users retrieve Instance of a layout with: $N.layout() OR $N.data("layout") + // return the Instance-pointer if layout has already been initialized + if ($N.data("layoutContainer") && $N.data("layout")) + return $N.data("layout"); // cached pointer + + // init global vars + var + $Ps = {} // Panes x5 - set in initPanes() + , $Cs = {} // Content x5 - set in initPanes() + , $Rs = {} // Resizers x4 - set in initHandles() + , $Ts = {} // Togglers x4 - set in initHandles() + , $Ms = $([]) // Masks - up to 2 masks per pane (IFRAME + DIV) + // aliases for code brevity + , sC = state.container // alias for easy access to 'container dimensions' + , sID = state.id // alias for unique layout ID/namespace - eg: "layout435" + ; + + // create Instance object to expose data & option Properties, and primary action Methods + var Instance = { + // layout data + options: options // property - options hash + , state: state // property - dimensions hash + // object pointers + , container: $N // property - object pointers for layout container + , panes: $Ps // property - object pointers for ALL Panes: panes.north, panes.center + , contents: $Cs // property - object pointers for ALL Content: contents.north, contents.center + , resizers: $Rs // property - object pointers for ALL Resizers, eg: resizers.north + , togglers: $Ts // property - object pointers for ALL Togglers, eg: togglers.north + // border-pane open/close + , hide: hide // method - ditto + , show: show // method - ditto + , toggle: toggle // method - pass a 'pane' ("north", "west", etc) + , open: open // method - ditto + , close: close // method - ditto + , slideOpen: slideOpen // method - ditto + , slideClose: slideClose // method - ditto + , slideToggle: slideToggle // method - ditto + // pane actions + , setSizeLimits: setSizeLimits // method - pass a 'pane' - update state min/max data + , _sizePane: sizePane // method -intended for user by plugins only! + , sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto' + , sizeContent: sizeContent // method - pass a 'pane' + , swapPanes: swapPanes // method - pass TWO 'panes' - will swap them + , showMasks: showMasks // method - pass a 'pane' OR list of panes - default = all panes with mask option set + , hideMasks: hideMasks // method - ditto' + // pane element methods + , initContent: initContent // method - ditto + , addPane: addPane // method - pass a 'pane' + , removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem + , createChildren: createChildren // method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].children + , refreshChildren: refreshChildren // method - pass a 'pane' and a layout-instance + // special pane option setting + , enableClosable: enableClosable // method - pass a 'pane' + , disableClosable: disableClosable // method - ditto + , enableSlidable: enableSlidable // method - ditto + , disableSlidable: disableSlidable // method - ditto + , enableResizable: enableResizable // method - ditto + , disableResizable: disableResizable// method - ditto + // utility methods for panes + , allowOverflow: allowOverflow // utility - pass calling element (this) + , resetOverflow: resetOverflow // utility - ditto + // layout control + , destroy: destroy // method - no parameters + , initPanes: isInitialized // method - no parameters + , resizeAll: resizeAll // method - no parameters + // callback triggering + , runCallbacks: _runCallbacks // method - pass evtName & pane (if a pane-event), eg: trigger("onopen", "west") + // alias collections of options, state and children - created in addPane and extended elsewhere + , hasParentLayout: false // set by initContainer() + , children: children // pointers to child-layouts, eg: Instance.children.west.layoutName + , north: false // alias group: { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], children: children[pane] } + , south: false // ditto + , west: false // ditto + , east: false // ditto + , center: false // ditto + }; + + // create the border layout NOW + if (_create() === 'cancel') // onload_start callback returned false to CANCEL layout creation + return null; + else // true OR false -- if layout-elements did NOT init (hidden or do not exist), can auto-init later + return Instance; // return the Instance object + +} + + +})( jQuery ); + + + + +/** + * jquery.layout.state 1.2 + * $Date: 2014-08-30 08:00:00 (Sat, 30 Aug 2014) $ + * + * Copyright (c) 2014 + * Kevin Dalman (http://allpro.net) + * + * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) + * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. + * + * @requires: UI Layout 1.4.0 or higher + * @requires: $.ui.cookie (above) + * + * @see: http://groups.google.com/group/jquery-ui-layout + */ +;(function ($) { + +if (!$.layout) return; + + +/** + * UI COOKIE UTILITY + * + * A $.cookie OR $.ui.cookie namespace *should be standard*, but until then... + * This creates $.ui.cookie so Layout does not need the cookie.jquery.js plugin + * NOTE: This utility is REQUIRED by the layout.state plugin + * + * Cookie methods in Layout are created as part of State Management + */ +if (!$.ui) $.ui = {}; +$.ui.cookie = { + + // cookieEnabled is not in DOM specs, but DOES works in all browsers,including IE6 + acceptsCookies: !!navigator.cookieEnabled + +, read: function (name) { + var + c = document.cookie + , cs = c ? c.split(';') : [] + , pair, data, i + ; + for (i=0; pair=cs[i]; i++) { + data = $.trim(pair).split('='); // name=value => [ name, value ] + if (data[0] == name) // found the layout cookie + return decodeURIComponent(data[1]); + } + return null; + } + +, write: function (name, val, cookieOpts) { + var params = "" + , date = "" + , clear = false + , o = cookieOpts || {} + , x = o.expires || null + , t = $.type(x) + ; + if (t === "date") + date = x; + else if (t === "string" && x > 0) { + x = parseInt(x,10); + t = "number"; + } + if (t === "number") { + date = new Date(); + if (x > 0) + date.setDate(date.getDate() + x); + else { + date.setFullYear(1970); + clear = true; + } + } + if (date) params += ";expires="+ date.toUTCString(); + if (o.path) params += ";path="+ o.path; + if (o.domain) params += ";domain="+ o.domain; + if (o.secure) params += ";secure"; + document.cookie = name +"="+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie + } + +, clear: function (name) { + $.ui.cookie.write(name, "", {expires: -1}); + } + +}; +// if cookie.jquery.js is not loaded, create an alias to replicate it +// this may be useful to other plugins or code dependent on that plugin +if (!$.cookie) $.cookie = function (k, v, o) { + var C = $.ui.cookie; + if (v === null) + C.clear(k); + else if (v === undefined) + return C.read(k); + else + C.write(k, v, o); +}; + + + +/** + * State-management options stored in options.stateManagement, which includes a .cookie hash + * Default options saves ALL KEYS for ALL PANES, ie: pane.size, pane.isClosed, pane.isHidden + * + * // STATE/COOKIE OPTIONS + * @example $(el).layout({ + stateManagement: { + enabled: true + , stateKeys: "east.size,west.size,east.isClosed,west.isClosed" + , cookie: { name: "appLayout", path: "/" } + } + }) + * @example $(el).layout({ stateManagement__enabled: true }) // enable auto-state-management using cookies + * @example $(el).layout({ stateManagement__cookie: { name: "appLayout", path: "/" } }) + * @example $(el).layout({ stateManagement__cookie__name: "appLayout", stateManagement__cookie__path: "/" }) + * + * // STATE/COOKIE METHODS + * @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} ); + * @example myLayout.loadCookie(); + * @example myLayout.deleteCookie(); + * @example var JSON = myLayout.readState(); // CURRENT Layout State + * @example var JSON = myLayout.readCookie(); // SAVED Layout State (from cookie) + * @example var JSON = myLayout.state.stateData; // LAST LOADED Layout State (cookie saved in layout.state hash) + * + * CUSTOM STATE-MANAGEMENT (eg, saved in a database) + * @example var JSON = myLayout.readState( "west.isClosed,north.size,south.isHidden" ); + * @example myLayout.loadState( JSON ); + */ + +// tell Layout that the state plugin is available +$.layout.plugins.stateManagement = true; + +// Add State-Management options to layout.defaults +$.layout.defaults.stateManagement = { + enabled: false // true = enable state-management, even if not using cookies +, autoSave: true // Save a state-cookie when page exits? +, autoLoad: true // Load the state-cookie when Layout inits? +, animateLoad: true // animate panes when loading state into an active layout +, includeChildren: true // recurse into child layouts to include their state as well + // List state-data to save - must be pane-specific +, stateKeys: "north.size,south.size,east.size,west.size,"+ + "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+ + "north.isHidden,south.isHidden,east.isHidden,west.isHidden" +, cookie: { + name: "" // If not specified, will use Layout.name, else just "Layout" + , domain: "" // blank = current domain + , path: "" // blank = current page, "/" = entire website + , expires: "" // 'days' to keep cookie - leave blank for 'session cookie' + , secure: false + } +}; + +// Set stateManagement as a 'layout-option', NOT a 'pane-option' +$.layout.optionsMap.layout.push("stateManagement"); +// Update config so layout does not move options into the pane-default branch (panes) +$.layout.config.optionRootKeys.push("stateManagement"); + +/* + * State Management methods + */ +$.layout.state = { + + /** + * Get the current layout state and save it to a cookie + * + * myLayout.saveCookie( keys, cookieOpts ) + * + * @param {Object} inst + * @param {(string|Array)=} keys + * @param {Object=} cookieOpts + */ + saveCookie: function (inst, keys, cookieOpts) { + var o = inst.options + , sm = o.stateManagement + , oC = $.extend(true, {}, sm.cookie, cookieOpts || null) + , data = inst.state.stateData = inst.readState( keys || sm.stateKeys ) // read current panes-state + ; + $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC ); + return $.extend(true, {}, data); // return COPY of state.stateData data + } + + /** + * Remove the state cookie + * + * @param {Object} inst + */ +, deleteCookie: function (inst) { + var o = inst.options; + $.ui.cookie.clear( o.stateManagement.cookie.name || o.name || "Layout" ); + } + + /** + * Read & return data from the cookie - as JSON + * + * @param {Object} inst + */ +, readCookie: function (inst) { + var o = inst.options; + var c = $.ui.cookie.read( o.stateManagement.cookie.name || o.name || "Layout" ); + // convert cookie string back to a hash and return it + return c ? $.layout.state.decodeJSON(c) : {}; + } + + /** + * Get data from the cookie and USE IT to loadState + * + * @param {Object} inst + */ +, loadCookie: function (inst) { + var c = $.layout.state.readCookie(inst); // READ the cookie + if (c && !$.isEmptyObject( c )) { + inst.state.stateData = $.extend(true, {}, c); // SET state.stateData + inst.loadState(c); // LOAD the retrieved state + } + return c; + } + + /** + * Update layout options from the cookie, if one exists + * + * @param {Object} inst + * @param {Object=} stateData + * @param {boolean=} animate + */ +, loadState: function (inst, data, opts) { + if (!$.isPlainObject( data ) || $.isEmptyObject( data )) return; + + // normalize data & cache in the state object + data = inst.state.stateData = $.layout.transformData( data ); // panes = default subkey + + // add missing/default state-restore options + var smo = inst.options.stateManagement; + opts = $.extend({ + animateLoad: false //smo.animateLoad + , includeChildren: smo.includeChildren + }, opts ); + + if (!inst.state.initialized) { + /* + * layout NOT initialized, so just update its options + */ + // MUST remove pane.children keys before applying to options + // use a copy so we don't remove keys from original data + var o = $.extend(true, {}, data); + //delete o.center; // center has no state-data - only children + $.each($.layout.config.allPanes, function (idx, pane) { + if (o[pane]) delete o[pane].children; + }); + // update CURRENT layout-options with saved state data + $.extend(true, inst.options, o); + } + else { + /* + * layout already initialized, so modify layout's configuration + */ + var noAnimate = !opts.animateLoad + , o, c, h, state, open + ; + $.each($.layout.config.borderPanes, function (idx, pane) { + o = data[ pane ]; + if (!$.isPlainObject( o )) return; // no key, skip pane + + s = o.size; + c = o.initClosed; + h = o.initHidden; + ar = o.autoResize + state = inst.state[pane]; + open = state.isVisible; + + // reset autoResize + if (ar) + state.autoResize = ar; + // resize BEFORE opening + if (!open) + inst._sizePane(pane, s, false, false, false); // false=skipCallback/noAnimation/forceResize + // open/close as necessary - DO NOT CHANGE THIS ORDER! + if (h === true) inst.hide(pane, noAnimate); + else if (c === true) inst.close(pane, false, noAnimate); + else if (c === false) inst.open (pane, false, noAnimate); + else if (h === false) inst.show (pane, false, noAnimate); + // resize AFTER any other actions + if (open) + inst._sizePane(pane, s, false, false, noAnimate); // animate resize if option passed + }); + + /* + * RECURSE INTO CHILD-LAYOUTS + */ + if (opts.includeChildren) { + var paneStateChildren, childState; + $.each(inst.children, function (pane, paneChildren) { + paneStateChildren = data[pane] ? data[pane].children : 0; + if (paneStateChildren && paneChildren) { + $.each(paneChildren, function (stateKey, child) { + childState = paneStateChildren[stateKey]; + if (child && childState) + child.loadState( childState ); + }); + } + }); + } + } + } + + /** + * Get the *current layout state* and return it as a hash + * + * @param {Object=} inst // Layout instance to get state for + * @param {object=} [opts] // State-Managements override options + */ +, readState: function (inst, opts) { + // backward compatility + if ($.type(opts) === 'string') opts = { keys: opts }; + if (!opts) opts = {}; + var sm = inst.options.stateManagement + , ic = opts.includeChildren + , recurse = ic !== undefined ? ic : sm.includeChildren + , keys = opts.stateKeys || sm.stateKeys + , alt = { isClosed: 'initClosed', isHidden: 'initHidden' } + , state = inst.state + , panes = $.layout.config.allPanes + , data = {} + , pair, pane, key, val + , ps, pC, child, array, count, branch + ; + if ($.isArray(keys)) keys = keys.join(","); + // convert keys to an array and change delimiters from '__' to '.' + keys = keys.replace(/__/g, ".").split(','); + // loop keys and create a data hash + for (var i=0, n=keys.length; i < n; i++) { + pair = keys[i].split("."); + pane = pair[0]; + key = pair[1]; + if ($.inArray(pane, panes) < 0) continue; // bad pane! + val = state[ pane ][ key ]; + if (val == undefined) continue; + if (key=="isClosed" && state[pane]["isSliding"]) + val = true; // if sliding, then *really* isClosed + ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val; + } + + // recurse into the child-layouts for each pane + if (recurse) { + $.each(panes, function (idx, pane) { + pC = inst.children[pane]; + ps = state.stateData[pane]; + if ($.isPlainObject( pC ) && !$.isEmptyObject( pC )) { + // ensure a key exists for this 'pane', eg: branch = data.center + branch = data[pane] || (data[pane] = {}); + if (!branch.children) branch.children = {}; + $.each( pC, function (key, child) { + // ONLY read state from an initialize layout + if ( child.state.initialized ) + branch.children[ key ] = $.layout.state.readState( child ); + // if we have PREVIOUS (onLoad) state for this child-layout, KEEP IT! + else if ( ps && ps.children && ps.children[ key ] ) { + branch.children[ key ] = $.extend(true, {}, ps.children[ key ] ); + } + }); + } + }); + } + + return data; + } + + /** + * Stringify a JSON hash so can save in a cookie or db-field + */ +, encodeJSON: function (json) { + var local = window.JSON || {}; + return (local.stringify || stringify)(json); + + function stringify (h) { + var D=[], i=0, k, v, t // k = key, v = value + , a = $.isArray(h) + ; + for (k in h) { + v = h[k]; + t = typeof v; + if (t == 'string') // STRING - add quotes + v = '"'+ v +'"'; + else if (t == 'object') // SUB-KEY - recurse into it + v = parse(v); + D[i++] = (!a ? '"'+ k +'":' : '') + v; + } + return (a ? '[' : '{') + D.join(',') + (a ? ']' : '}'); + }; + } + + /** + * Convert stringified JSON back to a hash object + * @see $.parseJSON(), adding in jQuery 1.4.1 + */ +, decodeJSON: function (str) { + try { return $.parseJSON ? $.parseJSON(str) : window["eval"]("("+ str +")") || {}; } + catch (e) { return {}; } + } + + +, _create: function (inst) { + var s = $.layout.state + , o = inst.options + , sm = o.stateManagement + ; + // ADD State-Management plugin methods to inst + $.extend( inst, { + // readCookie - update options from cookie - returns hash of cookie data + readCookie: function () { return s.readCookie(inst); } + // deleteCookie + , deleteCookie: function () { s.deleteCookie(inst); } + // saveCookie - optionally pass keys-list and cookie-options (hash) + , saveCookie: function (keys, cookieOpts) { return s.saveCookie(inst, keys, cookieOpts); } + // loadCookie - readCookie and use to loadState() - returns hash of cookie data + , loadCookie: function () { return s.loadCookie(inst); } + // loadState - pass a hash of state to use to update options + , loadState: function (stateData, opts) { s.loadState(inst, stateData, opts); } + // readState - returns hash of current layout-state + , readState: function (keys) { return s.readState(inst, keys); } + // add JSON utility methods too... + , encodeJSON: s.encodeJSON + , decodeJSON: s.decodeJSON + }); + + // init state.stateData key, even if plugin is initially disabled + inst.state.stateData = {}; + + // autoLoad MUST BE one of: data-array, data-hash, callback-function, or TRUE + if ( !sm.autoLoad ) return; + + // When state-data exists in the autoLoad key USE IT, + // even if stateManagement.enabled == false + if ($.isPlainObject( sm.autoLoad )) { + if (!$.isEmptyObject( sm.autoLoad )) { + inst.loadState( sm.autoLoad ); + } + } + else if ( sm.enabled ) { + // update the options from cookie or callback + // if options is a function, call it to get stateData + if ($.isFunction( sm.autoLoad )) { + var d = {}; + try { + d = sm.autoLoad( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn + } catch (e) {} + if (d && $.isPlainObject( d ) && !$.isEmptyObject( d )) + inst.loadState(d); + } + else // any other truthy value will trigger loadCookie + inst.loadCookie(); + } + } + +, _unload: function (inst) { + var sm = inst.options.stateManagement; + if (sm.enabled && sm.autoSave) { + // if options is a function, call it to save the stateData + if ($.isFunction( sm.autoSave )) { + try { + sm.autoSave( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn + } catch (e) {} + } + else // any truthy value will trigger saveCookie + inst.saveCookie(); + } + } + +}; + +// add state initialization method to Layout's onCreate array of functions +$.layout.onCreate.push( $.layout.state._create ); +$.layout.onUnload.push( $.layout.state._unload ); + +})( jQuery ); + + + +/** + * @preserve jquery.layout.buttons 1.0 + * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $ + * + * Copyright (c) 2011 + * Kevin Dalman (http://allpro.net) + * + * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) + * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. + * + * @dependancies: UI Layout 1.3.0.rc30.1 or higher + * + * @support: http://groups.google.com/group/jquery-ui-layout + * + * Docs: [ to come ] + * Tips: [ to come ] + */ +;(function ($) { + +if (!$.layout) return; + + +// tell Layout that the state plugin is available +$.layout.plugins.buttons = true; + +// Add State-Management options to layout.defaults +$.layout.defaults.autoBindCustomButtons = false; +// Set stateManagement as a layout-option, NOT a pane-option +$.layout.optionsMap.layout.push("autoBindCustomButtons"); + +/* + * Button methods + */ +$.layout.buttons = { + // set data used by multiple methods below + config: { + borderPanes: "north,south,west,east" + } + + /** + * Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons + * + * @see _create() + */ +, init: function (inst) { + var pre = "ui-layout-button-" + , layout = inst.options.name || "" + , name; + $.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) { + $.each($.layout.buttons.config.borderPanes.split(","), function (ii, pane) { + $("."+pre+action+"-"+pane).each(function(){ + // if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name' + name = $(this).data("layoutName") || $(this).attr("layoutName"); + if (name == undefined || name === layout) + inst.bindButton(this, action, pane); + }); + }); + }); + } + + /** + * Helper function to validate params received by addButton utilities + * + * Two classes are added to the element, based on the buttonClass... + * The type of button is appended to create the 2nd className: + * - ui-layout-button-pin + * - ui-layout-pane-button-toggle + * - ui-layout-pane-button-open + * - ui-layout-pane-button-close + * + * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" + * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. + * @return {Array.} If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise returns null + */ +, get: function (inst, selector, pane, action) { + var $E = $(selector) + , o = inst.options + //, err = o.showErrorMessages + ; + if ($E.length && $.layout.buttons.config.borderPanes.indexOf(pane) >= 0) { + var btn = o[pane].buttonClass +"-"+ action; + $E .addClass( btn +" "+ btn +"-"+ pane ) + .data("layoutName", o.name); // add layout identifier - even if blank! + } + return $E; + } + + + /** + * NEW syntax for binding layout-buttons - will eventually replace addToggle, addOpen, etc. + * + * @param {(string|!Object)} sel jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" + * @param {string} action + * @param {string} pane + */ +, bind: function (inst, sel, action, pane) { + var _ = $.layout.buttons; + switch (action.toLowerCase()) { + case "toggle": _.addToggle (inst, sel, pane); break; + case "open": _.addOpen (inst, sel, pane); break; + case "close": _.addClose (inst, sel, pane); break; + case "pin": _.addPin (inst, sel, pane); break; + case "toggle-slide": _.addToggle (inst, sel, pane, true); break; + case "open-slide": _.addOpen (inst, sel, pane, true); break; + } + return inst; + } + + /** + * Add a custom Toggler button for a pane + * + * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" + * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. + * @param {boolean=} slide true = slide-open, false = pin-open + */ +, addToggle: function (inst, selector, pane, slide) { + $.layout.buttons.get(inst, selector, pane, "toggle") + .click(function(evt){ + inst.toggle(pane, !!slide); + evt.stopPropagation(); + }); + return inst; + } + + /** + * Add a custom Open button for a pane + * + * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" + * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. + * @param {boolean=} slide true = slide-open, false = pin-open + */ +, addOpen: function (inst, selector, pane, slide) { + $.layout.buttons.get(inst, selector, pane, "open") + .attr("title", inst.options[pane].tips.Open) + .click(function (evt) { + inst.open(pane, !!slide); + evt.stopPropagation(); + }); + return inst; + } + + /** + * Add a custom Close button for a pane + * + * @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" + * @param {string} pane Name of the pane the button is for: 'north', 'south', etc. + */ +, addClose: function (inst, selector, pane) { + $.layout.buttons.get(inst, selector, pane, "close") + .attr("title", inst.options[pane].tips.Close) + .click(function (evt) { + inst.close(pane); + evt.stopPropagation(); + }); + return inst; + } + + /** + * 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|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button" + * @param {string} pane Name of the pane the pin is for: 'north', 'south', etc. + */ +, addPin: function (inst, selector, pane) { + var $E = $.layout.buttons.get(inst, selector, pane, "pin"); + if ($E.length) { + var s = inst.state[pane]; + $E.click(function (evt) { + $.layout.buttons.setPinState(inst, $(this), pane, (s.isSliding || s.isClosed)); + if (s.isSliding || s.isClosed) inst.open( pane ); // change from sliding to open + else inst.close( pane ); // slide-closed + evt.stopPropagation(); + }); + // add up/down pin attributes and classes + $.layout.buttons.setPinState(inst, $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 + s.pins.push( selector ); // just save the selector string + } + return inst; + } + + /** + * Change the class of the pin button to make it look 'up' or 'down' + * + * @see addPin(), syncPins() + * @param {Array.} $Pin The pin-span element in a jQuery wrapper + * @param {string} pane These are the params returned to callbacks by layout() + * @param {boolean} doPin true = set the pin 'down', false = set it 'up' + */ +, setPinState: function (inst, $Pin, pane, doPin) { + var updown = $Pin.attr("pin"); + if (updown && doPin === (updown=="down")) return; // already in correct state + var + po = inst.options[pane] + , lang = po.tips + , pin = po.buttonClass +"-pin" + , side = pin +"-"+ pane + , UP = pin +"-up "+ side +"-up" + , DN = pin +"-down "+side +"-down" + ; + $Pin + .attr("pin", doPin ? "down" : "up") // logic + .attr("title", doPin ? lang.Unpin : lang.Pin) + .removeClass( doPin ? UP : DN ) + .addClass( doPin ? DN : UP ) + ; + } + + /** + * 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 + * + * @see open(), close() + * @param {string} pane These are the params returned to callbacks by layout() + * @param {boolean} doPin True means set the pin 'down', False means 'up' + */ +, syncPinBtns: function (inst, pane, doPin) { + // REAL METHOD IS _INSIDE_ LAYOUT - THIS IS HERE JUST FOR REFERENCE + $.each(state[pane].pins, function (i, selector) { + $.layout.buttons.setPinState(inst, $(selector), pane, doPin); + }); + } + + +, _load: function (inst) { + // ADD Button methods to Layout Instance + $.extend( inst, { + bindButton: function (selector, action, pane) { return $.layout.buttons.bind(inst, selector, action, pane); } + // DEPRECATED METHODS... + , addToggleBtn: function (selector, pane, slide) { return $.layout.buttons.addToggle(inst, selector, pane, slide); } + , addOpenBtn: function (selector, pane, slide) { return $.layout.buttons.addOpen(inst, selector, pane, slide); } + , addCloseBtn: function (selector, pane) { return $.layout.buttons.addClose(inst, selector, pane); } + , addPinBtn: function (selector, pane) { return $.layout.buttons.addPin(inst, selector, pane); } + }); + + // init state array to hold pin-buttons + for (var i=0; i<4; i++) { + var pane = $.layout.buttons.config.borderPanes[i]; + inst.state[pane].pins = []; + } + + // auto-init buttons onLoad if option is enabled + if ( inst.options.autoBindCustomButtons ) + $.layout.buttons.init(inst); + } + +, _unload: function (inst) { + // TODO: unbind all buttons??? + } + +}; + +// add initialization method to Layout's onLoad array of functions +$.layout.onLoad.push( $.layout.buttons._load ); +//$.layout.onUnload.push( $.layout.buttons._unload ); + +})( jQuery ); + + + + +/** + * jquery.layout.browserZoom 1.0 + * $Date: 2011-12-29 08:00:00 (Thu, 29 Dec 2011) $ + * + * Copyright (c) 2012 + * Kevin Dalman (http://allpro.net) + * + * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) + * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. + * + * @requires: UI Layout 1.3.0.rc30.1 or higher + * + * @see: http://groups.google.com/group/jquery-ui-layout + * + * TODO: Extend logic to handle other problematic zooming in browsers + * TODO: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event + */ +(function ($) { + +// tell Layout that the plugin is available +$.layout.plugins.browserZoom = true; + +$.layout.defaults.browserZoomCheckInterval = 1000; +$.layout.optionsMap.layout.push("browserZoomCheckInterval"); + +/* + * browserZoom methods + */ +$.layout.browserZoom = { + + _init: function (inst) { + // abort if browser does not need this check + if ($.layout.browserZoom.ratio() !== false) + $.layout.browserZoom._setTimer(inst); + } + +, _setTimer: function (inst) { + // abort if layout destroyed or browser does not need this check + if (inst.destroyed) return; + var o = inst.options + , s = inst.state + // don't need check if inst has parentLayout, but check occassionally in case parent destroyed! + // MINIMUM 100ms interval, for performance + , ms = inst.hasParentLayout ? 5000 : Math.max( o.browserZoomCheckInterval, 100 ) + ; + // set the timer + setTimeout(function(){ + if (inst.destroyed || !o.resizeWithWindow) return; + var d = $.layout.browserZoom.ratio(); + if (d !== s.browserZoom) { + s.browserZoom = d; + inst.resizeAll(); + } + // set a NEW timeout + $.layout.browserZoom._setTimer(inst); + } + , ms ); + } + +, ratio: function () { + var w = window + , s = screen + , d = document + , dE = d.documentElement || d.body + , b = $.layout.browser + , v = b.version + , r, sW, cW + ; + // we can ignore all browsers that fire window.resize event onZoom + if (!b.msie || v > 8) + return false; // don't need to track zoom + if (s.deviceXDPI && s.systemXDPI) // syntax compiler hack + return calc(s.deviceXDPI, s.systemXDPI); + // everything below is just for future reference! + if (b.webkit && (r = d.body.getBoundingClientRect)) + return calc((r.left - r.right), d.body.offsetWidth); + if (b.webkit && (sW = w.outerWidth)) + return calc(sW, w.innerWidth); + if ((sW = s.width) && (cW = dE.clientWidth)) + return calc(sW, cW); + return false; // no match, so cannot - or don't need to - track zoom + + function calc (x,y) { return (parseInt(x,10) / parseInt(y,10) * 100).toFixed(); } + } + +}; +// add initialization method to Layout's onLoad array of functions +$.layout.onReady.push( $.layout.browserZoom._init ); + + +})( jQuery ); + + + + +/** + * UI Layout Plugin: Slide-Offscreen Animation + * + * Prevent panes from being 'hidden' so that an iframes/objects + * does not reload/refresh when pane 'opens' again. + * This plug-in adds a new animation called "slideOffscreen". + * It is identical to the normal "slide" effect, but avoids hiding the element + * + * Requires Layout 1.3.0.RC30.1 or later for Close offscreen + * Requires Layout 1.3.0.RC30.5 or later for Hide, initClosed & initHidden offscreen + * + * Version: 1.1 - 2012-11-18 + * Author: Kevin Dalman (kevin@jquery-dev.com) + * @preserve jquery.layout.slideOffscreen-1.1.js + */ +;(function ($) { + +// Add a new "slideOffscreen" effect +if ($.effects) { + + // add an option so initClosed and initHidden will work + $.layout.defaults.panes.useOffscreenClose = false; // user must enable when needed + /* set the new animation as the default for all panes + $.layout.defaults.panes.fxName = "slideOffscreen"; + */ + + if ($.layout.plugins) + $.layout.plugins.effects.slideOffscreen = true; + + // dupe 'slide' effect defaults as new effect defaults + $.layout.effects.slideOffscreen = $.extend(true, {}, $.layout.effects.slide); + + // add new effect to jQuery UI + $.effects.slideOffscreen = function(o) { + return this.queue(function(){ + + var fx = $.effects + , opt = o.options + , $el = $(this) + , pane = $el.data('layoutEdge') + , state = $el.data('parentLayout').state + , dist = state[pane].size + , s = this.style + , props = ['top','bottom','left','right'] + // Set options + , mode = fx.setMode($el, opt.mode || 'show') // Set Mode + , show = (mode == 'show') + , dir = opt.direction || 'left' // Default Direction + , ref = (dir == 'up' || dir == 'down') ? 'top' : 'left' + , pos = (dir == 'up' || dir == 'left') + , offscrn = $.layout.config.offscreenCSS || {} + , keyLR = $.layout.config.offscreenReset + , keyTB = 'offscreenResetTop' // only used internally + , animation = {} + ; + // Animation settings + animation[ref] = (show ? (pos ? '+=' : '-=') : (pos ? '-=' : '+=')) + dist; + + if (show) { // show() animation, so save top/bottom but retain left/right set when 'hidden' + $el.data(keyTB, { top: s.top, bottom: s.bottom }); + + // set the top or left offset in preparation for animation + // Note: ALL animations work by shifting the top or left edges + if (pos) { // top (north) or left (west) + $el.css(ref, isNaN(dist) ? "-" + dist : -dist); // Shift outside the left/top edge + } + else { // bottom (south) or right (east) - shift all the way across container + if (dir === 'right') + $el.css({ left: state.container.layoutWidth, right: 'auto' }); + else // dir === bottom + $el.css({ top: state.container.layoutHeight, bottom: 'auto' }); + } + // restore the left/right setting if is a top/bottom animation + if (ref === 'top') + $el.css( $el.data( keyLR ) || {} ); + } + else { // hide() animation, so save ALL CSS + $el.data(keyTB, { top: s.top, bottom: s.bottom }); + $el.data(keyLR, { left: s.left, right: s.right }); + } + + // Animate + $el.show().animate(animation, { queue: false, duration: o.duration, easing: opt.easing, complete: function(){ + // Restore top/bottom + if ($el.data( keyTB )) + $el.css($el.data( keyTB )).removeData( keyTB ); + if (show) // Restore left/right too + $el.css($el.data( keyLR ) || {}).removeData( keyLR ); + else // Move the pane off-screen (left: -99999, right: 'auto') + $el.css( offscrn ); + + if (o.callback) o.callback.apply(this, arguments); // Callback + $el.dequeue(); + }}); + + }); + }; + +} + +})( jQuery ); diff --git a/modules/core/src/main/java/com/jeesite/config/common/ShiroConfig.java b/modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java similarity index 96% rename from modules/core/src/main/java/com/jeesite/config/common/ShiroConfig.java rename to modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java index d2c652da..ba3fc416 100644 --- a/modules/core/src/main/java/com/jeesite/config/common/ShiroConfig.java +++ b/modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.common; +package com.jeesite.modules.config; import java.util.Map; diff --git a/modules/core/src/main/java/com/jeesite/modules/db/InitCoreData.xlsx b/modules/core/src/main/java/com/jeesite/modules/db/InitCoreData.xlsx index 00d7715d9cd51c685b6a0cbb41714e515b03dac3..2af18d5eeb0e67be58ff2e9c12873cf09fc57f05 100644 GIT binary patch delta 144224 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%0DrYhcOu2Gu!< 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 delta 144155 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

        m(`tFdX1&w> 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 V{tZX!z>iCzMRl;vW|CqR`3GjxE}sAZ diff --git a/modules/core/src/main/resources/jeesite-core.yml b/modules/core/src/main/resources/jeesite-core.yml index 725590c0..0d1a4489 100644 --- a/modules/core/src/main/resources/jeesite-core.yml +++ b/modules/core/src/main/resources/jeesite-core.yml @@ -132,13 +132,13 @@ page: # 用户相关参数 user: - # 指定超级管理员编号(实施人员,开发团队使用的用户) - superAdminCode: thinkgem + # 指定超级管理员编号(研发团队使用的账号) + superAdminCode: system # 超级管理员获取菜单的最小权重(默认20;>=40二级管理员;>=60系统管理员;>=80超级管理员) superAdminGetMenuMinWeight: 40 - # 系统管理员角色编号(客户方使用的角色,客户方管理员) + # 系统管理员角色编号(客户方管理员使用的角色) corpAdminRoleCode: corpAdmin # 用户类型配置信息(employee员工,member会员,btype往来单位,persion个人,expert专家,...) diff --git a/web/.settings/org.eclipse.wst.common.project.facet.core.xml b/web/.settings/org.eclipse.wst.common.project.facet.core.xml index 53f8cf25..af7ced35 100644 --- a/web/.settings/org.eclipse.wst.common.project.facet.core.xml +++ b/web/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -1,7 +1,7 @@ - - - - - - - \ No newline at end of file + + + + + + + diff --git a/web/pom.xml b/web/pom.xml index b1e1ff62..03eb8ac1 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -19,7 +19,7 @@ - com.jeesite.config.Application + com.jeesite.modules.config.Application false false diff --git a/web/src/main/java/com/jeesite/config/Application.java b/web/src/main/java/com/jeesite/modules/config/Application.java similarity index 88% rename from web/src/main/java/com/jeesite/config/Application.java rename to web/src/main/java/com/jeesite/modules/config/Application.java index 86678a0e..df1c9575 100644 --- a/web/src/main/java/com/jeesite/config/Application.java +++ b/web/src/main/java/com/jeesite/modules/config/Application.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config; +package com.jeesite.modules.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -17,7 +17,7 @@ import com.jeesite.common.io.PropertiesUtils; * @version 2018-1-8 */ @Profile("default") -@SpringBootApplication(scanBasePackages={"com.jeesite.config"}) +@SpringBootApplication(scanBasePackages={"com.jeesite.modules"}) public class Application extends SpringBootServletInitializer { public static void main(String[] args) { diff --git a/web/src/main/java/com/jeesite/config/task/MsgLocalSendTask.java b/web/src/main/java/com/jeesite/modules/config/task/MsgLocalSendTask.java similarity index 93% rename from web/src/main/java/com/jeesite/config/task/MsgLocalSendTask.java rename to web/src/main/java/com/jeesite/modules/config/task/MsgLocalSendTask.java index e3f113b1..87dc1966 100644 --- a/web/src/main/java/com/jeesite/config/task/MsgLocalSendTask.java +++ b/web/src/main/java/com/jeesite/modules/config/task/MsgLocalSendTask.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.task; +package com.jeesite.modules.config.task; /** * 消息发送服务,如果需要支持定时任务,则要在作业管理里添加该任务:msgLocalSendTask.execute(); diff --git a/web/src/main/java/com/jeesite/config/web/DruidStatConfig.java b/web/src/main/java/com/jeesite/modules/config/web/DruidStatConfig.java similarity index 93% rename from web/src/main/java/com/jeesite/config/web/DruidStatConfig.java rename to web/src/main/java/com/jeesite/modules/config/web/DruidStatConfig.java index 12080ee8..5ef2d134 100644 --- a/web/src/main/java/com/jeesite/config/web/DruidStatConfig.java +++ b/web/src/main/java/com/jeesite/modules/config/web/DruidStatConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.web; +package com.jeesite.modules.config.web; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; diff --git a/web/src/main/java/com/jeesite/config/web/FilterConfig.java b/web/src/main/java/com/jeesite/modules/config/web/FilterConfig.java similarity index 95% rename from web/src/main/java/com/jeesite/config/web/FilterConfig.java rename to web/src/main/java/com/jeesite/modules/config/web/FilterConfig.java index 309cf14c..847c348b 100644 --- a/web/src/main/java/com/jeesite/config/web/FilterConfig.java +++ b/web/src/main/java/com/jeesite/modules/config/web/FilterConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.web; +package com.jeesite.modules.config.web; import javax.servlet.Filter; diff --git a/web/src/main/java/com/jeesite/config/web/ListenerConfig.java b/web/src/main/java/com/jeesite/modules/config/web/ListenerConfig.java similarity index 92% rename from web/src/main/java/com/jeesite/config/web/ListenerConfig.java rename to web/src/main/java/com/jeesite/modules/config/web/ListenerConfig.java index 09173d16..268ac5dc 100644 --- a/web/src/main/java/com/jeesite/config/web/ListenerConfig.java +++ b/web/src/main/java/com/jeesite/modules/config/web/ListenerConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.web; +package com.jeesite.modules.config.web; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.context.annotation.Bean; diff --git a/web/src/main/java/com/jeesite/config/web/ServletConfig.java b/web/src/main/java/com/jeesite/modules/config/web/ServletConfig.java similarity index 82% rename from web/src/main/java/com/jeesite/config/web/ServletConfig.java rename to web/src/main/java/com/jeesite/modules/config/web/ServletConfig.java index 497056dc..4e006b9b 100644 --- a/web/src/main/java/com/jeesite/config/web/ServletConfig.java +++ b/web/src/main/java/com/jeesite/modules/config/web/ServletConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.web; +package com.jeesite.modules.config.web; import org.springframework.context.annotation.Configuration; diff --git a/web/src/main/java/com/jeesite/config/web/interceptor/LogInterceptorConfig.java b/web/src/main/java/com/jeesite/modules/config/web/interceptor/LogInterceptorConfig.java similarity index 94% rename from web/src/main/java/com/jeesite/config/web/interceptor/LogInterceptorConfig.java rename to web/src/main/java/com/jeesite/modules/config/web/interceptor/LogInterceptorConfig.java index c7f791f5..5f7e6701 100644 --- a/web/src/main/java/com/jeesite/config/web/interceptor/LogInterceptorConfig.java +++ b/web/src/main/java/com/jeesite/modules/config/web/interceptor/LogInterceptorConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.web.interceptor; +package com.jeesite.modules.config.web.interceptor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; diff --git a/web/src/main/java/com/jeesite/config/web/interceptor/MobileViewInterceptorConfig.java b/web/src/main/java/com/jeesite/modules/config/web/interceptor/MobileViewInterceptorConfig.java similarity index 92% rename from web/src/main/java/com/jeesite/config/web/interceptor/MobileViewInterceptorConfig.java rename to web/src/main/java/com/jeesite/modules/config/web/interceptor/MobileViewInterceptorConfig.java index 936b94bf..f731f755 100644 --- a/web/src/main/java/com/jeesite/config/web/interceptor/MobileViewInterceptorConfig.java +++ b/web/src/main/java/com/jeesite/modules/config/web/interceptor/MobileViewInterceptorConfig.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2013-Now http://jeesite.com All rights reserved. */ -package com.jeesite.config.web.interceptor; +package com.jeesite.modules.config.web.interceptor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; diff --git a/web/src/test/java/com/jeesite/test/InitCoreData.java b/web/src/test/java/com/jeesite/test/InitCoreData.java index 2be779a6..9ebcd9bd 100644 --- a/web/src/test/java/com/jeesite/test/InitCoreData.java +++ b/web/src/test/java/com/jeesite/test/InitCoreData.java @@ -7,7 +7,7 @@ import org.junit.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Commit; -import com.jeesite.config.Application; +import com.jeesite.modules.config.Application; /** * 初始化核心表数据 @@ -21,17 +21,17 @@ public class InitCoreData extends com.jeesite.modules.db.InitCoreData { @Test public void initCoreData() throws Exception{ initLog(); -// initConfig(); -// initModule(); -// initDict(); -// initRole(); -// initMenu(); -// initUser(); + initConfig(); + initModule(); + initDict(); + initRole(); + initMenu(); + initUser(); // initArea(); -// initOffice(); -// initCompany(); -// initPost(); -// initEmpUser(); + initOffice(); + initCompany(); + initPost(); + initEmpUser(); } }