Permalink
Browse files

MINOR Moved jquery-fitheighttoparent, jquery-layout, swfupload from s…

…apphire into cms

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92614 467b73ca-7a2a-4603-9d3b-597d59a354a9
  • Loading branch information...
1 parent e17b09b commit 538ff9b3213dd1fbeb24b46a64b63f3ec8dec47c @chillu chillu committed Nov 21, 2009
Showing with 21,030 additions and 4 deletions.
  1. +1 −1 code/AssetAdmin.php
  2. +3 −3 code/LeftAndMain.php
  3. +53 −0 javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js
  4. +11 −0 javascript/jquery-fitheighttoparent/test.html
  5. +150 −0 javascript/jquery-fitheighttoparent/tests/unit.html
  6. +1 −0 thirdparty/jquery-layout/README.txt
  7. +60 −0 thirdparty/jquery-layout/changelog.txt
  8. +23 −0 thirdparty/jquery-layout/example.html
  9. +4,376 −0 thirdparty/jquery-layout/jquery.js
  10. +2,507 −0 thirdparty/jquery-layout/jquery.layout.js
  11. +80 −0 thirdparty/jquery-layout/jquery.layout.min.js
  12. +9,074 −0 thirdparty/jquery-layout/jquery.ui.all.js
  13. +150 −0 thirdparty/jquery-layout/nested.html
  14. +197 −0 thirdparty/jquery-layout/simple.html
  15. +8 −0 thirdparty/swfupload/.piston.yml
  16. +193 −0 thirdparty/swfupload/Changelog.txt
  17. +8 −0 thirdparty/swfupload/Flash8/Delegate.as
  18. +115 −0 thirdparty/swfupload/Flash8/ExternalCall.as
  19. +65 −0 thirdparty/swfupload/Flash8/FileItem.as
  20. +55 −0 thirdparty/swfupload/Flash8/SWFUpload v2.as2proj
  21. +914 −0 thirdparty/swfupload/Flash8/SWFUpload.as
  22. +3 −0 thirdparty/swfupload/Flash8/deploy.bat
  23. BIN thirdparty/swfupload/Flash8/swfupload_f8.swf
  24. +117 −0 thirdparty/swfupload/Flash9/ExternalCall.as
  25. +74 −0 thirdparty/swfupload/Flash9/FileItem.as
  26. +75 −0 thirdparty/swfupload/Flash9/SWFUpload v2.as3proj
  27. +920 −0 thirdparty/swfupload/Flash9/SWFUpload.as
  28. +3 −0 thirdparty/swfupload/Flash9/deploy.bat
  29. BIN thirdparty/swfupload/Flash9/swfupload_f9.swf
  30. +11 −0 thirdparty/swfupload/license.txt
  31. +50 −0 thirdparty/swfupload/plugins/swfupload.cookies.js
  32. 0 thirdparty/swfupload/plugins/swfupload.detectionkit.js
  33. +102 −0 thirdparty/swfupload/plugins/swfupload.documentready.js
  34. +63 −0 thirdparty/swfupload/plugins/swfupload.graceful_degradation.js
  35. +58 −0 thirdparty/swfupload/plugins/swfupload.queue.js
  36. BIN thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.accept.png
  37. BIN thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.add.png
  38. +73 −0 thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.callbacks.js
  39. BIN thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.cancel.png
  40. +332 −0 thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.js
  41. BIN thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.progressbar.png
  42. +73 −0 thirdparty/swfupload/plugins/swfupload.v102/swfupload.v102.theme.css
  43. +1,032 −0 thirdparty/swfupload/swfupload.js
View
2 code/AssetAdmin.php
@@ -86,7 +86,7 @@ function init() {
Requirements::javascript(CMS_DIR . "/javascript/CMSMain_upload.js");
Requirements::javascript(CMS_DIR . "/javascript/Upload.js");
- Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/swfupload/swfupload.js");
+ Requirements::javascript(CMS_DIR . "/thirdparty/swfupload/swfupload.js");
Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js");
Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js");
View
6 code/LeftAndMain.php
@@ -186,8 +186,8 @@ function init() {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.slide.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.drop.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.scale.js');
- Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-layout/jquery.layout.js');
- Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-fitheighttoparent/jquery.fitheighttoparent.js');
+ Requirements::javascript(CMS_DIR . '/thirdparty/jquery-layout/jquery.layout.js');
+ Requirements::javascript(CMS_DIR . '/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js');
Requirements::javascript(CMS_DIR . '/javascript/ssui.core.js');
// @todo Load separately so the CSS files can be inlined
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.all.css');
@@ -276,7 +276,7 @@ function init() {
'cms/javascript/LeftAndMain_right.js',
'jsparty/tree/tree.js',
'cms/javascript/TinyMCEImageEnhancement.js',
- 'sapphire/thirdparty/swfupload/swfupload.js',
+ 'cms/thirdparty/swfupload/swfupload.js',
'cms/javascript/Upload.js',
'cms/javascript/TinyMCEImageEnhancement.js',
'sapphire/javascript/TreeSelectorField.js',
View
53 javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js
@@ -0,0 +1,53 @@
+/**
+ * Fits an element's height to its parent by substracting
+ * all (visible) siblings heights from the element.
+ * Caution: This will set overflow: hidden on the parent
+ *
+ * Copyright 2009 Ingo Schommer, SilverStripe Ltd.
+ * Licensed under MIT License: http://www.opensource.org/licenses/mit-license.php
+ *
+ * @todo Implement selectors to ignore certain elements
+ *
+ * @author Ingo Schommer, SilverStripe Ltd.
+ * @version 0.1
+ */
+jQuery.fn.extend({
+ fitHeightToParent: function() {
+ return jQuery(this).each(function() {
+ var $this = jQuery(this);
+
+ var boxmodel = ['marginTop','marginBottom','paddingTop','paddingBottom','borderBottomWidth','borderTopWidth'];
+
+ // don't bother if element or parent arent visible,
+ // we won't get height readings
+ if($this.is(':visible') && $this.parent().is(':visible')) {
+
+ // we set overflow = hidden so that large children don't muck things up in IE6 box model
+ $this.parent().css('overflow', 'hidden');
+
+ // get height from parent without any margins as a starting point,
+ // and reduce any top/bottom paddings
+ var height = $this.parent().innerHeight()
+ - parseFloat($this.parent().css('paddingTop'))
+ - parseFloat($this.parent().css('paddingBottom'));
+
+ // substract height of any siblings of the current element
+ // including their margins/paddings/borders
+ $this.siblings(':visible').filter(function() {
+ // remove all absolutely positioned elements
+ return (jQuery(this).css('position') != 'absolute');
+ }).each(function() {
+ height -= jQuery(this).outerHeight(true);
+ });
+
+ // remove margins/paddings/borders on inner element
+ jQuery.each(boxmodel, function(i, name) {
+ height -= parseFloat($this.css(name)) || 0;
+ });
+
+ // set new height
+ $this.height(height);
+ }
+ });
+ }
+});
View
11 javascript/jquery-fitheighttoparent/test.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <script src="http://code.jquery.com/jquery-latest.js"></script>
+ <script type="text/javascript" src="jquery.fitheighttoparent.js"></script>
+</head>
+<body>
+ test
+</body>
+</html>
View
150 javascript/jquery-fitheighttoparent/tests/unit.html
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <script src="http://code.jquery.com/jquery-latest.js"></script>
+ <script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
+ <script type="text/javascript" src="../jquery.fitheighttoparent.js"></script>
+
+ <link rel="stylesheet" href="http://dev.jquery.com/view/trunk/qunit/testsuite.css" type="text/css" media="screen" />
+
+ <style type="text/css">
+ .outer {background: #333;}
+ .inner {background: #f0f;}
+ .sibling {background: #bbb;}
+
+ #test1-parent {height: 200px; margin: 10px 0; padding: 15px 10px; border: 5px solid #000;}
+ #test1-sibling-before {height: 20px; margin: 3px 0; padding: 2px 0;}
+ #test1-sibling-after {height: 30px; margin: 3px 0; padding: 2px 0;}
+
+ #test2-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
+ #test2-sibling-absolute {height: 20px; position: absolute;}
+
+ #test3-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
+ #test3-sibling {height: 20px;}
+ #test3-sibling-hidden {height: 30px; display: none;}
+
+ #test4-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
+ #test4-sibling {height: 20px;}
+ #test4 {margin: 15px 0; padding: 10px 0;border: 5px solid #000;}
+
+ #test5-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
+ #test5-sibling {height: 20px;}
+ #test5 {margin: 15px 0; padding: 10px 0;border: 5px solid #000;}
+
+ #test6-parent {height: 200px; margin: 10px 0; padding: 15px 10px; position: relative; width: 100%;}
+ #test6-sibling {height: 20px;}
+ #test6 {overflow: auto; }
+ </style>
+
+ <script>
+ $(document).ready(function(){
+
+ test("with inline siblings, margins/paddings/borders on parent", function() {
+ equals(
+ jQuery('#test1').fitHeightToParent().height(),
+ 130
+ );
+ });
+
+ test("with absolute siblings", function() {
+ equals(
+ jQuery('#test2').fitHeightToParent().height(),
+ 200
+ );
+ });
+
+ test("with hidden siblings", function() {
+ equals(
+ jQuery('#test3').fitHeightToParent().height(),
+ 180
+ );
+ });
+
+ test("with margins/paddings/borders on resized element", function() {
+ equals(
+ jQuery('#test4').fitHeightToParent().height(),
+ 120
+ );
+ });
+
+ test("form with fieldset", function() {
+ equals(
+ jQuery('#test5').fitHeightToParent().height(),
+ 120
+ );
+ });
+
+ test("overflow auto with long inner element", function() {
+ equals(
+ jQuery('#test6').fitHeightToParent().height(),
+ 180
+ );
+ });
+
+ });
+ </script>
+
+</head>
+<body>
+
+<script type="text/javascript" src="http://jqueryjs.googlecode.com/svn/trunk/qunit/testrunner.js"></script>
+ <h1>jquery.fitheighttoparent unit test</h1>
+ <h2 id="banner"></h2>
+ <h2 id="userAgent"></h2>
+
+ <ol id="tests"></ol>
+
+ <div id="main"></div>
+
+ <div id="test1-parent" class="outer">
+ <div id="test1-sibling-before" class="sibling"></div>
+ <div id="test1" class="inner">
+ <p>test 1</p>
+ </div>
+ <div id="test1-sibling-after" class="sibling"></div>
+ </div>
+
+ <div id="test2-parent" class="outer">
+ <div id="test2-sibling-absolute" class="sibling"></div>
+ <div id="test2" class="inner">
+ <p>test 2</p>
+ </div>
+ </div>
+
+ <div id="test3-parent" class="outer">
+ <div id="test3-sibling" class="sibling"></div>
+ <div id="test3" class="inner">
+ <p>test 3</p>
+ </div>
+ <div id="test3-sibling-hidden" class="sibling"></div>
+ </div>
+
+ <div id="test4-parent" class="outer">
+ <div id="test4-sibling" class="sibling"></div>
+ <div id="test4" class="inner">
+ <p>test 4</p>
+ </div>
+ </div>
+
+ <form action="#" method="POST" id="test5-parent" class="outer">
+ <fieldset id="test5" class="inner">
+ <p>test 5</p>
+ <input type="text">
+ </fieldset>
+ <div id="test5-sibling" class="sibling"></div>
+ </form>
+
+ <div id="test6-parent" class="outer">
+ <div id="test6-sibling" class="sibling"></div>
+ <div id="test6" class="inner">
+<p>test 6</p>
+<p>Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.</p>
+<p>
+Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.</p>
+<p>
+Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.</p>
+ </div>
+ </div>
+</body>
+</html>
View
1 thirdparty/jquery-layout/README.txt
@@ -0,0 +1 @@
+See http://layout.jquery-dev.net/downloads.html
View
60 thirdparty/jquery-layout/changelog.txt
@@ -0,0 +1,60 @@
+1.2.0
+* ADDED maskIframesOnResize option: true=ALL -OR- a selector string
+* ADDED options to set different animations on open and close
+* ADDED new callback events, ie: onshow, onhide
+* ADDED start/end callbacks, eg: onopen_start, onopen_end, etc.
+* ADDED ability to cancel events using callbacks, eg: onopen_start
+* CHANGED Layout.config.fxDefaults to Layout.effects (internal use)
+* FIXED missing semi-colon so minified version works in IE
+
+1.1.3
+* FIXED typo in cursor-hotkeys code
+* ADDED scrollToBookmarkOnLoad options - enables use of URL hash:
+ o www.site.com/page.html#myBookmark
+ o AFTER layout is created, attempts to scroll to bookmark
+ o default = true - otherwise bookmarks are non-functional
+
+1.1.2
+* UPDATED paneSelector rules to handle FORMS and pane-nesting
+ o automatically looks for panes inside 'first form' in container
+ o if using an ID as paneSelector, pane can be 'deeply nested'
+* ADDED auto-CSS for 'containers' other than BODY
+ o overflow: hidden - ensures no scrollbars on container
+ o position: relative - IF NOT: fixed, absolute or relative
+ o height: 100% - IF NOT specified or is 'auto'
+* ADDED noAnimation param to open() and close() - not used internally
+
+1.1.1
+* CHANGED toggler element from a SPAN to a DIV
+* CHANGED auto-generated custom-buttons classes for better consistency
+ o [buttonClass]-[pane]-[buttonType] ==> [buttonClass]-[buttonType]-[pane]
+ o ui-layout-button-west-open ==> ui-layout-button-open-west
+ o ui-layout-button-west-pin-up ==> ui-layout-button-pin-west-up
+* CHANGED default for hideTogglerOnSlide to false
+* CHANGED internal 'cDims' hash to alias for state.container
+* CHANGED internal aliases: s = state[pane] and o = options[pane]
+* UPDATED toggler-text to auto-show correct spans (content-open/closed)
+* FIXED toggler-text - now centers text span correctly
+* FIXED bug affecting IE6 when layout has no north or south pane
+* ADDED new layout property 'state' - eg: myLayout.state.west.size
+* REMOVED layout.containerDimensions property - USE: layout.state.container
+* CHANGED data returned to callbacks - added pane-state as 3rd param
+
+1.1.0
+* RENAMED raisePaneZindexOnHover ==> showOverflowOnHover
+* REMOVED "overflow: auto" from base-styles. Overflow must now be set by
+ CSS - unless applyDefaultStyles==true. No longer need "!important" to
+ set pane overflow in your stylesheet.
+* CHANGED minSize default from 50 to 0 (still auto-limited to 'css size')
+* FIXED bug in allowOverflow - now works with 'custom paneClass'
+* EXPOSED two CSS utility methods
+ o myLayout.cssWidth( elem )
+ o myLayout.cssHeight( elem )
+* NEW auto-resize for ALL layouts on windows.resize
+* UPDATED auto-resizing of panes after a container-resize
+* NEW flow-code to prevent simultaneous pane animations
+* NEW options to add text inside toggler-buttons
+* NEW options for hotkeys - standard (cursors) and user-defined
+
+1.0
+* Initial release
View
23 thirdparty/jquery-layout/example.html
@@ -0,0 +1,23 @@
+<HTML>
+<HEAD>
+<TITLE>Layout Example</TITLE>
+<SCRIPT type="text/javascript" src="jquery.js"></SCRIPT>
+<SCRIPT type="text/javascript" src="jquery.layout.js"></SCRIPT>
+<SCRIPT type="text/javascript">
+$(document).ready(function () {
+ $('body').layout({ applyDefaultStyles: true });
+});
+</SCRIPT>
+</HEAD>
+<BODY>
+<DIV class="ui-layout-center">Center
+ <P><A href="http://layout.jquery-dev.net/demos.html">Go to the Demos page</A></P>
+ <P>* Pane-resizing is disabled because ui.draggable.js is not linked</P>
+ <P>* Pane-animation is disabled because ui.effects.js is not linked</P>
+</DIV>
+<DIV class="ui-layout-north">North</DIV>
+<DIV class="ui-layout-south">South</DIV>
+<DIV class="ui-layout-east">East</DIV>
+<DIV class="ui-layout-west">West</DIV>
+</BODY>
+</HTML>
View
4,376 thirdparty/jquery-layout/jquery.js
4,376 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
2,507 thirdparty/jquery-layout/jquery.layout.js
@@ -0,0 +1,2507 @@
+/*
+ * jquery.layout 1.2.0
+ *
+ * Copyright (c) 2008
+ * 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.
+ *
+ * $Date: 2008-12-27 02:17:22 +0100 (sab, 27 dic 2008) $
+ * $Rev: 203 $
+ *
+ * NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars
+ */
+(function($) {
+
+$.fn.layout = function (opts) {
+
+/*
+ * ###########################
+ * WIDGET CONFIG & OPTIONS
+ * ###########################
+ */
+
+ // DEFAULTS for options
+ var
+ prefix = "ui-layout-" // prefix for ALL selectors and classNames
+ , defaults = { // misc default values
+ paneClass: prefix+"pane" // ui-layout-pane
+ , resizerClass: prefix+"resizer" // ui-layout-resizer
+ , togglerClass: prefix+"toggler" // ui-layout-toggler
+ , togglerInnerClass: prefix+"" // ui-layout-open / ui-layout-closed
+ , buttonClass: prefix+"button" // ui-layout-button
+ , contentSelector: "."+prefix+"content"// ui-layout-content
+ , contentIgnoreSelector: "."+prefix+"ignore" // ui-layout-mask
+ }
+ ;
+
+ // DEFAULT PANEL OPTIONS - CHANGE IF DESIRED
+ var options = {
+ name: "" // FUTURE REFERENCE - not used right now
+ , scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
+ , defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
+ applyDefaultStyles: false // apply basic styles directly to resizers & buttons? If not, then stylesheet must handle it
+ , closable: true // pane can open & close
+ , resizable: true // when open, pane can be resized
+ , slidable: true // when closed, pane can 'slide' open over other panes - closes on mouse-out
+ //, paneSelector: [ ] // MUST be pane-specific!
+ , contentSelector: defaults.contentSelector // INNER div/element to auto-size so only it scrolls, not the entire pane!
+ , contentIgnoreSelector: defaults.contentIgnoreSelector // elem(s) to 'ignore' when measuring 'content'
+ , paneClass: defaults.paneClass // border-Pane - default: 'ui-layout-pane'
+ , resizerClass: defaults.resizerClass // Resizer Bar - default: 'ui-layout-resizer'
+ , togglerClass: defaults.togglerClass // Toggler Button - default: 'ui-layout-toggler'
+ , buttonClass: defaults.buttonClass // CUSTOM Buttons - default: 'ui-layout-button-toggle/-open/-close/-pin'
+ , resizerDragOpacity: 1 // option for ui.draggable
+ //, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
+ , maskIframesOnResize: true // true = all iframes OR = iframe-selector(s) - adds masking-div during resizing/dragging
+ //, size: 100 // inital size of pane - defaults are set 'per pane'
+ , minSize: 0 // when manually resizing a pane
+ , maxSize: 0 // ditto, 0 = no limit
+ , spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
+ , spacing_closed: 6 // ditto - when pane is 'closed'
+ , togglerLength_open: 50 // Length = WIDTH of toggler button on north/south edges - HEIGHT on east/west edges
+ , togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
+ , togglerAlign_open: "center" // top/left, bottom/right, center, OR...
+ , togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
+ , togglerTip_open: "Close" // Toggler tool-tip (title)
+ , togglerTip_closed: "Open" // ditto
+ , resizerTip: "Resize" // Resizer tool-tip (title)
+ , sliderTip: "Slide Open" // resizer-bar triggers 'sliding' when pane is closed
+ , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
+ , slideTrigger_open: "click" // click, dblclick, mouseover
+ , slideTrigger_close: "mouseout" // click, mouseout
+ , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
+ , togglerContent_open: "" // text or HTML to put INSIDE the toggler
+ , togglerContent_closed: "" // ditto
+ , showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
+ , enableCursorHotkey: true // enabled 'cursor' hotkeys
+ //, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
+ , customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
+ // NOTE: fxSss_open & fxSss_close options (eg: fxName_open) are auto-generated if not passed
+ , fxName: "slide" // ('none' or blank), slide, drop, scale
+ , fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
+ , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
+ , initClosed: false // true = init pane as 'closed'
+ , initHidden: false // true = init pane as 'hidden' - no resizer or spacing
+
+ /* callback options do not have to be set - listed here for reference only
+ , onshow_start: "" // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
+ , onshow_end: "" // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
+ , onhide_start: "" // CALLBACK when pane STARTS to Close - BEFORE onclose_start
+ , onhide_end: "" // CALLBACK when pane ENDS being Closed - AFTER onclose_end
+ , onopen_start: "" // CALLBACK when pane STARTS to Open
+ , onopen_end: "" // CALLBACK when pane ENDS being Opened
+ , onclose_start: "" // CALLBACK when pane STARTS to Close
+ , onclose_end: "" // CALLBACK when pane ENDS being Closed
+ , onresize_start: "" // CALLBACK when pane STARTS to be ***MANUALLY*** Resized
+ , onresize_end: "" // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
+ */
+ }
+ , north: {
+ paneSelector: "."+prefix+"north" // default = .ui-layout-north
+ , size: "auto"
+ , resizerCursor: "n-resize"
+ }
+ , south: {
+ paneSelector: "."+prefix+"south" // default = .ui-layout-south
+ , size: "auto"
+ , resizerCursor: "s-resize"
+ }
+ , east: {
+ paneSelector: "."+prefix+"east" // default = .ui-layout-east
+ , size: 200
+ , resizerCursor: "e-resize"
+ }
+ , west: {
+ paneSelector: "."+prefix+"west" // default = .ui-layout-west
+ , size: 200
+ , resizerCursor: "w-resize"
+ }
+ , center: {
+ paneSelector: "."+prefix+"center" // default = .ui-layout-center
+ }
+
+ };
+
+
+ var effects = { // LIST *PREDEFINED EFFECTS* HERE, even if effect has no settings
+ slide: {
+ all: { duration: "fast" } // eg: duration: 1000, easing: "easeOutBounce"
+ , north: { direction: "up" }
+ , south: { direction: "down" }
+ , east: { direction: "right"}
+ , west: { direction: "left" }
+ }
+ , drop: {
+ all: { duration: "slow" } // eg: duration: 1000, easing: "easeOutQuint"
+ , north: { direction: "up" }
+ , south: { direction: "down" }
+ , east: { direction: "right"}
+ , west: { direction: "left" }
+ }
+ , scale: {
+ all: { duration: "fast" }
+ }
+ };
+
+
+ // STATIC, INTERNAL CONFIG - DO NOT CHANGE THIS!
+ var config = {
+ allPanes: "north,south,east,west,center"
+ , borderPanes: "north,south,east,west"
+ , zIndex: { // set z-index values here
+ resizer_normal: 1 // normal z-index for resizer-bars
+ , pane_normal: 2 // normal z-index for panes
+ , mask: 4 // overlay div used to mask pane(s) during resizing
+ , sliding: 100 // applied to both the pane and its resizer when a pane is 'slid open'
+ , resizing: 10000 // applied to the CLONED resizer-bar when being 'dragged'
+ , animation: 10000 // applied to the pane when being animated - not applied to the resizer
+ }
+ , resizers: {
+ cssReq: {
+ position: "absolute"
+ , padding: 0
+ , margin: 0
+ , fontSize: "1px"
+ , textAlign: "left" // to counter-act "center" alignment!
+ , overflow: "hidden" // keep toggler button from overflowing
+ , zIndex: 1
+ }
+ , cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
+ background: "#DDD"
+ , border: "none"
+ }
+ }
+ , togglers: {
+ cssReq: {
+ position: "absolute"
+ , display: "block"
+ , padding: 0
+ , margin: 0
+ , overflow: "hidden"
+ , textAlign: "center"
+ , fontSize: "1px"
+ , cursor: "pointer"
+ , zIndex: 1
+ }
+ , cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
+ background: "#AAA"
+ }
+ }
+ , content: {
+ cssReq: {
+ overflow: "auto"
+ }
+ , cssDef: {}
+ }
+ , defaults: { // defaults for ALL panes - overridden by 'per-pane settings' below
+ cssReq: {
+ position: "absolute"
+ , margin: 0
+ , zIndex: 2
+ }
+ , cssDef: {
+ padding: "10px"
+ , background: "#FFF"
+ , border: "1px solid #BBB"
+ , overflow: "auto"
+ }
+ }
+ , north: {
+ edge: "top"
+ , sizeType: "height"
+ , dir: "horz"
+ , cssReq: {
+ top: 0
+ , bottom: "auto"
+ , left: 0
+ , right: 0
+ , width: "auto"
+ // height: DYNAMIC
+ }
+ }
+ , south: {
+ edge: "bottom"
+ , sizeType: "height"
+ , dir: "horz"
+ , cssReq: {
+ top: "auto"
+ , bottom: 0
+ , left: 0
+ , right: 0
+ , width: "auto"
+ // height: DYNAMIC
+ }
+ }
+ , east: {
+ edge: "right"
+ , sizeType: "width"
+ , dir: "vert"
+ , cssReq: {
+ left: "auto"
+ , right: 0
+ , top: "auto" // DYNAMIC
+ , bottom: "auto" // DYNAMIC
+ , height: "auto"
+ // width: DYNAMIC
+ }
+ }
+ , west: {
+ edge: "left"
+ , sizeType: "width"
+ , dir: "vert"
+ , cssReq: {
+ left: 0
+ , right: "auto"
+ , top: "auto" // DYNAMIC
+ , bottom: "auto" // DYNAMIC
+ , height: "auto"
+ // width: DYNAMIC
+ }
+ }
+ , center: {
+ dir: "center"
+ , cssReq: {
+ left: "auto" // DYNAMIC
+ , right: "auto" // DYNAMIC
+ , top: "auto" // DYNAMIC
+ , bottom: "auto" // DYNAMIC
+ , height: "auto"
+ , width: "auto"
+ }
+ }
+ };
+
+
+ // DYNAMIC DATA
+ var state = {
+ // generate random 'ID#' to identify layout - used to create global namespace for timers
+ id: Math.floor(Math.random() * 10000)
+ , container: {}
+ , north: {}
+ , south: {}
+ , east: {}
+ , west: {}
+ , center: {}
+ };
+
+
+ var
+ altEdge = {
+ top: "bottom"
+ , bottom: "top"
+ , left: "right"
+ , right: "left"
+ }
+ , altSide = {
+ north: "south"
+ , south: "north"
+ , east: "west"
+ , west: "east"
+ }
+ ;
+
+
+/*
+ * ###########################
+ * INTERNAL HELPER FUNCTIONS
+ * ###########################
+ */
+
+ /**
+ * isStr
+ *
+ * Returns true if passed param is EITHER a simple string OR a 'string object' - otherwise returns false
+ */
+ var isStr = function (o) {
+ if (typeof o == "string")
+ return true;
+ else if (typeof o == "object") {
+ try {
+ var match = o.constructor.toString().match(/string/i);
+ return (match !== null);
+ } catch (e) {}
+ }
+ return false;
+ };
+
+ /**
+ * str
+ *
+ * Returns a simple string if the passed param is EITHER a simple string OR a 'string object',
+ * else returns the original object
+ */
+ var str = function (o) {
+ if (typeof o == "string" || isStr(o)) return $.trim(o); // trim converts 'String object' to a simple string
+ else return o;
+ };
+
+ /**
+ * min / max
+ *
+ * Alias for Math.min/.max to simplify coding
+ */
+ var min = function (x,y) { return Math.min(x,y); };
+ var max = function (x,y) { return Math.max(x,y); };
+
+ /**
+ * transformData
+ *
+ * Processes the options passed in and transforms them into the format used by layout()
+ * Missing keys are added, and converts the data if passed in 'flat-format' (no sub-keys)
+ * In flat-format, pane-specific-settings are prefixed like: north__optName (2-underscores)
+ * To update effects, options MUST use nested-keys format, with an effects key
+ *
+ * @callers initOptions()
+ * @params JSON d Data/options passed by user - may be a single level or nested levels
+ * @returns JSON Creates a data struture that perfectly matches 'options', ready to be imported
+ */
+ var transformData = function (d) {
+ var json = { defaults:{fxSettings:{}}, north:{fxSettings:{}}, south:{fxSettings:{}}, east:{fxSettings:{}}, west:{fxSettings:{}}, center:{fxSettings:{}} };
+ d = d || {};
+ if (d.effects || d.defaults || d.north || d.south || d.west || d.east || d.center)
+ json = $.extend( json, d ); // already in json format - add to base keys
+ else
+ // convert 'flat' to 'nest-keys' format - also handles 'empty' user-options
+ $.each( d, function (key,val) {
+ a = key.split("__");
+ json[ a[1] ? a[0] : "defaults" ][ a[1] ? a[1] : a[0] ] = val;
+ });
+ return json;
+ };
+
+ /**
+ * setFlowCallback
+ *
+ * Set an INTERNAL callback to avoid simultaneous animation
+ * Runs only if needed and only if all callbacks are not 'already set'!
+ *
+ * @param String action Either 'open' or 'close'
+ * @pane String pane A valid border-pane name, eg 'west'
+ * @pane Boolean param Extra param for callback (optional)
+ */
+ var setFlowCallback = function (action, pane, param) {
+ var
+ cb = action +","+ pane +","+ (param ? 1 : 0)
+ , cP, cbPane
+ ;
+ $.each(c.borderPanes.split(","), function (i,p) {
+ if (c[p].isMoving) {
+ bindCallback(p); // TRY to bind a callback
+ return false; // BREAK
+ }
+ });
+
+ function bindCallback (p, test) {
+ cP = c[p];
+ if (!cP.doCallback) {
+ cP.doCallback = true;
+ cP.callback = cb;
+ }
+ else { // try to 'chain' this callback
+ cpPane = cP.callback.split(",")[1]; // 2nd param is 'pane'
+ if (cpPane != p && cpPane != pane) // callback target NOT 'itself' and NOT 'this pane'
+ bindCallback (cpPane, true); // RECURSE
+ }
+ }
+ };
+
+ /**
+ * execFlowCallback
+ *
+ * RUN the INTERNAL callback for this pane - if one exists
+ *
+ * @param String action Either 'open' or 'close'
+ * @pane String pane A valid border-pane name, eg 'west'
+ * @pane Boolean param Extra param for callback (optional)
+ */
+ var execFlowCallback = function (pane) {
+ var cP = c[pane];
+
+ // RESET flow-control flaGs
+ c.isLayoutBusy = false;
+ delete cP.isMoving;
+ if (!cP.doCallback || !cP.callback) return;
+
+ cP.doCallback = false; // RESET logic flag
+
+ // EXECUTE the callback
+ var
+ cb = cP.callback.split(",")
+ , param = (cb[2] > 0 ? true : false)
+ ;
+ if (cb[0] == "open")
+ open( cb[1], param );
+ else if (cb[0] == "close")
+ close( cb[1], param );
+
+ if (!cP.doCallback) cP.callback = null; // RESET - unless callback above enabled it again!
+ };
+
+ /**
+ * execUserCallback
+ *
+ * Executes a Callback function after a trigger event, like resize, open or close
+ *
+ * @param String pane This is passed only so we can pass the 'pane object' to the callback
+ * @param String v_fn Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
+ */
+ var execUserCallback = function (pane, v_fn) {
+ if (!v_fn) return;
+ var fn;
+ try {
+ if (typeof v_fn == "function")
+ fn = v_fn;
+ else if (typeof v_fn != "string")
+ return;
+ else if (v_fn.indexOf(",") > 0) {
+ // function name cannot contain a comma, so must be a function name AND a 'name' parameter
+ var
+ args = v_fn.split(",")
+ , fn = eval(args[0])
+ ;
+ if (typeof fn=="function" && args.length > 1)
+ return fn(args[1]); // pass the argument parsed from 'list'
+ }
+ else // just the name of an external function?
+ fn = eval(v_fn);
+
+ if (typeof fn=="function")
+ // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
+ return fn( pane, $Ps[pane], $.extend({},state[pane]), $.extend({},options[pane]), options.name );
+ }
+ catch (ex) {}
+ };
+
+ /**
+ * cssNum
+ *
+ * Returns the 'current CSS value' for an element - returns 0 if property does not exist
+ *
+ * @callers Called by many methods
+ * @param jQuery $Elem Must pass a jQuery object - first element is processed
+ * @param String property The name of the CSS property, eg: top, width, etc.
+ * @returns Variant Usually is used to get an integer value for position (top, left) or size (height, width)
+ */
+ var cssNum = function ($E, prop) {
+ var
+ val = 0
+ , hidden = false
+ , visibility = ""
+ ;
+ if (!$.browser.msie) { // IE CAN read dimensions of 'hidden' elements - FF CANNOT
+ if ($.curCSS($E[0], "display", true) == "none") {
+ hidden = true;
+ visibility = $.curCSS($E[0], "visibility", true); // SAVE current setting
+ $E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so we can measure it
+ }
+ }
+
+ val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
+
+ if (hidden) { // WAS hidden, so put back the way it was
+ $E.css({ display: "none" });
+ if (visibility && visibility != "hidden")
+ $E.css({ visibility: visibility }); // reset 'visibility'
+ }
+
+ return val;
+ };
+
+ /**
+ * cssW / cssH / cssSize
+ *
+ * Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
+ *
+ * @callers initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
+ * @param Variant elem Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
+ * @param Integer outerWidth/outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
+ * @returns Integer Returns the innerHeight of the elem by subtracting padding and borders
+ *
+ * @TODO May need to add additional logic to handle more browser/doctype variations?
+ */
+ var cssW = function (e, outerWidth) {
+ var $E;
+ if (isStr(e)) {
+ e = str(e);
+ $E = $Ps[e];
+ }
+ else
+ $E = $(e);
+
+ // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
+ if (outerWidth <= 0)
+ return 0;
+ else if (!(outerWidth>0))
+ outerWidth = isStr(e) ? getPaneSize(e) : $E.outerWidth();
+
+ if (!$.boxModel)
+ return outerWidth;
+
+ else // strip border and padding size from outerWidth to get CSS Width
+ return outerWidth
+ - cssNum($E, "paddingLeft")
+ - cssNum($E, "paddingRight")
+ - ($.curCSS($E[0], "borderLeftStyle", true) == "none" ? 0 : cssNum($E, "borderLeftWidth"))
+ - ($.curCSS($E[0], "borderRightStyle", true) == "none" ? 0 : cssNum($E, "borderRightWidth"))
+ ;
+ };
+ var cssH = function (e, outerHeight) {
+ var $E;
+ if (isStr(e)) {
+ e = str(e);
+ $E = $Ps[e];
+ }
+ else
+ $E = $(e);
+
+ // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
+ if (outerHeight <= 0)
+ return 0;
+ else if (!(outerHeight>0))
+ outerHeight = (isStr(e)) ? getPaneSize(e) : $E.outerHeight();
+
+ if (!$.boxModel)
+ return outerHeight;
+
+ else // strip border and padding size from outerHeight to get CSS Height
+ return outerHeight
+ - cssNum($E, "paddingTop")
+ - cssNum($E, "paddingBottom")
+ - ($.curCSS($E[0], "borderTopStyle", true) == "none" ? 0 : cssNum($E, "borderTopWidth"))
+ - ($.curCSS($E[0], "borderBottomStyle", true) == "none" ? 0 : cssNum($E, "borderBottomWidth"))
+ ;
+ };
+ var cssSize = function (pane, outerSize) {
+ if (c[pane].dir=="horz") // pane = north or south
+ return cssH(pane, outerSize);
+ else // pane = east or west
+ return cssW(pane, outerSize);
+ };
+
+ /**
+ * getPaneSize
+ *
+ * Calculates the current 'size' (width or height) of a border-pane - optionally with 'pane spacing' added
+ *
+ * @returns Integer Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
+ */
+ var getPaneSize = function (pane, inclSpace) {
+ var
+ $P = $Ps[pane]
+ , o = options[pane]
+ , s = state[pane]
+ , oSp = (inclSpace ? o.spacing_open : 0)
+ , cSp = (inclSpace ? o.spacing_closed : 0)
+ ;
+ if (!$P || s.isHidden)
+ return 0;
+ else if (s.isClosed || (s.isSliding && inclSpace))
+ return cSp;
+ else if (c[pane].dir == "horz")
+ return $P.outerHeight() + oSp;
+ else // dir == "vert"
+ return $P.outerWidth() + oSp;
+ };
+
+ var setPaneMinMaxSizes = function (pane) {
+ var
+ d = cDims
+ , edge = c[pane].edge
+ , dir = c[pane].dir
+ , o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $altPane = $Ps[ altSide[pane] ]
+ , paneSpacing = o.spacing_open
+ , altPaneSpacing = options[ altSide[pane] ].spacing_open
+ , altPaneSize = (!$altPane ? 0 : (dir=="horz" ? $altPane.outerHeight() : $altPane.outerWidth()))
+ , containerSize = (dir=="horz" ? d.innerHeight : d.innerWidth)
+ // limitSize prevents this pane from 'overlapping' opposite pane - even if opposite pane is currently closed
+ , limitSize = containerSize - paneSpacing - altPaneSize - altPaneSpacing
+ , minSize = s.minSize || 0
+ , maxSize = Math.min(s.maxSize || 9999, limitSize)
+ , minPos, maxPos // used to set resizing limits
+ ;
+ switch (pane) {
+ case "north": minPos = d.offsetTop + minSize;
+ maxPos = d.offsetTop + maxSize;
+ break;
+ case "west": minPos = d.offsetLeft + minSize;
+ maxPos = d.offsetLeft + maxSize;
+ break;
+ case "south": minPos = d.offsetTop + d.innerHeight - maxSize;
+ maxPos = d.offsetTop + d.innerHeight - minSize;
+ break;
+ case "east": minPos = d.offsetLeft + d.innerWidth - maxSize;
+ maxPos = d.offsetLeft + d.innerWidth - minSize;
+ break;
+ }
+ // save data to pane-state
+ $.extend(s, { minSize: minSize, maxSize: maxSize, minPosition: minPos, maxPosition: maxPos });
+ };
+
+ /**
+ * getPaneDims
+ *
+ * Returns data for setting the size/position of center pane. Date is also used to set Height for east/west panes
+ *
+ * @returns JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
+ */
+ var getPaneDims = function () {
+ var d = {
+ top: getPaneSize("north", true) // true = include 'spacing' value for p
+ , bottom: getPaneSize("south", true)
+ , left: getPaneSize("west", true)
+ , right: getPaneSize("east", true)
+ , width: 0
+ , height: 0
+ };
+
+ with (d) {
+ width = cDims.innerWidth - left - right;
+ height = cDims.innerHeight - bottom - top;
+ // now add the 'container border/padding' to get final positions - relative to the container
+ top += cDims.top;
+ bottom += cDims.bottom;
+ left += cDims.left;
+ right += cDims.right;
+ }
+
+ return d;
+ };
+
+
+ /**
+ * getElemDims
+ *
+ * Returns data for setting size of an element (container or a pane).
+ *
+ * @callers create(), onWindowResize() for container, plus others for pane
+ * @returns JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
+ */
+ var getElemDims = function ($E) {
+ var
+ d = {} // dimensions hash
+ , e, b, p // edge, border, padding
+ ;
+
+ $.each("Left,Right,Top,Bottom".split(","), function () {
+ e = str(this);
+ b = d["border" +e] = cssNum($E, "border"+e+"Width");
+ p = d["padding"+e] = cssNum($E, "padding"+e);
+ d["offset" +e] = b + p; // total offset of content from outer edge
+ // if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
+ if ($E == $Container)
+ d[e.toLowerCase()] = ($.boxModel ? p : 0);
+ });
+
+ d.innerWidth = d.outerWidth = $E.outerWidth();
+ d.innerHeight = d.outerHeight = $E.outerHeight();
+ if ($.boxModel) {
+ d.innerWidth -= (d.offsetLeft + d.offsetRight);
+ d.innerHeight -= (d.offsetTop + d.offsetBottom);
+ }
+
+ return d;
+ };
+
+
+ var setTimer = function (pane, action, fn, ms) {
+ var
+ Layout = window.layout = window.layout || {}
+ , Timers = Layout.timers = Layout.timers || {}
+ , name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
+ ;
+ if (Timers[name]) return; // timer already set!
+ else Timers[name] = setTimeout(fn, ms);
+ };
+
+ var clearTimer = function (pane, action) {
+ var
+ Layout = window.layout = window.layout || {}
+ , Timers = Layout.timers = Layout.timers || {}
+ , name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
+ ;
+ if (Timers[name]) {
+ clearTimeout( Timers[name] );
+ delete Timers[name];
+ return true;
+ }
+ else
+ return false;
+ };
+
+
+/*
+ * ###########################
+ * INITIALIZATION METHODS
+ * ###########################
+ */
+
+ /**
+ * create
+ *
+ * Initialize the layout - called automatically whenever an instance of layout is created
+ *
+ * @callers NEVER explicity called
+ * @returns An object pointer to the instance created
+ */
+ var create = function () {
+ // initialize config/options
+ initOptions();
+
+ // initialize all objects
+ initContainer(); // set CSS as needed and init state.container dimensions
+ initPanes(); // size & position all panes
+ initHandles(); // create and position all resize bars & togglers buttons
+ initResizable(); // activate resizing on all panes where resizable=true
+ sizeContent("all"); // AFTER panes & handles have been initialized, size 'content' divs
+
+ if (options.scrollToBookmarkOnLoad)
+ with (self.location) if (hash) replace( hash ); // scrollTo Bookmark
+
+ // bind hotkey function - keyDown - if required
+ initHotkeys();
+
+ // bind resizeAll() for 'this layout instance' to window.resize event
+ $(window).resize(function () {
+ var timerID = "timerLayout_"+state.id;
+ if (window[timerID]) clearTimeout(window[timerID]);
+ window[timerID] = null;
+ if (true || $.browser.msie) // use a delay for IE because the resize event fires repeatly
+ window[timerID] = setTimeout(resizeAll, 100);
+ else // most other browsers have a built-in delay before firing the resize event
+ resizeAll(); // resize all layout elements NOW!
+ });
+ };
+
+ /**
+ * initContainer
+ *
+ * Validate and initialize container CSS and events
+ *
+ * @callers create()
+ */
+ var initContainer = function () {
+ try { // format html/body if this is a full page layout
+ if ($Container[0].tagName == "BODY") {
+ $("html").css({
+ height: "100%"
+ , overflow: "hidden"
+ });
+ $("body").css({
+ position: "relative"
+ , height: "100%"
+ , overflow: "hidden"
+ , margin: 0
+ , padding: 0 // TODO: test whether body-padding could be handled?
+ , border: "none" // a body-border creates problems because it cannot be measured!
+ });
+ }
+ else { // set required CSS - overflow and position
+ var
+ CSS = { overflow: "hidden" } // make sure container will not 'scroll'
+ , p = $Container.css("position")
+ , h = $Container.css("height")
+ ;
+ // if this is a NESTED layout, then outer-pane ALREADY has position and height
+ if (!$Container.hasClass("ui-layout-pane")) {
+ if (!p || "fixed,absolute,relative".indexOf(p) < 0)
+ CSS.position = "relative"; // container MUST have a 'position'
+ if (!h || h=="auto")
+ CSS.height = "100%"; // container MUST have a 'height'
+ }
+ $Container.css( CSS );
+ }
+ } catch (ex) {}
+
+ // get layout-container dimensions (updated when necessary)
+ cDims = state.container = getElemDims( $Container ); // update data-pointer too
+ };
+
+ /**
+ * initHotkeys
+ *
+ * Bind layout hotkeys - if options enabled
+ *
+ * @callers create()
+ */
+ var initHotkeys = function () {
+ // bind keyDown to capture hotkeys, if option enabled for ANY pane
+ $.each(c.borderPanes.split(","), function (i,pane) {
+ var o = options[pane];
+ if (o.enableCursorHotkey || o.customHotkey) {
+ $(document).keydown( keyDown ); // only need to bind this ONCE
+ return false; // BREAK - binding was done
+ }
+ });
+ };
+
+ /**
+ * initOptions
+ *
+ * Build final CONFIG and OPTIONS data
+ *
+ * @callers create()
+ */
+ var initOptions = function () {
+ // simplify logic by making sure passed 'opts' var has basic keys
+ opts = transformData( opts );
+
+ // update default effects, if case user passed key
+ if (opts.effects) {
+ $.extend( effects, opts.effects );
+ delete opts.effects;
+ }
+
+ // see if any 'global options' were specified
+ $.each("name,scrollToBookmarkOnLoad".split(","), function (idx,key) {
+ if (opts[key] !== undefined)
+ options[key] = opts[key];
+ else if (opts.defaults[key] !== undefined) {
+ options[key] = opts.defaults[key];
+ delete opts.defaults[key];
+ }
+ });
+
+ // remove any 'defaults' that MUST be set 'per-pane'
+ $.each("paneSelector,resizerCursor,customHotkey".split(","),
+ function (idx,key) { delete opts.defaults[key]; } // is OK if key does not exist
+ );
+
+ // now update options.defaults
+ $.extend( options.defaults, opts.defaults );
+ // make sure required sub-keys exist
+ //if (typeof options.defaults.fxSettings != "object") options.defaults.fxSettings = {};
+
+ // merge all config & options for the 'center' pane
+ c.center = $.extend( true, {}, c.defaults, c.center );
+ $.extend( options.center, opts.center );
+ // Most 'default options' do not apply to 'center', so add only those that DO
+ var o_Center = $.extend( true, {}, options.defaults, opts.defaults, options.center ); // TEMP data
+ $.each("paneClass,contentSelector,contentIgnoreSelector,applyDefaultStyles,showOverflowOnHover".split(","),
+ function (idx,key) { options.center[key] = o_Center[key]; }
+ );
+
+ var defs = options.defaults;
+
+ // create a COMPLETE set of options for EACH border-pane
+ $.each(c.borderPanes.split(","), function(i,pane) {
+ // apply 'pane-defaults' to CONFIG.PANE
+ c[pane] = $.extend( true, {}, c.defaults, c[pane] );
+ // apply 'pane-defaults' + user-options to OPTIONS.PANE
+ o = options[pane] = $.extend( true, {}, options.defaults, options[pane], opts.defaults, opts[pane] );
+
+ // make sure we have base-classes
+ if (!o.paneClass) o.paneClass = defaults.paneClass;
+ if (!o.resizerClass) o.resizerClass = defaults.resizerClass;
+ if (!o.togglerClass) o.togglerClass = defaults.togglerClass;
+
+ // create FINAL fx options for each pane, ie: options.PANE.fxName/fxSpeed/fxSettings[_open|_close]
+ $.each(["_open","_close",""], function (i,n) {
+ var
+ sName = "fxName"+n
+ , sSpeed = "fxSpeed"+n
+ , sSettings = "fxSettings"+n
+ ;
+ // recalculate fxName according to specificity rules
+ o[sName] =
+ opts[pane][sName] // opts.west.fxName_open
+ || opts[pane].fxName // opts.west.fxName
+ || opts.defaults[sName] // opts.defaults.fxName_open
+ || opts.defaults.fxName // opts.defaults.fxName
+ || o[sName] // options.west.fxName_open
+ || o.fxName // options.west.fxName
+ || defs[sName] // options.defaults.fxName_open
+ || defs.fxName // options.defaults.fxName
+ || "none"
+ ;
+ // validate fxName to be sure is a valid effect
+ var fxName = o[sName];
+ if (fxName == "none" || !$.effects || !$.effects[fxName] || (!effects[fxName] && !o[sSettings] && !o.fxSettings))
+ fxName = o[sName] = "none"; // effect not loaded, OR undefined FX AND fxSettings not passed
+ // set vars for effects subkeys to simplify logic
+ var
+ fx = effects[fxName] || {} // effects.slide
+ , fx_all = fx.all || {} // effects.slide.all
+ , fx_pane = fx[pane] || {} // effects.slide.west
+ ;
+ // RECREATE the fxSettings[_open|_close] keys using specificity rules
+ o[sSettings] = $.extend(
+ {}
+ , fx_all // effects.slide.all
+ , fx_pane // effects.slide.west
+ , defs.fxSettings || {} // options.defaults.fxSettings
+ , defs[sSettings] || {} // options.defaults.fxSettings_open
+ , o.fxSettings // options.west.fxSettings
+ , o[sSettings] // options.west.fxSettings_open
+ , opts.defaults.fxSettings // opts.defaults.fxSettings
+ , opts.defaults[sSettings] || {} // opts.defaults.fxSettings_open
+ , opts[pane].fxSettings // opts.west.fxSettings
+ , opts[pane][sSettings] || {} // opts.west.fxSettings_open
+ );
+ // recalculate fxSpeed according to specificity rules
+ o[sSpeed] =
+ opts[pane][sSpeed] // opts.west.fxSpeed_open
+ || opts[pane].fxSpeed // opts.west.fxSpeed (pane-default)
+ || opts.defaults[sSpeed] // opts.defaults.fxSpeed_open
+ || opts.defaults.fxSpeed // opts.defaults.fxSpeed
+ || o[sSpeed] // options.west.fxSpeed_open
+ || o[sSettings].duration // options.west.fxSettings_open.duration
+ || o.fxSpeed // options.west.fxSpeed
+ || o.fxSettings.duration // options.west.fxSettings.duration
+ || defs.fxSpeed // options.defaults.fxSpeed
+ || defs.fxSettings.duration// options.defaults.fxSettings.duration
+ || fx_pane.duration // effects.slide.west.duration
+ || fx_all.duration // effects.slide.all.duration
+ || "normal" // DEFAULT
+ ;
+ // DEBUG: if (pane=="east") debugData( $.extend({}, {speed: o[sSpeed], fxSettings_duration: o[sSettings].duration}, o[sSettings]), pane+"."+sName+" = "+fxName );
+ });
+ });
+ };
+
+ /**
+ * initPanes
+ *
+ * Initialize module objects, styling, size and position for all panes
+ *
+ * @callers create()
+ */
+ var initPanes = function () {
+ // NOTE: do north & south FIRST so we can measure their height - do center LAST
+ $.each(c.allPanes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ , fx = s.fx
+ , dir = c[pane].dir
+ // if o.size is not > 0, then we will use MEASURE the pane and use that as it's 'size'
+ , size = o.size=="auto" || isNaN(o.size) ? 0 : o.size
+ , minSize = o.minSize || 1
+ , maxSize = o.maxSize || 9999
+ , spacing = o.spacing_open || 0
+ , sel = o.paneSelector
+ , isIE6 = ($.browser.msie && $.browser.version < 7)
+ , CSS = {}
+ , $P, $C
+ ;
+ $Cs[pane] = false; // init
+
+ if (sel.substr(0,1)==="#") // ID selector
+ // NOTE: elements selected 'by ID' DO NOT have to be 'children'
+ $P = $Ps[pane] = $Container.find(sel+":first");
+ else { // class or other selector
+ $P = $Ps[pane] = $Container.children(sel+":first");
+ // look for the pane nested inside a 'form' element
+ if (!$P.length) $P = $Ps[pane] = $Container.children("form:first").children(sel+":first");
+ }
+
+ if (!$P.length) {
+ $Ps[pane] = false; // logic
+ return true; // SKIP to next
+ }
+
+ // add basic classes & attributes
+ $P
+ .attr("pane", pane) // add pane-identifier
+ .addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
+ ;
+
+ // init pane-logic vars, etc.
+ if (pane != "center") {
+ s.isClosed = false; // true = pane is closed
+ s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
+ s.isResizing= false; // true = pane is in process of being resized
+ s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
+ s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
+ // create special keys for internal use
+ c[pane].pins = []; // used to track and sync 'pin-buttons' for border-panes
+ }
+
+ CSS = $.extend({ visibility: "visible", display: "block" }, c.defaults.cssReq, c[pane].cssReq );
+ if (o.applyDefaultStyles) $.extend( CSS, c.defaults.cssDef, c[pane].cssDef ); // cosmetic defaults
+ $P.css(CSS); // add base-css BEFORE 'measuring' to calc size & position
+ CSS = {}; // reset var
+
+ // set css-position to account for container borders & padding
+ switch (pane) {
+ case "north": CSS.top = cDims.top;
+ CSS.left = cDims.left;
+ CSS.right = cDims.right;
+ break;
+ case "south": CSS.bottom = cDims.bottom;
+ CSS.left = cDims.left;
+ CSS.right = cDims.right;
+ break;
+ case "west": CSS.left = cDims.left; // top, bottom & height set by sizeMidPanes()
+ break;
+ case "east": CSS.right = cDims.right; // ditto
+ break;
+ case "center": // top, left, width & height set by sizeMidPanes()
+ }
+
+ if (dir == "horz") { // north or south pane
+ if (size === 0 || size == "auto") {
+ $P.css({ height: "auto" });
+ size = $P.outerHeight();
+ }
+ size = max(size, minSize);
+ size = min(size, maxSize);
+ size = min(size, cDims.innerHeight - spacing);
+ CSS.height = max(1, cssH(pane, size));
+ s.size = size; // update state
+ // make sure minSize is sufficient to avoid errors
+ s.maxSize = maxSize; // init value
+ s.minSize = max(minSize, size - CSS.height + 1); // = pane.outerHeight when css.height = 1px
+ // handle IE6
+ //if (isIE6) CSS.width = cssW($P, cDims.innerWidth);
+ $P.css(CSS); // apply size & position
+ }
+ else if (dir == "vert") { // east or west pane
+ if (size === 0 || size == "auto") {
+ $P.css({ width: "auto", float: "left" }); // float = FORCE pane to auto-size
+ size = $P.outerWidth();
+ $P.css({ float: "none" }); // RESET
+ }
+ size = max(size, minSize);
+ size = min(size, maxSize);
+ size = min(size, cDims.innerWidth - spacing);
+ CSS.width = max(1, cssW(pane, size));
+ s.size = size; // update state
+ s.maxSize = maxSize; // init value
+ // make sure minSize is sufficient to avoid errors
+ s.minSize = max(minSize, size - CSS.width + 1); // = pane.outerWidth when css.width = 1px
+ $P.css(CSS); // apply size - top, bottom & height set by sizeMidPanes
+ sizeMidPanes(pane, null, true); // true = onInit
+ }
+ else if (pane == "center") {
+ $P.css(CSS); // top, left, width & height set by sizeMidPanes...
+ sizeMidPanes("center", null, true); // true = onInit
+ }
+
+ // close or hide the pane if specified in settings
+ if (o.initClosed && o.closable) {
+ $P.hide().addClass("closed");
+ s.isClosed = true;
+ }
+ else if (o.initHidden || o.initClosed) {
+ hide(pane, true); // will be completely invisible - no resizer or spacing
+ s.isHidden = true;
+ }
+ else
+ $P.addClass("open");
+
+ // check option for auto-handling of pop-ups & drop-downs
+ if (o.showOverflowOnHover)
+ $P.hover( allowOverflow, resetOverflow );
+
+ /*
+ * see if this pane has a 'content element' that we need to auto-size
+ */
+ if (o.contentSelector) {
+ $C = $Cs[pane] = $P.children(o.contentSelector+":first"); // match 1-element only
+ if (!$C.length) {
+ $Cs[pane] = false;
+ return true; // SKIP to next
+ }
+ $C.css( c.content.cssReq );
+ if (o.applyDefaultStyles) $C.css( c.content.cssDef ); // cosmetic defaults
+ // NO PANE-SCROLLING when there is a content-div
+ $P.css({ overflow: "hidden" });
+ }
+ });
+ };
+
+ /**
+ * initHandles
+ *
+ * Initialize module objects, styling, size and position for all resize bars and toggler buttons
+ *
+ * @callers create()
+ */
+ var initHandles = function () {
+ // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
+ $.each(c.borderPanes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ , rClass = o.resizerClass
+ , tClass = o.togglerClass
+ , $P = $Ps[pane]
+ ;
+ $Rs[pane] = false; // INIT
+ $Ts[pane] = false;
+
+ if (!$P || (!o.closable && !o.resizable)) return; // pane does not exist - skip
+
+ var
+ edge = c[pane].edge
+ , isOpen = $P.is(":visible")
+ , spacing = (isOpen ? o.spacing_open : o.spacing_closed)
+ , _pane = "-"+ pane // used for classNames
+ , _state = (isOpen ? "-open" : "-closed") // used for classNames
+ , $R, $T
+ ;
+ // INIT RESIZER BAR
+ $R = $Rs[pane] = $("<span></span>");
+
+ if (isOpen && o.resizable)
+ ; // this is handled by initResizable
+ else if (!isOpen && o.slidable)
+ $R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
+
+ $R
+ // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
+ .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
+ .attr("resizer", pane) // so we can read this from the resizer
+ .css(c.resizers.cssReq) // add base/required styles
+ // POSITION of resizer bar - allow for container border & padding
+ .css(edge, cDims[edge] + getPaneSize(pane))
+ // ADD CLASSNAMES - eg: class="resizer resizer-west resizer-open"
+ .addClass( rClass +" "+ rClass+_pane +" "+ rClass+_state +" "+ rClass+_pane+_state )
+ .appendTo($Container) // append DIV to container
+ ;
+ // ADD VISUAL STYLES
+ if (o.applyDefaultStyles)
+ $R.css(c.resizers.cssDef);
+
+ if (o.closable) {
+ // INIT COLLAPSER BUTTON
+ $T = $Ts[pane] = $("<div></div>");
+ $T
+ // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-toggler"
+ .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
+ .css(c.togglers.cssReq) // add base/required styles
+ .attr("title", (isOpen ? o.togglerTip_open : o.togglerTip_closed))
+ .click(function(evt){ toggle(pane); evt.stopPropagation(); })
+ .mouseover(function(evt){ evt.stopPropagation(); }) // prevent resizer event
+ // ADD CLASSNAMES - eg: class="toggler toggler-west toggler-west-open"
+ .addClass( tClass +" "+ tClass+_pane +" "+ tClass+_state +" "+ tClass+_pane+_state )
+ .appendTo($R) // append SPAN to resizer DIV
+ ;
+
+ // ADD INNER-SPANS TO TOGGLER
+ if (o.togglerContent_open) // ui-layout-open
+ $("<span>"+ o.togglerContent_open +"</span>")
+ .addClass("content content-open")
+ .css("display", s.isClosed ? "none" : "block")
+ .appendTo( $T )
+ ;
+ if (o.togglerContent_closed) // ui-layout-closed
+ $("<span>"+ o.togglerContent_closed +"</span>")
+ .addClass("content content-closed")
+ .css("display", s.isClosed ? "block" : "none")
+ .appendTo( $T )
+ ;
+
+ // ADD BASIC VISUAL STYLES
+ if (o.applyDefaultStyles)
+ $T.css(c.togglers.cssDef);
+
+ if (!isOpen) bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
+ }
+
+ });
+
+ // SET ALL HANDLE SIZES & LENGTHS
+ sizeHandles("all", true); // true = onInit
+ };
+
+ /**
+ * initResizable
+ *
+ * Add resize-bars to all panes that specify it in options
+ *
+ * @dependancies $.fn.resizable - will abort if not found
+ * @callers create()
+ */
+ var initResizable = function () {
+ var
+ draggingAvailable = (typeof $.fn.draggable == "function")
+ , minPosition, maxPosition, edge // set in start()
+ ;
+
+ $.each(c.borderPanes.split(","), function() {
+ var
+ pane = str(this)
+ , o = options[pane]
+ , s = state[pane]
+ ;
+ if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
+ o.resizable = false;
+ return true; // skip to next
+ }
+
+ var
+ rClass = o.resizerClass
+ // 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
+ , dragClass = rClass+"-drag" // resizer-drag
+ , dragPaneClass = rClass+"-"+pane+"-drag" // resizer-north-drag
+ // 'dragging' class is applied to the CLONED resizer-bar while it is being dragged
+ , draggingClass = rClass+"-dragging" // resizer-dragging
+ , draggingPaneClass = rClass+"-"+pane+"-dragging" // resizer-north-dragging
+ , draggingClassSet = false // logic var
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+
+ if (!s.isClosed)
+ $R
+ .attr("title", o.resizerTip)
+ .css("cursor", o.resizerCursor) // n-resize, s-resize, etc
+ ;
+
+ $R.draggable({
+ containment: $Container[0] // limit resizing to layout container
+ , axis: (c[pane].dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
+ , delay: 200
+ , distance: 1
+ // basic format for helper - style it using class: .ui-draggable-dragging
+ , helper: "clone"
+ , opacity: o.resizerDragOpacity
+ //, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
+ , zIndex: c.zIndex.resizing
+
+ , start: function (e, ui) {
+ // onresize_start callback - will CANCEL hide if returns false
+ // TODO: CONFIRM that dragging can be cancelled like this???
+ if (false === execUserCallback(pane, o.onresize_start)) return false;
+
+ s.isResizing = true; // prevent pane from closing while resizing
+ clearTimer(pane, "closeSlider"); // just in case already triggered
+
+ $R.addClass( dragClass +" "+ dragPaneClass ); // add drag classes
+ draggingClassSet = false; // reset logic var - see drag()
+
+ // SET RESIZING LIMITS - used in drag()
+ var resizerWidth = (pane=="east" || pane=="south" ? o.spacing_open : 0);
+ setPaneMinMaxSizes(pane); // update pane-state
+ s.minPosition -= resizerWidth;
+ s.maxPosition -= resizerWidth;
+ edge = (c[pane].dir=="horz" ? "top" : "left");
+
+ // MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
+ $(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).each(function() {
+ $('<div class="ui-layout-mask"/>')
+ .css({
+ background: "#fff"
+ , opacity: "0.001"
+ , zIndex: 9
+ , position: "absolute"
+ , width: this.offsetWidth+"px"
+ , height: this.offsetHeight+"px"
+ })
+ .css($(this).offset()) // top & left
+ .appendTo(this.parentNode) // put div INSIDE pane to avoid zIndex issues
+ ;
+ });
+ }
+
+ , drag: function (e, ui) {
+ if (!draggingClassSet) { // can only add classes after clone has been added to the DOM
+ $(".ui-draggable-dragging")
+ .addClass( draggingClass +" "+ draggingPaneClass ) // add dragging classes
+ .children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
+ ;
+ draggingClassSet = true;
+ // draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
+ if (s.isSliding) $Ps[pane].css("zIndex", c.zIndex.sliding);
+ }
+ // CONTAIN RESIZER-BAR TO RESIZING LIMITS
+ if (ui.position[edge] < s.minPosition) ui.position[edge] = s.minPosition;
+ else if (ui.position[edge] > s.maxPosition) ui.position[edge] = s.maxPosition;
+ }
+
+ , stop: function (e, ui) {
+ var
+ dragPos = ui.position
+ , resizerPos
+ , newSize
+ ;
+ $R.removeClass( dragClass +" "+ dragPaneClass ); // remove drag classes
+
+ switch (pane) {
+ case "north": resizerPos = dragPos.top; break;
+ case "west": resizerPos = dragPos.left; break;
+ case "south": resizerPos = cDims.outerHeight - dragPos.top - $R.outerHeight(); break;
+ case "east": resizerPos = cDims.outerWidth - dragPos.left - $R.outerWidth(); break;
+ }
+ // remove container margin from resizer position to get the pane size
+ newSize = resizerPos - cDims[ c[pane].edge ];
+
+ sizePane(pane, newSize);
+
+ // UN-MASK PANES MASKED IN drag.start
+ $("div.ui-layout-mask").remove(); // Remove iframe masks
+
+ s.isResizing = false;
+ }
+
+ });
+ });
+ };
+
+
+
+/*
+ * ###########################
+ * ACTION METHODS
+ * ###########################
+ */
+
+ /**
+ * hide / show
+ *
+ * Completely 'hides' a pane, including its spacing - as if it does not exist
+ * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
+ *
+ * @param String pane The pane being hidden, ie: north, south, east, or west
+ */
+ var hide = function (pane, onInit) {
+ var
+ o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+ if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
+
+ // onhide_start callback - will CANCEL hide if returns false
+ if (false === execUserCallback(pane, o.onhide_start)) return;
+
+ s.isSliding = false; // just in case
+
+ // now hide the elements
+ if ($R) $R.hide(); // hide resizer-bar
+ if (onInit || s.isClosed) {
+ s.isClosed = true; // to trigger open-animation on show()
+ s.isHidden = true;
+ $P.hide(); // no animation when loading page
+ sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
+ execUserCallback(pane, o.onhide_end || o.onhide);
+ }
+ else {
+ s.isHiding = true; // used by onclose
+ close(pane, false); // adjust all panes to fit
+ //s.isHidden = true; - will be set by close - if not cancelled
+ }
+ };
+
+ var show = function (pane, openPane) {
+ var
+ o = options[pane]
+ , s = state[pane]
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+ if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
+
+ // onhide_start callback - will CANCEL hide if returns false
+ if (false === execUserCallback(pane, o.onshow_start)) return;
+
+ s.isSliding = false; // just in case
+ s.isShowing = true; // used by onopen/onclose
+ //s.isHidden = false; - will be set by open/close - if not cancelled
+
+ // now show the elements
+ if ($R && o.spacing_open > 0) $R.show();
+ if (openPane === false)
+ close(pane, true); // true = force
+ else
+ open(pane); // adjust all panes to fit
+ };
+
+
+ /**
+ * toggle
+ *
+ * Toggles a pane open/closed by calling either open or close
+ *
+ * @param String pane The pane being toggled, ie: north, south, east, or west
+ */
+ var toggle = function (pane) {
+ var s = state[pane];
+ if (s.isHidden)
+ show(pane); // will call 'open' after unhiding it
+ else if (s.isClosed)
+ open(pane);
+ else
+ close(pane);
+ };
+
+ /**
+ * close
+ *
+ * Close the specified pane (animation optional), and resize all other panes as needed
+ *
+ * @param String pane The pane being closed, ie: north, south, east, or west
+ */
+ var close = function (pane, force, noAnimation) {
+ var
+ $P = $Ps[pane]
+ , $R = $Rs[pane]
+ , $T = $Ts[pane]
+ , o = options[pane]
+ , s = state[pane]
+ , doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none")
+ , edge = c[pane].edge
+ , rClass = o.resizerClass
+ , tClass = o.togglerClass
+ , _pane = "-"+ pane // used for classNames
+ , _open = "-open"
+ , _sliding= "-sliding"
+ , _closed = "-closed"
+ // transfer logic vars to temp vars
+ , isShowing = s.isShowing
+ , isHiding = s.isHiding
+ ;
+ // now clear the logic vars
+ delete s.isShowing;
+ delete s.isHiding;
+
+ if (!$P || (!o.resizable && !o.closable)) return; // invalid request
+ else if (!force && s.isClosed && !isShowing) return; // already closed
+
+ if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
+ setFlowCallback("close", pane, force); // set a callback for this action, if possible
+ return; // ABORT
+ }
+
+ // onclose_start callback - will CANCEL hide if returns false
+ // SKIP if just 'showing' a hidden pane as 'closed'
+ if (!isShowing && false === execUserCallback(pane, o.onclose_start)) return;
+
+ // SET flow-control flags
+ c[pane].isMoving = true;
+ c.isLayoutBusy = true;
+
+ s.isClosed = true;
+ // update isHidden BEFORE sizing panes
+ if (isHiding) s.isHidden = true;
+ else if (isShowing) s.isHidden = false;
+
+ // sync any 'pin buttons'
+ syncPinBtns(pane, false);
+
+ // resize panes adjacent to this one
+ if (!s.isSliding) sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
+
+ // if this pane has a resizer bar, move it now
+ if ($R) {
+ $R
+ .css(edge, cDims[edge]) // move the resizer bar
+ .removeClass( rClass+_open +" "+ rClass+_pane+_open )
+ .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
+ .addClass( rClass+_closed +" "+ rClass+_pane+_closed )
+ ;
+ // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent
+ if (o.resizable)
+ $R
+ .draggable("disable")
+ .css("cursor", "default")
+ .attr("title","")
+ ;
+ // if pane has a toggler button, adjust that too
+ if ($T) {
+ $T
+ .removeClass( tClass+_open +" "+ tClass+_pane+_open )
+ .addClass( tClass+_closed +" "+ tClass+_pane+_closed )
+ .attr("title", o.togglerTip_closed) // may be blank
+ ;
+ }
+ sizeHandles(); // resize 'length' and position togglers for adjacent panes
+ }
+
+ // ANIMATE 'CLOSE' - if no animation, then was ALREADY shown above
+ if (doFX) {
+ lockPaneForFX(pane, true); // need to set left/top so animation will work
+ $P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
+ lockPaneForFX(pane, false); // undo
+ if (!s.isClosed) return; // pane was opened before animation finished!
+ close_2();
+ });
+ }
+ else {
+ $P.hide(); // just hide pane NOW
+ close_2();
+ }
+
+ // SUBROUTINE
+ function close_2 () {
+ bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
+
+ // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
+ if (!isShowing) execUserCallback(pane, o.onclose_end || o.onclose);
+ // onhide OR onshow callback
+ if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
+ if (isHiding) execUserCallback(pane, o.onhide_end || o.onhide);
+
+ // internal flow-control callback
+ execFlowCallback(pane);
+ }
+ };
+
+ /**
+ * open
+ *
+ * Open the specified pane (animation optional), and resize all other panes as needed
+ *
+ * @param String pane The pane being opened, ie: north, south, east, or west
+ */
+ var open = function (pane, slide, noAnimation) {
+ var
+ $P = $Ps[pane]
+ , $R = $Rs[pane]
+ , $T = $Ts[pane]
+ , o = options[pane]
+ , s = state[pane]
+ , doFX = !noAnimation && s.isClosed && (o.fxName_open != "none")
+ , edge = c[pane].edge
+ , rClass = o.resizerClass
+ , tClass = o.togglerClass
+ , _pane = "-"+ pane // used for classNames
+ , _open = "-open"
+ , _closed = "-closed"
+ , _sliding= "-sliding"
+ // transfer logic var to temp var
+ , isShowing = s.isShowing
+ ;
+ // now clear the logic var
+ delete s.isShowing;
+
+ if (!$P || (!o.resizable && !o.closable)) return; // invalid request
+ else if (!s.isClosed && !s.isSliding) return; // already open
+
+ // pane can ALSO be unhidden by just calling show(), so handle this scenario
+ if (s.isHidden && !isShowing) {
+ show(pane, true);
+ return;
+ }
+
+ if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
+ setFlowCallback("open", pane, slide); // set a callback for this action, if possible
+ return; // ABORT
+ }
+
+ // onopen_start callback - will CANCEL hide if returns false
+ if (false === execUserCallback(pane, o.onopen_start)) return;
+
+ // SET flow-control flags
+ c[pane].isMoving = true;
+ c.isLayoutBusy = true;
+
+ // 'PIN PANE' - stop sliding
+ if (s.isSliding && !slide) // !slide = 'open pane normally' - NOT sliding
+ bindStopSlidingEvents(pane, false); // will set isSliding=false
+
+ s.isClosed = false;
+ // update isHidden BEFORE sizing panes
+ if (isShowing) s.isHidden = false;
+
+ // Container size may have changed - shrink the pane if now 'too big'
+ setPaneMinMaxSizes(pane); // update pane-state
+ if (s.size > s.maxSize) // pane is too big! resize it before opening
+ $P.css( c[pane].sizeType, max(1, cssSize(pane, s.maxSize)) );
+
+ bindStartSlidingEvent(pane, false); // remove trigger event from resizer-bar
+
+ if (doFX) { // ANIMATE
+ lockPaneForFX(pane, true); // need to set left/top so animation will work
+ $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
+ lockPaneForFX(pane, false); // undo
+ if (s.isClosed) return; // pane was closed before animation finished!
+ open_2(); // continue
+ });
+ }
+ else {// no animation
+ $P.show(); // just show pane and...
+ open_2(); // continue
+ }
+
+ // SUBROUTINE
+ function open_2 () {
+ // NOTE: if isSliding, then other panes are NOT 'resized'
+ if (!s.isSliding) // resize all panes adjacent to this one
+ sizeMidPanes(c[pane].dir=="vert" ? "center" : "all");
+
+ // if this pane has a toggler, move it now
+ if ($R) {
+ $R
+ .css(edge, cDims[edge] + getPaneSize(pane)) // move the toggler
+ .removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
+ .addClass( rClass+_open +" "+ rClass+_pane+_open )
+ .addClass( !s.isSliding ? "" : rClass+_sliding +" "+ rClass+_pane+_sliding )
+ ;
+ if (o.resizable)
+ $R
+ .draggable("enable")
+ .css("cursor", o.resizerCursor)
+ .attr("title", o.resizerTip)
+ ;
+ else
+ $R.css("cursor", "default"); // n-resize, s-resize, etc
+ // if pane also has a toggler button, adjust that too
+ if ($T) {
+ $T
+ .removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
+ .addClass( tClass+_open +" "+ tClass+_pane+_open )
+ .attr("title", o.togglerTip_open) // may be blank
+ ;
+ }
+ sizeHandles("all"); // resize resizer & toggler sizes for all panes
+ }
+
+ // resize content every time pane opens - to be sure
+ sizeContent(pane);
+
+ // sync any 'pin buttons'
+ syncPinBtns(pane, !s.isSliding);
+
+ // onopen callback
+ execUserCallback(pane, o.onopen_end || o.onopen);
+
+ // onshow callback
+ if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
+
+ // internal flow-control callback
+ execFlowCallback(pane);
+ }
+ };
+
+
+ /**
+ * lockPaneForFX
+ *
+ * Must set left/top on East/South panes so animation will work properly
+ *
+ * @param String pane The pane to lock, 'east' or 'south' - any other is ignored!
+ * @param Boolean doLock true = set left/top, false = remove
+ */
+ var lockPaneForFX = function (pane, doLock) {
+ var $P = $Ps[pane];
+ if (doLock) {
+ $P.css({ zIndex: c.zIndex.animation }); // overlay all elements during animation
+ if (pane=="south")
+ $P.css({ top: cDims.top + cDims.innerHeight - $P.outerHeight() });
+ else if (pane=="east")
+ $P.css({ left: cDims.left + cDims.innerWidth - $P.outerWidth() });
+ }
+ else {
+ if (!state[pane].isSliding) $P.css({ zIndex: c.zIndex.pane_normal });
+ if (pane=="south")
+ $P.css({ top: "auto" });
+ else if (pane=="east")
+ $P.css({ left: "auto" });
+ }
+ };
+
+
+ /**
+ * bindStartSlidingEvent
+ *
+ * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
+ *
+ * @callers open(), close()
+ * @param String pane The pane to enable/disable, 'north', 'south', etc.
+ * @param Boolean enable Enable or Disable sliding?
+ */
+ var bindStartSlidingEvent = function (pane, enable) {
+ var
+ o = options[pane]
+ , $R = $Rs[pane]
+ , trigger = o.slideTrigger_open
+ ;
+ if (!$R || !o.slidable) return;
+ // make sure we have a valid event
+ if (trigger != "click" && trigger != "dblclick" && trigger != "mouseover") trigger = "click";
+ $R
+ // add or remove trigger event
+ [enable ? "bind" : "unbind"](trigger, slideOpen)
+ // set the appropriate cursor & title/tip
+ .css("cursor", (enable ? o.sliderCursor: "default"))
+ .attr("title", (enable ? o.sliderTip : ""))
+ ;
+ };
+
+ /**
+ * bindStopSlidingEvents
+ *
+ * Add or remove 'mouseout' events to 'slide close' when pane is 'sliding' open or closed
+ * Also increases zIndex when pane is sliding open
+ * See bindStartSlidingEvent for code to control 'slide open'
+ *
+ * @callers slideOpen(), slideClosed()
+ * @param String pane The pane to process, 'north', 'south', etc.
+ * @param Boolean isOpen Is pane open or closed?
+ */
+ var bindStopSlidingEvents = function (pane, enable) {
+ var
+ o = options[pane]
+ , s = state[pane]
+ , trigger = o.slideTrigger_close
+ , action = (enable ? "bind" : "unbind") // can't make 'unbind' work! - see disabled code below
+ , $P = $Ps[pane]
+ , $R = $Rs[pane]
+ ;
+
+ s.isSliding = enable; // logic
+ clearTimer(pane, "closeSlider"); // just in case
+
+ // raise z-index when sliding
+ $P.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.pane_normal) });
+ $R.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.resizer_normal) });
+
+ // make sure we have a valid event
+ if (trigger != "click" && trigger != "mouseout") trigger = "mouseout";
+
+ // when trigger is 'mouseout', must cancel timer when mouse moves between 'pane' and 'resizer'
+ if (enable) { // BIND trigger events
+ $P.bind(trigger, slideClosed );
+ $R.bind(trigger, slideClosed );
+ if (trigger = "mouseout") {
+ $P.bind("mouseover", cancelMouseOut );
+ $R.bind("mouseover", cancelMouseOut );
+ }
+ }
+ else { // UNBIND trigger events
+ // TODO: why does unbind of a 'single function' not work reliably?
+ //$P[action](trigger, slideClosed );
+ $P.unbind(trigger);
+ $R.unbind(trigger);
+ if (trigger = "mouseout") {
+ //$P[action]("mouseover", cancelMouseOut );
+ $P.unbind("mouseover");
+ $R.unbind("mouseover");
+ clearTimer(pane, "closeSlider");
+ }
+ }
+
+ // SUBROUTINE for mouseout timer clearing
+ function cancelMouseOut (evt) {
+ clearTimer(pane, "closeSlider");
+ evt.stopPropagation();
+ }
+ };
+
+ var slideOpen = function () {
+ var pane = $(this).attr("resizer"); // attr added by initHandles
+ if (state[pane].isClosed) { // skip if already open!
+ bindStopSlidingEvents(pane, true); // pane is opening, so BIND trigger events to close it
+ open(pane, true); // true = slide - ie, called from here!
+ }
+ };
+
+ var slideClosed = function () {
+ var
+ $E = $(this)
+ , pane = $E.attr("pane") || $E.attr("resizer")
+ , o = options[pane]
+ , s = state[pane]
+ ;
+ if (s.isClosed || s.isResizing)
+ return; // skip if already closed OR in process of resizing
+ else if (o.slideTrigger_close == "click")
+ close_NOW(); // close immediately onClick
+ else // trigger = mouseout - use a delay
+ setTimer(pane, "closeSlider", close_NOW, 300); // .3 sec delay
+
+ // SUBROUTINE for timed close
+ function close_NOW () {
+ bindStopSlidingEvents(pane, false); // pane is being closed, so UNBIND trigger events
+ if (!s.isClosed) close(pane); // skip if already closed!
+ }
+ };
+
+
+ /**
+ * sizePane