jquery-layout-rails 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -22,4 +22,4 @@ Alternatively you can add both dependencies at once:
22
22
  If you have multiple requires in your application, you shouldn't include jquery in more
23
23
  than one of them or it will be included more than once. This means it will remove all prior
24
24
  registered plugins on each inclusion. So, if that is your case you should include
25
- `jquery/layout` instead of `jquery-layou` for better control over jQuery inclusion.
25
+ `jquery/layout` instead of `jquery-layout` for better control over jQuery inclusion.
@@ -1,3 +1,3 @@
1
1
  module JqueryLayoutRails
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,7 +1,8 @@
1
1
  /**
2
- * @preserve jquery.layout 1.3.0 - Release Candidate 30.51
3
- * $Date: 2012-05-12 08:00:00 (Sat, 12 May 2012) $
4
- * $Rev: 303005 $
2
+ * @preserve
3
+ * jquery.layout 1.3.0 - Release Candidate 30.79
4
+ * $Date: 2013-01-12 08:00:00 (Sat, 12 Jan 2013) $
5
+ * $Rev: 303007 $
5
6
  *
6
7
  * Copyright (c) 2012
7
8
  * Fabrizio Balliano (http://www.fabrizioballiano.net)
@@ -10,7 +11,7 @@
10
11
  * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
11
12
  * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
12
13
  *
13
- * Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.5
14
+ * Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.79
14
15
  *
15
16
  * Docs: http://layout.jquery-dev.net/documentation.html
16
17
  * Tips: http://layout.jquery-dev.net/tips.html
@@ -23,6 +24,10 @@
23
24
  * {number=} optional parameter
24
25
  * {*} ALL types
25
26
  */
27
+ /* TODO for jQ 2.0
28
+ * change .andSelf() to .addBack()
29
+ * $.fn.disableSelection won't work
30
+ */
26
31
 
27
32
  // NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars
28
33
 
@@ -32,61 +37,38 @@
32
37
  var min = Math.min
33
38
  , max = Math.max
34
39
  , round = Math.floor
35
- ;
36
- function isStr (v) { return $.type(v) === "string"; }
37
-
38
- function runPluginCallbacks (Instance, a_fn) {
39
- if ($.isArray(a_fn))
40
- for (var i=0, c=a_fn.length; i<c; i++) {
41
- var fn = a_fn[i];
42
- try {
43
- if (isStr(fn)) // 'name' of a function
44
- fn = eval(fn);
45
- if ($.isFunction(fn))
46
- fn( Instance );
47
- } catch (ex) {}
48
- }
49
- };
50
40
 
41
+ , isStr = function (v) { return $.type(v) === "string"; }
51
42
 
43
+ /**
44
+ * @param {!Object} Instance
45
+ * @param {Array.<string>} a_fn
46
+ */
47
+ , runPluginCallbacks = function (Instance, a_fn) {
48
+ if ($.isArray(a_fn))
49
+ for (var i=0, c=a_fn.length; i<c; i++) {
50
+ var fn = a_fn[i];
51
+ try {
52
+ if (isStr(fn)) // 'name' of a function
53
+ fn = eval(fn);
54
+ if ($.isFunction(fn))
55
+ g(fn)( Instance );
56
+ } catch (ex) {}
57
+ }
58
+ function g (f) { return f; }; // compiler hack
59
+ }
60
+ ;
52
61
 
53
62
  /*
54
63
  * GENERIC $.layout METHODS - used by all layouts
55
64
  */
56
65
  $.layout = {
57
66
 
58
- version: "1.3.rc30.51"
59
- , revision: 0.033005 // 1.3.0 final = 1.0300 - major(n+).minor(nn)+patch(nn+)
60
-
61
- // LANGUAGE CUSTOMIZATION
62
- , language: {
63
- // Tips and messages for resizers, togglers, custom buttons, etc.
64
- Open: "Open" // eg: "Open Pane"
65
- , Close: "Close"
66
- , Resize: "Resize"
67
- , Slide: "Slide Open"
68
- , Pin: "Pin"
69
- , Unpin: "Un-Pin"
70
- , noRoomToOpenTip: "Not enough room to show this pane."
71
- , minSizeWarning: "Panel has reached its minimum size"
72
- , maxSizeWarning: "Panel has reached its maximum size"
73
- // Developer error messages
74
- , pane: "pane" // description of "layout pane element"
75
- , selector: "selector" // description of "jQuery-selector"
76
- , errButton: "Error Adding Button \n\nInvalid "
77
- , errContainerMissing: "UI Layout Initialization Error\n\nThe specified layout-container does not exist."
78
- , errCenterPaneMissing: "UI Layout Initialization Error\n\nThe center-pane element does not exist.\n\nThe center-pane is a required element."
79
- , errContainerHeight: "UI Layout Initialization Warning\n\nThe layout-container \"CONTAINER\" has no height.\n\nTherefore the layout is 0-height and hence 'invisible'!"
80
- }
81
-
82
- // can update code here if $.browser is phased out
83
- , browser: {
84
- mozilla: !!$.browser.mozilla
85
- , webkit: !!$.browser.webkit || !!$.browser.safari // webkit = jQ 1.4
86
- , msie: !!$.browser.msie
87
- , isIE6: !!$.browser.msie && $.browser.version == 6
88
- , version: $.browser.version // not used in Layout core, but may be used by plugins
89
- }
67
+ version: "1.3.rc30.79"
68
+ , revision: 0.033007 // 1.3.0 final = 1.0300 - major(n+).minor(nn)+patch(nn+)
69
+
70
+ // $.layout.browser REPLACES $.browser
71
+ , browser: {} // set below
90
72
 
91
73
  // *PREDEFINED* EFFECTS & DEFAULTS
92
74
  // MUST list effect here - OR MUST set an fxSettings option (can be an empty hash: {})
@@ -200,7 +182,7 @@ $.layout = {
200
182
  }
201
183
  }
202
184
  , north: {
203
- side: "Top"
185
+ side: "top"
204
186
  , sizeType: "Height"
205
187
  , dir: "horz"
206
188
  , cssReq: {
@@ -213,7 +195,7 @@ $.layout = {
213
195
  }
214
196
  }
215
197
  , south: {
216
- side: "Bottom"
198
+ side: "bottom"
217
199
  , sizeType: "Height"
218
200
  , dir: "horz"
219
201
  , cssReq: {
@@ -226,7 +208,7 @@ $.layout = {
226
208
  }
227
209
  }
228
210
  , east: {
229
- side: "Right"
211
+ side: "right"
230
212
  , sizeType: "Width"
231
213
  , dir: "vert"
232
214
  , cssReq: {
@@ -239,7 +221,7 @@ $.layout = {
239
221
  }
240
222
  }
241
223
  , west: {
242
- side: "Left"
224
+ side: "left"
243
225
  , sizeType: "Width"
244
226
  , dir: "vert"
245
227
  , cssReq: {
@@ -298,15 +280,18 @@ $.layout = {
298
280
  return typeof evt === "object" && evt.stopPropagation ? evt : null;
299
281
  }
300
282
  , parsePaneName: function (evt_or_pane) {
301
- // getEventObject() automatically calls .stopPropagation(), WHICH MUST BE DONE!
302
- var evt = $.layout.getEventObject( evt_or_pane );
283
+ var evt = $.layout.getEventObject( evt_or_pane )
284
+ , pane = evt_or_pane;
303
285
  if (evt) {
304
286
  // ALWAYS stop propagation of events triggered in Layout!
305
287
  evt.stopPropagation();
306
- return $(this).data("layoutEdge");
288
+ pane = $(this).data("layoutEdge");
307
289
  }
308
- else
309
- return evt_or_pane;
290
+ if (pane && !/^(west|east|north|south|center)$/.test(pane)) {
291
+ $.layout.msg('LAYOUT ERROR - Invalid pane-name: "'+ pane +'"');
292
+ pane = "error";
293
+ }
294
+ return pane;
310
295
  }
311
296
 
312
297
 
@@ -316,7 +301,7 @@ $.layout = {
316
301
  draggable: !!$.fn.draggable // resizing
317
302
  , effects: {
318
303
  core: !!$.effects // animimations (specific effects tested by initOptions)
319
- , slide: $.effects && $.effects.slide // default effect
304
+ , slide: $.effects && ($.effects.slide || ($.effects.effect && $.effects.effect.slide)) // default effect
320
305
  }
321
306
  }
322
307
 
@@ -338,7 +323,7 @@ $.layout = {
338
323
  , scrollbarHeight: function () { return window.scrollbarHeight || $.layout.getScrollbarSize('height'); }
339
324
  , getScrollbarSize: function (dim) {
340
325
  var $c = $('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body");
341
- var d = { width: $c.width() - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };
326
+ var d = { width: $c.css("width") - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };
342
327
  $c.remove();
343
328
  window.scrollbarWidth = d.width;
344
329
  window.scrollbarHeight = d.height;
@@ -350,19 +335,20 @@ $.layout = {
350
335
  * Returns hash container 'display' and 'visibility'
351
336
  *
352
337
  * @see $.swap() - swaps CSS, runs callback, resets CSS
338
+ * @param {!Object} $E jQuery element
339
+ * @param {boolean=} [force=false] Run even if display != none
340
+ * @return {!Object} Returns current style props, if applicable
353
341
  */
354
342
  , showInvisibly: function ($E, force) {
355
- if (!$E) return {};
356
- if (!$E.jquery) $E = $($E);
357
- var CSS = {
358
- display: $E.css('display')
359
- , visibility: $E.css('visibility')
360
- };
361
- if (force || CSS.display === "none") { // only if not *already hidden*
362
- $E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so can be measured
343
+ if ($E && $E.length && (force || $E.css("display") === "none")) { // only if not *already hidden*
344
+ var s = $E[0].style
345
+ // save ONLY the 'style' props because that is what we must restore
346
+ , CSS = { display: s.display || '', visibility: s.visibility || '' };
347
+ // show element 'invisibly' so can be measured
348
+ $E.css({ display: "block", visibility: "hidden" });
363
349
  return CSS;
364
350
  }
365
- else return {};
351
+ return {};
366
352
  }
367
353
 
368
354
  /**
@@ -371,32 +357,29 @@ $.layout = {
371
357
  * @see _create(), onWindowResize() for container, plus others for pane
372
358
  * @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
373
359
  */
374
- , getElementDimensions: function ($E) {
360
+ , getElementDimensions: function ($E, inset) {
375
361
  var
376
- d = {} // dimensions hash
377
- , x = d.css = {} // CSS hash
378
- , i = {} // TEMP insets
379
- , b, p // TEMP border, padding
362
+ // dimensions hash - start with current data IF passed
363
+ d = { css: {}, inset: {} }
364
+ , x = d.css // CSS hash
365
+ , i = { bottom: 0 } // TEMP insets (bottom = complier hack)
380
366
  , N = $.layout.cssNum
381
367
  , off = $E.offset()
368
+ , b, p, ei // TEMP border, padding
382
369
  ;
383
370
  d.offsetLeft = off.left;
384
371
  d.offsetTop = off.top;
385
372
 
373
+ if (!inset) inset = {}; // simplify logic below
374
+
386
375
  $.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge
387
376
  b = x["border" + e] = $.layout.borderWidth($E, e);
388
377
  p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e);
389
- i[e] = b + p; // total offset of content from outer side
390
- d["inset"+ e] = p;
378
+ ei = e.toLowerCase();
379
+ d.inset[ei] = inset[ei] >= 0 ? inset[ei] : p; // any missing insetX value = paddingX
380
+ i[ei] = d.inset[ei] + b; // total offset of content from outer side
391
381
  });
392
382
 
393
- d.offsetWidth = $E.innerWidth(); // offsetWidth is used in calc when doing manual resize
394
- d.offsetHeight = $E.innerHeight(); // ditto
395
- d.outerWidth = $E.outerWidth();
396
- d.outerHeight = $E.outerHeight();
397
- d.innerWidth = max(0, d.outerWidth - i.Left - i.Right);
398
- d.innerHeight = max(0, d.outerHeight - i.Top - i.Bottom);
399
-
400
383
  x.width = $E.width();
401
384
  x.height = $E.height();
402
385
  x.top = N($E,"top",true);
@@ -404,12 +387,24 @@ $.layout = {
404
387
  x.left = N($E,"left",true);
405
388
  x.right = N($E,"right",true);
406
389
 
390
+ d.outerWidth = $E.outerWidth();
391
+ d.outerHeight = $E.outerHeight();
392
+ // calc the TRUE inner-dimensions, even in quirks-mode!
393
+ d.innerWidth = max(0, d.outerWidth - i.left - i.right);
394
+ d.innerHeight = max(0, d.outerHeight - i.top - i.bottom);
395
+ // layoutWidth/Height is used in calcs for manual resizing
396
+ // layoutW/H only differs from innerW/H when in quirks-mode - then is like outerW/H
397
+ d.layoutWidth = $E.innerWidth();
398
+ d.layoutHeight = $E.innerHeight();
399
+
400
+ //if ($E.prop('tagName') === 'BODY') { debugData( d, $E.prop('tagName') ); } // DEBUG
401
+
407
402
  //d.visible = $E.is(":visible");// && x.width > 0 && x.height > 0;
408
403
 
409
404
  return d;
410
405
  }
411
406
 
412
- , getElementCSS: function ($E, list) {
407
+ , getElementStyles: function ($E, list) {
413
408
  var
414
409
  CSS = {}
415
410
  , style = $E[0].style
@@ -446,23 +441,19 @@ $.layout = {
446
441
  * @return {number} Returns the innerWidth of the elem by subtracting padding and borders
447
442
  */
448
443
  , cssWidth: function ($E, outerWidth) {
449
- var
450
- b = $.layout.borderWidth
451
- , n = $.layout.cssNum
452
- ;
453
444
  // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
454
445
  if (outerWidth <= 0) return 0;
455
446
 
456
- if (!$.support.boxModel) return outerWidth;
457
-
458
- // strip border and padding from outerWidth to get CSS Width
459
- var W = outerWidth
460
- - b($E, "Left")
461
- - b($E, "Right")
462
- - n($E, "paddingLeft")
463
- - n($E, "paddingRight")
447
+ var bs = !$.layout.browser.boxModel ? "border-box" : $.support.boxSizing ? $E.css("boxSizing") : "content-box"
448
+ , b = $.layout.borderWidth
449
+ , n = $.layout.cssNum
450
+ , W = outerWidth
464
451
  ;
465
-
452
+ // strip border and/or padding from outerWidth to get CSS Width
453
+ if (bs !== "border-box")
454
+ W -= (b($E, "Left") + b($E, "Right"));
455
+ if (bs === "content-box")
456
+ W -= (n($E, "paddingLeft") + n($E, "paddingRight"));
466
457
  return max(0,W);
467
458
  }
468
459
 
@@ -475,23 +466,19 @@ $.layout = {
475
466
  * @return {number} Returns the innerHeight of the elem by subtracting padding and borders
476
467
  */
477
468
  , cssHeight: function ($E, outerHeight) {
478
- var
479
- b = $.layout.borderWidth
480
- , n = $.layout.cssNum
481
- ;
482
469
  // a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
483
470
  if (outerHeight <= 0) return 0;
484
471
 
485
- if (!$.support.boxModel) return outerHeight;
486
-
487
- // strip border and padding from outerHeight to get CSS Height
488
- var H = outerHeight
489
- - b($E, "Top")
490
- - b($E, "Bottom")
491
- - n($E, "paddingTop")
492
- - n($E, "paddingBottom")
472
+ var bs = !$.layout.browser.boxModel ? "border-box" : $.support.boxSizing ? $E.css("boxSizing") : "content-box"
473
+ , b = $.layout.borderWidth
474
+ , n = $.layout.cssNum
475
+ , H = outerHeight
493
476
  ;
494
-
477
+ // strip border and/or padding from outerHeight to get CSS Height
478
+ if (bs !== "border-box")
479
+ H -= (b($E, "Top") + b($E, "Bottom"));
480
+ if (bs === "content-box")
481
+ H -= (n($E, "paddingTop") + n($E, "paddingBottom"));
495
482
  return max(0,H);
496
483
  }
497
484
 
@@ -507,8 +494,8 @@ $.layout = {
507
494
  , cssNum: function ($E, prop, allowAuto) {
508
495
  if (!$E.jquery) $E = $($E);
509
496
  var CSS = $.layout.showInvisibly($E)
510
- , p = $.curCSS($E[0], prop, true)
511
- , v = allowAuto && p=="auto" ? p : (parseInt(p, 10) || 0);
497
+ , p = $.css($E[0], prop, true)
498
+ , v = allowAuto && p=="auto" ? p : Math.round(parseFloat(p) || 0);
512
499
  $E.css( CSS ); // RESET
513
500
  return v;
514
501
  }
@@ -516,7 +503,7 @@ $.layout = {
516
503
  , borderWidth: function (el, side) {
517
504
  if (el.jquery) el = el[0];
518
505
  var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
519
- return $.curCSS(el, b+"Style", true) === "none" ? 0 : (parseInt($.curCSS(el, b+"Width", true), 10) || 0);
506
+ return $.css(el, b+"Style", true) === "none" ? 0 : Math.round(parseFloat($.css(el, b+"Width", true)) || 0);
520
507
  }
521
508
 
522
509
  /**
@@ -567,7 +554,7 @@ $.layout = {
567
554
  * @param {(Object|string)} info String message OR Hash/Array
568
555
  * @param {(Boolean|string|Object)=} [popup=false] True means alert-box - can be skipped
569
556
  * @param {(Object|string)=} [debugTitle=""] Title for Hash data - can be skipped
570
- * @param {Object=} [debutOpts={}] Extra options for debug output
557
+ * @param {Object=} [debugOpts] Extra options for debug output
571
558
  */
572
559
  , msg: function (info, popup, debugTitle, debugOpts) {
573
560
  if ($.isPlainObject(info) && window.debugData) {
@@ -614,28 +601,57 @@ $.layout = {
614
601
 
615
602
  };
616
603
 
617
- var lang = $.layout.language; // alias used in defaults...
618
604
 
619
- // DEFAULT OPTIONS - CHANGE IF DESIRED
605
+ /*
606
+ * $.layout.browser REPLACES removed $.browser, with extra data
607
+ * Parsing code here adapted from jQuery 1.8 $.browse
608
+ */
609
+ var u = navigator.userAgent.toLowerCase()
610
+ , m = /(chrome)[ \/]([\w.]+)/.exec( u )
611
+ || /(webkit)[ \/]([\w.]+)/.exec( u )
612
+ || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( u )
613
+ || /(msie) ([\w.]+)/.exec( u )
614
+ || u.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( u )
615
+ || []
616
+ , b = m[1] || ""
617
+ , v = m[2] || 0
618
+ , ie = b === "msie"
619
+ ;
620
+ $.layout.browser = {
621
+ version: v
622
+ , safari: b === "webkit" // webkit (NOT chrome) = safari
623
+ , webkit: b === "chrome" // chrome = webkit
624
+ , msie: ie
625
+ , isIE6: ie && v == 6
626
+ // ONLY IE reverts to old box-model - update for older jQ onReady
627
+ , boxModel: !ie || $.support.boxModel !== false
628
+ };
629
+ if (b) $.layout.browser[b] = true; // set CURRENT browser
630
+ /* OLD versions of jQuery only set $.support.boxModel after page is loaded
631
+ * so if this is IE, use support.boxModel to test for quirks-mode (ONLY IE changes boxModel) */
632
+ if (ie) $(function(){ $.layout.browser.boxModel = $.support.boxModel; });
633
+
634
+
635
+ // DEFAULT OPTIONS
620
636
  $.layout.defaults = {
621
637
  /*
622
638
  * LAYOUT & LAYOUT-CONTAINER OPTIONS
623
639
  * - none of these options are applicable to individual panes
624
640
  */
625
641
  name: "" // Not required, but useful for buttons and used for the state-cookie
626
- , containerSelector: "" // ONLY used when specifying a childOptions - to find container-element that is NOT directly-nested
627
642
  , containerClass: "ui-layout-container" // layout-container element
643
+ , inset: null // custom container-inset values (override padding)
628
644
  , scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
629
645
  , resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event
630
646
  , resizeWithWindowDelay: 200 // delay calling resizeAll because makes window resizing very jerky
631
647
  , resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized
648
+ , maskPanesEarly: false // true = create pane-masks on resizer.mouseDown instead of waiting for resizer.dragstart
632
649
  , onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific
633
650
  , onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific
634
651
  , onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements
635
652
  , onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized
636
653
  , onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload
637
654
  , onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload
638
- , autoBindCustomButtons: false // search for buttons with ui-layout-button class and auto-bind them
639
655
  , initPanes: true // false = DO NOT initialize the panes onLoad - will init later
640
656
  , showErrorMessages: true // enables fatal error messages to warn developers of common errors
641
657
  , showDebugMessages: false // display console-and-alert debug msgs - IF this Layout version _has_ debugging code!
@@ -650,6 +666,15 @@ $.layout.defaults = {
650
666
  , pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer
651
667
  , resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged'
652
668
  }
669
+ , errors: {
670
+ pane: "pane" // description of "layout pane element" - used only in error messages
671
+ , selector: "selector" // description of "jQuery-selector" - used only in error messages
672
+ , addButtonError: "Error Adding Button\nInvalid "
673
+ , containerMissing: "UI Layout Initialization Error\nThe specified layout-container does not exist."
674
+ , centerPaneMissing: "UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element."
675
+ , noContainerHeight: "UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!"
676
+ , callbackError: "UI Layout Callback Error\nThe EVENT callback is not a valid function."
677
+ }
653
678
  /*
654
679
  * PANE DEFAULT SETTINGS
655
680
  * - settings under the 'panes' key become the default settings for *all panes*
@@ -682,8 +707,6 @@ $.layout.defaults = {
682
707
  , togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
683
708
  , togglerAlign_open: "center" // top/left, bottom/right, center, OR...
684
709
  , togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
685
- , togglerTip_open: lang.Close // Toggler tool-tip (title)
686
- , togglerTip_closed: lang.Open // ditto
687
710
  , togglerContent_open: "" // text or HTML to put INSIDE the toggler
688
711
  , togglerContent_closed: "" // ditto
689
712
  // RESIZING OPTIONS
@@ -699,10 +722,7 @@ $.layout.defaults = {
699
722
  , livePaneResizing: false // true = LIVE Resizing as resizer is dragged
700
723
  , liveContentResizing: false // true = re-measure header/footer heights as resizer is dragged
701
724
  , liveResizingTolerance: 1 // how many px change before pane resizes, to control performance
702
- // TIPS & MESSAGES - also see lang object
703
- , noRoomToOpenTip: lang.noRoomToOpenTip
704
- , resizerTip: lang.Resize // Resizer tool-tip (title)
705
- , sliderTip: lang.Slide // resizer-bar triggers 'sliding' when pane is closed
725
+ // SLIDING OPTIONS
706
726
  , sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
707
727
  , slideTrigger_open: "click" // click, dblclick, mouseenter
708
728
  , slideTrigger_close: "mouseleave"// click, mouseleave
@@ -711,6 +731,18 @@ $.layout.defaults = {
711
731
  , hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
712
732
  , preventQuickSlideClose: $.layout.browser.webkit // Chrome triggers slideClosed as it is opening
713
733
  , preventPrematureSlideClose: false // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
734
+ // PANE-SPECIFIC TIPS & MESSAGES
735
+ , tips: {
736
+ Open: "Open" // eg: "Open Pane"
737
+ , Close: "Close"
738
+ , Resize: "Resize"
739
+ , Slide: "Slide Open"
740
+ , Pin: "Pin"
741
+ , Unpin: "Un-Pin"
742
+ , noRoomToOpen: "Not enough room to show this panel." // alert if user tries to open a pane that cannot
743
+ , minSizeWarning: "Panel has reached its minimum size" // displays in browser statusbar
744
+ , maxSizeWarning: "Panel has reached its maximum size" // ditto
745
+ }
714
746
  // HOT-KEYS & MISC
715
747
  , showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
716
748
  , enableCursorHotkey: true // enabled 'cursor' hotkeys
@@ -723,7 +755,7 @@ $.layout.defaults = {
723
755
  , fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
724
756
  , fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation
725
757
  , animatePaneSizing: false // true = animate resizing after dragging resizer-bar OR sizePane() is called
726
- /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set:
758
+ /* NOTE: Action-specific FX options are auto-generated from the options above if not specifically set:
727
759
  fxName_open: "slide" // 'Open' pane animation
728
760
  fnName_close: "slide" // 'Close' pane animation
729
761
  fxName_size: "slide" // 'Size' pane animation - when animatePaneSizing = true
@@ -735,13 +767,15 @@ $.layout.defaults = {
735
767
  fxSettings_size: {}
736
768
  */
737
769
  // CHILD/NESTED LAYOUTS
738
- , childOptions: null // Layout-options for nested/child layout - even {} is valid as options
739
- , initChildLayout: true // true = child layout will be created as soon as _this_ layout completes initialization
740
- , destroyChildLayout: true // true = destroy child-layout if this pane is destroyed
741
- , resizeChildLayout: true // true = trigger child-layout.resizeAll() when this pane is resized
742
- // PANE CALLBACKS
770
+ , children: null // Layout-options for nested/child layout - even {} is valid as options
771
+ , containerSelector: '' // if child is NOT 'directly nested', a selector to find it/them (can have more than one child layout!)
772
+ , initChildren: true // true = child layout will be created as soon as _this_ layout completes initialization
773
+ , destroyChildren: true // true = destroy child-layout if this pane is destroyed
774
+ , resizeChildren: true // true = trigger child-layout.resizeAll() when this pane is resized
775
+ // EVENT TRIGGERING
743
776
  , triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes
744
777
  , triggerEventsDuringLiveResize: true // true = trigger onresize callback REPEATEDLY if livePaneResizing==true
778
+ // PANE CALLBACKS
745
779
  , onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
746
780
  , onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
747
781
  , onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start
@@ -798,15 +832,15 @@ $.layout.defaults = {
798
832
 
799
833
  $.layout.optionsMap = {
800
834
  // layout/global options - NOT pane-options
801
- layout: ("stateManagement,effects,zIndexes,"
802
- + "name,zIndex,scrollToBookmarkOnLoad,showErrorMessages,"
803
- + "resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"
804
- + "onresizeall,onresizeall_start,onresizeall_end,onload,onunload,autoBindCustomButtons").split(",")
835
+ layout: ("name,instanceKey,stateManagement,effects,inset,zIndexes,errors,"
836
+ + "zIndex,scrollToBookmarkOnLoad,showErrorMessages,maskPanesEarly,"
837
+ + "outset,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"
838
+ + "onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end").split(",")
805
839
  // borderPanes: [ ALL options that are NOT specified as 'layout' ]
806
840
  // default.panes options that apply to the center-pane (most options apply _only_ to border-panes)
807
841
  , center: ("paneClass,contentSelector,contentIgnoreSelector,findNestedContent,applyDemoStyles,triggerEventsOnLoad,"
808
842
  + "showOverflowOnHover,maskContents,maskObjects,liveContentResizing,"
809
- + "childOptions,initChildLayout,resizeChildLayout,destroyChildLayout,"
843
+ + "containerSelector,children,initChildren,resizeChildren,destroyChildren,"
810
844
  + "onresize,onresize_start,onresize_end,onsizecontent,onsizecontent_start,onsizecontent_end").split(",")
811
845
  // options that MUST be specifically set 'per-pane' - CANNOT set in the panes (defaults) key
812
846
  , noDefault: ("paneSelector,resizerCursor,customHotkey").split(",")
@@ -817,36 +851,41 @@ $.layout.optionsMap = {
817
851
  * In flat-format, subkeys are _currently_ separated with 2 underscores, like north__optName
818
852
  * Plugins may also call this method so they can transform their own data
819
853
  *
820
- * @param {!Object} hash Data/options passed by user - may be a single level or nested levels
821
- * @return {Object} Returns hash of minWidth & minHeight
854
+ * @param {!Object} hash Data/options passed by user - may be a single level or nested levels
855
+ * @param {boolean=} [addKeys=false] Should the primary layout.options keys be added if they do not exist?
856
+ * @return {Object} Returns hash of minWidth & minHeight
822
857
  */
823
- $.layout.transformData = function (hash) {
824
- var json = { panes: {}, center: {} } // init return object
825
- , data, branch, optKey, keys, key, val, i, c;
858
+ $.layout.transformData = function (hash, addKeys) {
859
+ var json = addKeys ? { panes: {}, center: {} } : {} // init return object
860
+ , branch, optKey, keys, key, val, i, c;
826
861
 
827
862
  if (typeof hash !== "object") return json; // no options passed
828
863
 
829
864
  // convert all 'flat-keys' to 'sub-key' format
830
865
  for (optKey in hash) {
831
866
  branch = json;
832
- data = $.layout.optionsMap.layout;
833
867
  val = hash[ optKey ];
834
868
  keys = optKey.split("__"); // eg: west__size or north__fxSettings__duration
835
869
  c = keys.length - 1;
836
870
  // convert underscore-delimited to subkeys
837
871
  for (i=0; i <= c; i++) {
838
872
  key = keys[i];
839
- if (i === c)
840
- branch[key] = val;
841
- else if (!branch[key])
842
- branch[key] = {}; // create the subkey
843
- // recurse to sub-key for next loop - if not done
844
- branch = branch[key];
873
+ if (i === c) { // last key = value
874
+ if ($.isPlainObject( val ))
875
+ branch[key] = $.layout.transformData( val ); // RECURSE
876
+ else
877
+ branch[key] = val;
878
+ }
879
+ else {
880
+ if (!branch[key])
881
+ branch[key] = {}; // create the subkey
882
+ // recurse to sub-key for next loop - if not done
883
+ branch = branch[key];
884
+ }
845
885
  }
846
886
  }
847
-
848
887
  return json;
849
- }
888
+ };
850
889
 
851
890
  // INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
852
891
  $.layout.backwardCompatibility = {
@@ -854,11 +893,18 @@ $.layout.backwardCompatibility = {
854
893
  map: {
855
894
  // OLD Option Name: NEW Option Name
856
895
  applyDefaultStyles: "applyDemoStyles"
857
- , resizeNestedLayout: "resizeChildLayout"
896
+ // CHILD/NESTED LAYOUTS
897
+ , childOptions: "children"
898
+ , initChildLayout: "initChildren"
899
+ , destroyChildLayout: "destroyChildren"
900
+ , resizeChildLayout: "resizeChildren"
901
+ , resizeNestedLayout: "resizeChildren"
902
+ // MISC Options
858
903
  , resizeWhileDragging: "livePaneResizing"
859
904
  , resizeContentWhileDragging: "liveContentResizing"
860
905
  , triggerEventsWhileDragging: "triggerEventsDuringLiveResize"
861
906
  , maskIframesOnResize: "maskContents"
907
+ // STATE MANAGEMENT
862
908
  , useStateCookie: "stateManagement.enabled"
863
909
  , "cookie.autoLoad": "stateManagement.autoLoad"
864
910
  , "cookie.autoSave": "stateManagement.autoSave"
@@ -868,19 +914,26 @@ $.layout.backwardCompatibility = {
868
914
  , "cookie.path": "stateManagement.cookie.path"
869
915
  , "cookie.expires": "stateManagement.cookie.expires"
870
916
  , "cookie.secure": "stateManagement.cookie.secure"
917
+ // OLD Language options
918
+ , noRoomToOpenTip: "tips.noRoomToOpen"
919
+ , togglerTip_open: "tips.Close" // open = Close
920
+ , togglerTip_closed: "tips.Open" // closed = Open
921
+ , resizerTip: "tips.Resize"
922
+ , sliderTip: "tips.Slide"
871
923
  }
872
- /**
873
- * @param {Object} opts
874
- */
924
+
925
+ /**
926
+ * @param {Object} opts
927
+ */
875
928
  , renameOptions: function (opts) {
876
929
  var map = $.layout.backwardCompatibility.map
877
930
  , oldData, newData, value
878
931
  ;
879
932
  for (var itemPath in map) {
880
933
  oldData = getBranch( itemPath );
881
- value = oldData.branch[ oldData.key ]
934
+ value = oldData.branch[ oldData.key ];
882
935
  if (value !== undefined) {
883
- newData = getBranch( map[itemPath], true )
936
+ newData = getBranch( map[itemPath], true );
884
937
  newData.branch[ newData.key ] = value;
885
938
  delete oldData.branch[ oldData.key ];
886
939
  }
@@ -910,9 +963,10 @@ $.layout.backwardCompatibility = {
910
963
  return D;
911
964
  };
912
965
  }
913
- /**
914
- * @param {Object} opts
915
- */
966
+
967
+ /**
968
+ * @param {Object} opts
969
+ */
916
970
  , renameAllOptions: function (opts) {
917
971
  var ren = $.layout.backwardCompatibility.renameOptions;
918
972
  // rename root (layout) options
@@ -936,6 +990,7 @@ $.layout.backwardCompatibility = {
936
990
 
937
991
 
938
992
 
993
+
939
994
  /* ============================================================
940
995
  * BEGIN WIDGET: $( selector ).layout( {options} );
941
996
  * ============================================================
@@ -945,14 +1000,13 @@ $.fn.layout = function (opts) {
945
1000
 
946
1001
  // local aliases to global data
947
1002
  browser = $.layout.browser
948
- , lang = $.layout.language // internal alias
949
1003
  , _c = $.layout.config
950
1004
 
951
1005
  // local aliases to utlity methods
952
1006
  , cssW = $.layout.cssWidth
953
1007
  , cssH = $.layout.cssHeight
954
1008
  , elDims = $.layout.getElementDimensions
955
- , elCSS = $.layout.getElementCSS
1009
+ , styles = $.layout.getElementStyles
956
1010
  , evtObj = $.layout.getEventObject
957
1011
  , evtPane = $.layout.parsePaneName
958
1012
 
@@ -967,14 +1021,23 @@ $.fn.layout = function (opts) {
967
1021
  */
968
1022
  , state = {
969
1023
  // generate unique ID to use for event.namespace so can unbind only events added by 'this layout'
970
- id: "layout"+ $.now() // code uses alias: sID
971
- , initialized: false
972
- , container: {} // init all keys
973
- , north: {}
974
- , south: {}
975
- , east: {}
976
- , west: {}
977
- , center: {}
1024
+ id: "layout"+ $.now() // code uses alias: sID
1025
+ , initialized: false
1026
+ , paneResizing: false
1027
+ , panesSliding: {}
1028
+ , container: { // list all keys referenced in code to avoid compiler error msgs
1029
+ innerWidth: 0
1030
+ , innerHeight: 0
1031
+ , outerWidth: 0
1032
+ , outerHeight: 0
1033
+ , layoutWidth: 0
1034
+ , layoutHeight: 0
1035
+ }
1036
+ , north: { childIdx: 0 }
1037
+ , south: { childIdx: 0 }
1038
+ , east: { childIdx: 0 }
1039
+ , west: { childIdx: 0 }
1040
+ , center: { childIdx: 0 }
978
1041
  }
979
1042
 
980
1043
  /**
@@ -1004,32 +1067,48 @@ $.fn.layout = function (opts) {
1004
1067
  , clear: function (s) { var t=timer.data; if (t[s]) {clearTimeout(t[s]); delete t[s];} }
1005
1068
  }
1006
1069
 
1007
- , _log = function (msg, popup) {
1008
- $.layout.msg( options.name +' / '+ msg, (popup && options.showErrorMessages) );
1070
+ /**
1071
+ * Alert or console.log a message - IF option is enabled.
1072
+ *
1073
+ * @param {(string|!Object)} msg Message (or debug-data) to display
1074
+ * @param {boolean=} [popup=false] True by default, means 'alert', false means use console.log
1075
+ * @param {boolean=} [debug=false] True means is a widget debugging message
1076
+ */
1077
+ , _log = function (msg, popup, debug) {
1078
+ var o = options;
1079
+ if ((o.showErrorMessages && !debug) || (debug && o.showDebugMessages))
1080
+ $.layout.msg( o.name +' / '+ msg, (popup !== false) );
1081
+ return false;
1009
1082
  }
1010
1083
 
1011
1084
  /**
1012
1085
  * Executes a Callback function after a trigger event, like resize, open or close
1013
1086
  *
1014
- * @param {string} evtName Name of the layout callback, eg "onresize_start"
1015
- * @param {?string} pane This is passed only so we can pass the 'pane object' to the callback
1016
- * @param {?boolean} skipBoundEvents Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
1087
+ * @param {string} evtName Name of the layout callback, eg "onresize_start"
1088
+ * @param {(string|boolean)=} [pane=""] This is passed only so we can pass the 'pane object' to the callback
1089
+ * @param {(string|boolean)=} [skipBoundEvents=false] True = do not run events bound to the elements - only the callbacks set in options
1017
1090
  */
1018
1091
  , _runCallbacks = function (evtName, pane, skipBoundEvents) {
1019
- var o = pane ? options[pane] : options
1092
+ var hasPane = pane && isStr(pane)
1093
+ , s = hasPane ? state[pane] : state
1094
+ , o = hasPane ? options[pane] : options
1095
+ , lName = options.name
1020
1096
  // names like onopen and onopen_end separate are interchangeable in options...
1021
- , lng = evtName + (evtName.match(/_/) ? "" : "_end")
1097
+ , lng = evtName + (evtName.match(/_/) ? "" : "_end")
1022
1098
  , shrt = lng.match(/_end$/) ? lng.substr(0, lng.length - 4) : ""
1023
- , fn = o[lng]
1099
+ , fn = o[lng] || o[shrt]
1024
1100
  , retVal = "NC" // NC = No Callback
1025
1101
  , args = []
1102
+ , $P
1026
1103
  ;
1027
- if (!fn && shrt)
1028
- fn = o[shrt];
1104
+ if ( !hasPane && $.type(pane) === 'boolean' ) {
1105
+ skipBoundEvents = pane; // allow pane param to be skipped for Layout callback
1106
+ pane = "";
1107
+ }
1029
1108
 
1030
1109
  // first trigger the callback set in the options
1031
1110
  if (fn) {
1032
- //try {
1111
+ try {
1033
1112
  // convert function name (string) to function object
1034
1113
  if (isStr( fn )) {
1035
1114
  if (fn.match(/,/)) {
@@ -1044,33 +1123,46 @@ $.fn.layout = function (opts) {
1044
1123
  // execute the callback, if exists
1045
1124
  if ($.isFunction( fn )) {
1046
1125
  if (args.length)
1047
- retVal = fn(args[1]); // pass the argument parsed from 'list'
1048
- else if (pane && $Ps[pane])
1126
+ retVal = g(fn)(args[1]); // pass the argument parsed from 'list'
1127
+ else if ( hasPane )
1049
1128
  // pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
1050
- retVal = fn( pane, $Ps[pane], state[pane], options[pane], options.name );
1129
+ retVal = g(fn)( pane, $Ps[pane], s, o, lName );
1051
1130
  else // must be a layout/container callback - pass suitable info
1052
- retVal = fn( Instance, state, options, options.name );
1131
+ retVal = g(fn)( Instance, s, o, lName );
1053
1132
  }
1054
- //}
1055
- //catch (ex) {}
1133
+ }
1134
+ catch (ex) {
1135
+ _log( options.errors.callbackError.replace(/EVENT/, $.trim((pane || "") +" "+ lng)), false );
1136
+ if ($.type(ex) === 'string' && string.length)
1137
+ _log('Exception: '+ ex, false );
1138
+ }
1056
1139
  }
1057
1140
 
1058
1141
  // trigger additional events bound directly to the pane
1059
1142
  if (!skipBoundEvents && retVal !== false) {
1060
- if (pane) { // PANE events can be bound to each pane-elements
1061
- $Ps[pane].triggerHandler('layoutpane'+ lng, [ pane, $Ps[pane], state[pane], options[pane], options.name ]);
1143
+ if ( hasPane ) { // PANE events can be bound to each pane-elements
1144
+ $P = $Ps[pane];
1145
+ o = options[pane];
1146
+ s = state[pane];
1147
+ $P.triggerHandler('layoutpane'+ lng, [ pane, $P, s, o, lName ]);
1148
+ if (shrt)
1149
+ $P.triggerHandler('layoutpane'+ shrt, [ pane, $P, s, o, lName ]);
1150
+ }
1151
+ else { // LAYOUT events can be bound to the container-element
1152
+ $N.triggerHandler('layout'+ lng, [ Instance, s, o, lName ]);
1062
1153
  if (shrt)
1063
- $Ps[pane].triggerHandler('layoutpane'+ shrt, [ pane, $Ps[pane], state[pane], options[pane], options.name ]);
1154
+ $N.triggerHandler('layout'+ shrt, [ Instance, s, o, lName ]);
1064
1155
  }
1065
- else // LAYOUT events can be bound to the container-element
1066
- $N.triggerHandler('layout'+ lng, [ pane, $Ps[pane], state[pane], options[pane], options.name ]);
1067
1156
  }
1068
1157
 
1069
- // ALWAYS resizeChildLayout after a resize event - even during initialization
1070
- if (evtName === "onresize_end" || evtName === "onsizecontent_end")
1071
- resizeChildLayout(pane);
1158
+ // ALWAYS resizeChildren after an onresize_end event - even during initialization
1159
+ // IGNORE onsizecontent_end event because causes child-layouts to resize TWICE
1160
+ if (hasPane && evtName === "onresize_end") // BAD: || evtName === "onsizecontent_end"
1161
+ resizeChildren(pane+"", true); // compiler hack -force string
1072
1162
 
1073
1163
  return retVal;
1164
+
1165
+ function g (f) { return f; }; // compiler hack
1074
1166
  }
1075
1167
 
1076
1168
 
@@ -1163,18 +1255,6 @@ $.fn.layout = function (opts) {
1163
1255
  $E.hide().data('autoHidden', true);
1164
1256
  }
1165
1257
 
1166
- /**
1167
- * @param {(string|!Object)} el
1168
- * @param {number=} outerSize
1169
- * @param {boolean=} [autoHide=false]
1170
- */
1171
- , setOuterSize = function (el, outerSize, autoHide) {
1172
- if (_c[pane].dir=="horz") // pane = north or south
1173
- setOuterHeight(el, outerSize, autoHide);
1174
- else // pane = east or west
1175
- setOuterWidth(el, outerSize, autoHide);
1176
- }
1177
-
1178
1258
 
1179
1259
  /**
1180
1260
  * Converts any 'size' params to a pixel/integer size, if not already
@@ -1232,7 +1312,7 @@ $.fn.layout = function (opts) {
1232
1312
  *
1233
1313
  * @param {(string|!Object)} pane
1234
1314
  * @param {boolean=} [inclSpace=false]
1235
- * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
1315
+ * @return {number} Returns EITHER Width for east/west panes OR Height for north/south panes
1236
1316
  */
1237
1317
  , getPaneSize = function (pane, inclSpace) {
1238
1318
  var
@@ -1265,7 +1345,6 @@ $.fn.layout = function (opts) {
1265
1345
  , s = state[pane]
1266
1346
  , c = _c[pane]
1267
1347
  , dir = c.dir
1268
- , side = c.side.toLowerCase()
1269
1348
  , type = c.sizeType.toLowerCase()
1270
1349
  , isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param
1271
1350
  , $P = $Ps[pane]
@@ -1285,8 +1364,8 @@ $.fn.layout = function (opts) {
1285
1364
  , minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize )
1286
1365
  , maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize )
1287
1366
  , r = s.resizerPosition = {} // used to set resizing limits
1288
- , top = sC.insetTop
1289
- , left = sC.insetLeft
1367
+ , top = sC.inset.top
1368
+ , left = sC.inset.left
1290
1369
  , W = sC.innerWidth
1291
1370
  , H = sC.innerHeight
1292
1371
  , rW = o.spacing_open // subtract resizer-width to get top/left position for south/east
@@ -1327,10 +1406,10 @@ $.fn.layout = function (opts) {
1327
1406
  d.width = sC.innerWidth - d.left - d.right; // outerWidth
1328
1407
  d.height = sC.innerHeight - d.bottom - d.top; // outerHeight
1329
1408
  // add the 'container border/padding' to get final positions relative to the container
1330
- d.top += sC.insetTop;
1331
- d.bottom += sC.insetBottom;
1332
- d.left += sC.insetLeft;
1333
- d.right += sC.insetRight;
1409
+ d.top += sC.inset.top;
1410
+ d.bottom += sC.inset.bottom;
1411
+ d.left += sC.inset.left;
1412
+ d.right += sC.inset.right;
1334
1413
 
1335
1414
  return d;
1336
1415
  }
@@ -1376,14 +1455,22 @@ $.fn.layout = function (opts) {
1376
1455
  }
1377
1456
 
1378
1457
  , onResizerEnter = function (evt) { // ALSO called by toggler.mouseenter
1458
+ var pane = $(this).data("layoutEdge")
1459
+ , s = state[pane]
1460
+ ;
1461
+ // ignore closed-panes and mouse moving back & forth over resizer!
1462
+ // also ignore if ANY pane is currently resizing
1463
+ if ( s.isClosed || s.isResizing || state.paneResizing ) return;
1464
+
1379
1465
  if ($.fn.disableSelection)
1380
1466
  $("body").disableSelection();
1467
+ if (options.maskPanesEarly)
1468
+ showMasks( pane, { resizing: true });
1381
1469
  }
1382
1470
  , onResizerLeave = function (evt, el) {
1383
- var
1384
- e = el || this // el is only passed when called by the timer
1385
- , pane = $(e).data("layoutEdge")
1386
- , name = pane +"ResizerLeave"
1471
+ var e = el || this // el is only passed when called by the timer
1472
+ , pane = $(e).data("layoutEdge")
1473
+ , name = pane +"ResizerLeave"
1387
1474
  ;
1388
1475
  timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set
1389
1476
  timer.clear(name); // cancel enableSelection timer - may re/set below
@@ -1393,8 +1480,12 @@ $.fn.layout = function (opts) {
1393
1480
  if (!el) // 1st call - mouseleave event
1394
1481
  timer.set(name, function(){ onResizerLeave(evt, e); }, 200);
1395
1482
  // if user is resizing, then dragStop will enableSelection(), so can skip it here
1396
- else if (!state[pane].isResizing && $.fn.enableSelection) // 2nd call - by timer
1397
- $("body").enableSelection();
1483
+ else if ( !state.paneResizing ) { // 2nd call - by timer
1484
+ if ($.fn.enableSelection)
1485
+ $("body").enableSelection();
1486
+ if (options.maskPanesEarly)
1487
+ hideMasks();
1488
+ }
1398
1489
  }
1399
1490
 
1400
1491
  /*
@@ -1412,10 +1503,11 @@ $.fn.layout = function (opts) {
1412
1503
  , _create = function () {
1413
1504
  // initialize config/options
1414
1505
  initOptions();
1415
- var o = options;
1506
+ var o = options
1507
+ , s = state;
1416
1508
 
1417
1509
  // TEMP state so isInitialized returns true during init process
1418
- state.creatingLayout = true;
1510
+ s.creatingLayout = true;
1419
1511
 
1420
1512
  // init plugins for this layout, if there are any (eg: stateManagement)
1421
1513
  runPluginCallbacks( Instance, $.layout.onCreate );
@@ -1441,7 +1533,7 @@ $.fn.layout = function (opts) {
1441
1533
  // initLayoutElements will set initialized=true and run the onload callback IF successful
1442
1534
  if (o.initPanes) _initLayoutElements();
1443
1535
 
1444
- delete state.creatingLayout;
1536
+ delete s.creatingLayout;
1445
1537
 
1446
1538
  return state.initialized;
1447
1539
  }
@@ -1461,12 +1553,12 @@ $.fn.layout = function (opts) {
1461
1553
  * Initialize the layout - called automatically whenever an instance of layout is created
1462
1554
  *
1463
1555
  * @see _create() & isInitialized
1556
+ * @param {boolean=} [retry=false] // indicates this is a 2nd try
1464
1557
  * @return An object pointer to the instance created
1465
1558
  */
1466
1559
  , _initLayoutElements = function (retry) {
1467
1560
  // initialize config/options
1468
1561
  var o = options;
1469
-
1470
1562
  // CANNOT init panes inside a hidden container!
1471
1563
  if (!$N.is(":visible")) {
1472
1564
  // handle Chrome bug where popup window 'has no height'
@@ -1479,16 +1571,14 @@ $.fn.layout = function (opts) {
1479
1571
 
1480
1572
  // a center pane is required, so make sure it exists
1481
1573
  if (!getPane("center").length) {
1482
- if (options.showErrorMessages)
1483
- _log( lang.errCenterPaneMissing, true );
1484
- return false;
1574
+ return _log( o.errors.centerPaneMissing );
1485
1575
  }
1486
1576
 
1487
1577
  // TEMP state so isInitialized returns true during init process
1488
1578
  state.creatingLayout = true;
1489
1579
 
1490
1580
  // update Container dims
1491
- $.extend(sC, elDims( $N ));
1581
+ $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values
1492
1582
 
1493
1583
  // initialize all layout elements
1494
1584
  initPanes(); // size & position panes - calls initHandles() - which calls initResizable()
@@ -1518,62 +1608,148 @@ $.fn.layout = function (opts) {
1518
1608
  }
1519
1609
 
1520
1610
  /**
1521
- * Initialize nested layouts - called when _initLayoutElements completes
1522
- *
1523
- * NOT CURRENTLY USED
1611
+ * Initialize nested layouts for a specific pane - can optionally pass layout-options
1524
1612
  *
1525
- * @see _initLayoutElements
1526
- * @return An object pointer to the instance created
1613
+ * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
1614
+ * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].children
1615
+ * @return An object pointer to the layout instance created - or null
1527
1616
  */
1528
- , _initChildLayouts = function () {
1529
- $.each(_c.allPanes, function (idx, pane) {
1530
- if (options[pane].initChildLayout)
1531
- createChildLayout( pane );
1617
+ , createChildren = function (evt_or_pane, opts) {
1618
+ var pane = evtPane.call(this, evt_or_pane)
1619
+ , $P = $Ps[pane]
1620
+ ;
1621
+ if (!$P) return;
1622
+ var $C = $Cs[pane]
1623
+ , s = state[pane]
1624
+ , o = options[pane]
1625
+ , sm = options.stateManagement || {}
1626
+ , cos = opts ? (o.children = opts) : o.children
1627
+ ;
1628
+ if ( $.isPlainObject( cos ) )
1629
+ cos = [ cos ]; // convert a hash to a 1-elem array
1630
+ else if (!cos || !$.isArray( cos ))
1631
+ return;
1632
+
1633
+ $.each( cos, function (idx, co) {
1634
+ if ( !$.isPlainObject( co ) ) return;
1635
+
1636
+ // determine which element is supposed to be the 'child container'
1637
+ // if pane has a 'containerSelector' OR a 'content-div', use those instead of the pane
1638
+ var $containers = co.containerSelector ? $P.find( co.containerSelector ) : ($C || $P);
1639
+
1640
+ $containers.each(function(){
1641
+ var $cont = $(this)
1642
+ , child = $cont.data("layout") // see if a child-layout ALREADY exists on this element
1643
+ ;
1644
+ // if no layout exists, but children are set, try to create the layout now
1645
+ if (!child) {
1646
+ // TODO: see about moving this to the stateManagement plugin, as a method
1647
+ // set a unique child-instance key for this layout, if not already set
1648
+ setInstanceKey({ container: $cont, options: co }, s );
1649
+ // If THIS layout has a hash in stateManagement.autoLoad,
1650
+ // then see if it also contains state-data for this child-layout
1651
+ // If so, copy the stateData to child.options.stateManagement.autoLoad
1652
+ if ( sm.includeChildren && state.stateData[pane] ) {
1653
+ // THIS layout's state was cached when its state was loaded
1654
+ var paneChildren = state.stateData[pane].children || {}
1655
+ , childState = paneChildren[ co.instanceKey ]
1656
+ , co_sm = co.stateManagement || (co.stateManagement = { autoLoad: true })
1657
+ ;
1658
+ // COPY the stateData into the autoLoad key
1659
+ if ( co_sm.autoLoad === true && childState ) {
1660
+ co_sm.autoSave = false; // disable autoSave because saving handled by parent-layout
1661
+ co_sm.includeChildren = true; // cascade option - FOR NOW
1662
+ co_sm.autoLoad = $.extend(true, {}, childState); // COPY the state-hash
1663
+ }
1664
+ }
1665
+
1666
+ // create the layout
1667
+ child = $cont.layout( co );
1668
+
1669
+ // if successful, update data
1670
+ if (child) {
1671
+ // add the child and update all layout-pointers
1672
+ // MAY have already been done by child-layout calling parent.refreshChildren()
1673
+ refreshChildren( pane, child );
1674
+ }
1675
+ }
1676
+ });
1532
1677
  });
1533
1678
  }
1534
1679
 
1680
+ , setInstanceKey = function (child, parentPaneState) {
1681
+ // create a named key for use in state and instance branches
1682
+ var $c = child.container
1683
+ , o = child.options
1684
+ , sm = o.stateManagement
1685
+ , key = o.instanceKey || $c.data("layoutInstanceKey")
1686
+ ;
1687
+ if (!key) key = (sm && sm.cookie ? sm.cookie.name : '') || o.name; // look for a name/key
1688
+ if (!key) key = "layout"+ (++parentPaneState.childIdx); // if no name/key found, generate one
1689
+ else key = key.replace(/[^\w-]/gi, '_').replace(/_{2,}/g, '_'); // ensure is valid as a hash key
1690
+ o.instanceKey = key;
1691
+ $c.data("layoutInstanceKey", key); // useful if layout is destroyed and then recreated
1692
+ return key;
1693
+ }
1694
+
1535
1695
  /**
1536
- * Initialize nested layouts for a specific pane - can optionally pass layout-options
1537
- *
1538
- * @see _initChildLayouts
1539
- * @param {string} pane The pane being opened, ie: north, south, east, or west
1540
- * @param {Object=} [opts] Layout-options - if passed, will OVERRRIDE options[pane].childOptions
1541
- * @return An object pointer to the layout instance created - or null
1696
+ * @param {string} pane The pane being opened, ie: north, south, east, or west
1697
+ * @param {Object=} newChild New child-layout Instance to add to this pane
1542
1698
  */
1543
- , createChildLayout = function (evt_or_pane, opts) {
1544
- var pane = evtPane.call(this, evt_or_pane)
1545
- , $P = $Ps[pane]
1546
- , C = children
1699
+ , refreshChildren = function (pane, newChild) {
1700
+ var $P = $Ps[pane]
1701
+ , pC = children[pane]
1702
+ , s = state[pane]
1703
+ , o
1547
1704
  ;
1548
- if ($P) {
1549
- var $C = $Cs[pane]
1550
- , o = opts || options[pane].childOptions
1551
- , d = "layout"
1552
- // determine which element is supposed to be the 'child container'
1553
- // if pane has a 'containerSelector' OR a 'content-div', use those instead of the pane
1554
- , $Cont = o.containerSelector ? $P.find( o.containerSelector ) : ($C || $P)
1555
- , containerFound = $Cont.length
1556
- // see if a child-layout ALREADY exists on this element
1557
- , child = containerFound ? (C[pane] = $Cont.data(d) || null) : null
1558
- ;
1559
- // if no layout exists, but childOptions are set, try to create the layout now
1560
- if (!child && containerFound && o)
1561
- child = C[pane] = $Cont.eq(0).layout(o) || null;
1562
- if (child)
1563
- child.hasParentLayout = true; // set parent-flag in child
1705
+ // check for destroy()ed layouts and update the child pointers & arrays
1706
+ if ($.isPlainObject( pC )) {
1707
+ $.each( pC, function (key, child) {
1708
+ if (child.destroyed) delete pC[key]
1709
+ });
1710
+ // if no more children, remove the children hash
1711
+ if ($.isEmptyObject( pC ))
1712
+ pC = children[pane] = null; // clear children hash
1713
+ }
1714
+
1715
+ // see if there is a directly-nested layout inside this pane
1716
+ // if there is, then there can be only ONE child-layout, so check that...
1717
+ if (!newChild && !pC) {
1718
+ newChild = $P.data("layout");
1719
+ }
1720
+
1721
+ // if a newChild instance was passed, add it to children[pane]
1722
+ if (newChild) {
1723
+ // update child.state
1724
+ newChild.hasParentLayout = true; // set parent-flag in child
1725
+ // instanceKey is a key-name used in both state and children
1726
+ o = newChild.options;
1727
+ // set a unique child-instance key for this layout, if not already set
1728
+ setInstanceKey( newChild, s );
1729
+ // add pointer to pane.children hash
1730
+ if (!pC) pC = children[pane] = {}; // create an empty children hash
1731
+ pC[ o.instanceKey ] = newChild.container.data("layout"); // add childLayout instance
1732
+ }
1733
+
1734
+ // ALWAYS refresh the pane.children alias, even if null
1735
+ Instance[pane].children = children[pane];
1736
+
1737
+ // if newChild was NOT passed - see if there is a child layout NOW
1738
+ if (!newChild) {
1739
+ createChildren(pane); // MAY create a child and re-call this method
1564
1740
  }
1565
- Instance[pane].child = C[pane]; // ALWAYS set pane-object pointer, even if null
1566
1741
  }
1567
1742
 
1568
1743
  , windowResize = function () {
1569
- var delay = Number(options.resizeWithWindowDelay);
1744
+ var o = options
1745
+ , delay = Number(o.resizeWithWindowDelay);
1570
1746
  if (delay < 10) delay = 100; // MUST have a delay!
1571
1747
  // resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway
1572
1748
  timer.clear("winResize"); // if already running
1573
1749
  timer.set("winResize", function(){
1574
1750
  timer.clear("winResize");
1575
1751
  timer.clear("winResizeRepeater");
1576
- var dims = elDims( $N );
1752
+ var dims = elDims( $N, o.inset );
1577
1753
  // only trigger resizeAll() if container has changed size
1578
1754
  if (dims.innerWidth !== sC.innerWidth || dims.innerHeight !== sC.innerHeight)
1579
1755
  resizeAll();
@@ -1606,14 +1782,14 @@ $.fn.layout = function (opts) {
1606
1782
  */
1607
1783
  , _initContainer = function () {
1608
1784
  var
1609
- N = $N[0]
1785
+ N = $N[0]
1786
+ , $H = $("html")
1610
1787
  , tag = sC.tagName = N.tagName
1611
1788
  , id = sC.id = N.id
1612
1789
  , cls = sC.className = N.className
1613
1790
  , o = options
1614
1791
  , name = o.name
1615
- , fullPage= (tag === "BODY")
1616
- , props = "overflow,position,margin,padding,border"
1792
+ , props = "position,margin,padding,border"
1617
1793
  , css = "layoutCSS"
1618
1794
  , CSS = {}
1619
1795
  , hid = "hidden" // used A LOT!
@@ -1621,10 +1797,21 @@ $.fn.layout = function (opts) {
1621
1797
  , parent = $N.data("parentLayout") // parent-layout Instance
1622
1798
  , pane = $N.data("layoutEdge") // pane-name in parent-layout
1623
1799
  , isChild = parent && pane
1800
+ , num = $.layout.cssNum
1801
+ , $parent, n
1624
1802
  ;
1625
- // sC -> state.container
1803
+ // sC = state.container
1626
1804
  sC.selector = $N.selector.split(".slice")[0];
1627
1805
  sC.ref = (o.name ? o.name +' layout / ' : '') + tag + (id ? "#"+id : cls ? '.['+cls+']' : ''); // used in messages
1806
+ sC.isBody = (tag === "BODY");
1807
+
1808
+ // try to find a parent-layout
1809
+ if (!isChild && !sC.isBody) {
1810
+ $parent = $N.closest("."+ $.layout.defaults.panes.paneClass);
1811
+ parent = $parent.data("parentLayout");
1812
+ pane = $parent.data("layoutEdge");
1813
+ isChild = parent && pane;
1814
+ }
1628
1815
 
1629
1816
  $N .data({
1630
1817
  layout: Instance
@@ -1633,12 +1820,11 @@ $.fn.layout = function (opts) {
1633
1820
  .addClass(o.containerClass)
1634
1821
  ;
1635
1822
  var layoutMethods = {
1636
- destroy: ''
1637
- , initPanes: ''
1638
- , resizeAll: 'resizeAll'
1639
- , resize: 'resizeAll'
1640
- }
1641
- , name;
1823
+ destroy: ''
1824
+ , initPanes: ''
1825
+ , resizeAll: 'resizeAll'
1826
+ , resize: 'resizeAll'
1827
+ };
1642
1828
  // loop hash and bind all methods - include layoutID namespacing
1643
1829
  for (name in layoutMethods) {
1644
1830
  $N.bind("layout"+ name.toLowerCase() +"."+ sID, Instance[ layoutMethods[name] || name ]);
@@ -1649,82 +1835,126 @@ $.fn.layout = function (opts) {
1649
1835
  // update parent flag
1650
1836
  Instance.hasParentLayout = true;
1651
1837
  // set pointers to THIS child-layout (Instance) in parent-layout
1652
- // NOTE: parent.PANE.child is an ALIAS to parent.children.PANE
1653
- parent[pane].child = parent.children[pane] = $N.data("layout");
1838
+ parent.refreshChildren( pane, Instance );
1654
1839
  }
1655
1840
 
1656
1841
  // SAVE original container CSS for use in destroy()
1657
1842
  if (!$N.data(css)) {
1658
1843
  // handle props like overflow different for BODY & HTML - has 'system default' values
1659
- if (fullPage) {
1660
- CSS = $.extend( elCSS($N, props), {
1844
+ if (sC.isBody) {
1845
+ // SAVE <BODY> CSS
1846
+ $N.data(css, $.extend( styles($N, props), {
1661
1847
  height: $N.css("height")
1662
1848
  , overflow: $N.css("overflow")
1663
1849
  , overflowX: $N.css("overflowX")
1664
1850
  , overflowY: $N.css("overflowY")
1665
- });
1851
+ }));
1666
1852
  // ALSO SAVE <HTML> CSS
1667
- var $H = $("html");
1668
- $H.data(css, {
1853
+ $H.data(css, $.extend( styles($H, 'padding'), {
1669
1854
  height: "auto" // FF would return a fixed px-size!
1670
1855
  , overflow: $H.css("overflow")
1671
1856
  , overflowX: $H.css("overflowX")
1672
1857
  , overflowY: $H.css("overflowY")
1673
- });
1858
+ }));
1674
1859
  }
1675
1860
  else // handle props normally for non-body elements
1676
- CSS = elCSS($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY");
1677
-
1678
- $N.data(css, CSS);
1861
+ $N.data(css, styles($N, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY") );
1679
1862
  }
1680
1863
 
1681
- try { // format html/body if this is a full page layout
1682
- if (fullPage) {
1683
- $("html").css({
1864
+ try {
1865
+ // common container CSS
1866
+ CSS = {
1867
+ overflow: hid
1868
+ , overflowX: hid
1869
+ , overflowY: hid
1870
+ };
1871
+ $N.css( CSS );
1872
+
1873
+ if (o.inset && !$.isPlainObject(o.inset)) {
1874
+ // can specify a single number for equal outset all-around
1875
+ n = parseInt(o.inset, 10) || 0
1876
+ o.inset = {
1877
+ top: n
1878
+ , bottom: n
1879
+ , left: n
1880
+ , right: n
1881
+ };
1882
+ }
1883
+
1884
+ // format html & body if this is a full page layout
1885
+ if (sC.isBody) {
1886
+ // if HTML has padding, use this as an outer-spacing around BODY
1887
+ if (!o.outset) {
1888
+ // use padding from parent-elem (HTML) as outset
1889
+ o.outset = {
1890
+ top: num($H, "paddingTop")
1891
+ , bottom: num($H, "paddingBottom")
1892
+ , left: num($H, "paddingLeft")
1893
+ , right: num($H, "paddingRight")
1894
+ };
1895
+ }
1896
+ else if (!$.isPlainObject(o.outset)) {
1897
+ // can specify a single number for equal outset all-around
1898
+ n = parseInt(o.outset, 10) || 0
1899
+ o.outset = {
1900
+ top: n
1901
+ , bottom: n
1902
+ , left: n
1903
+ , right: n
1904
+ };
1905
+ }
1906
+ // HTML
1907
+ $H.css( CSS ).css({
1684
1908
  height: "100%"
1685
- , overflow: hid
1686
- , overflowX: hid
1687
- , overflowY: hid
1688
- });
1689
- $("body").css({
1690
- position: "relative"
1691
- , height: "100%"
1692
- , overflow: hid
1693
- , overflowX: hid
1694
- , overflowY: hid
1909
+ , border: "none" // no border or padding allowed when using height = 100%
1910
+ , padding: 0 // ditto
1695
1911
  , margin: 0
1696
- , padding: 0 // TODO: test whether body-padding could be handled?
1697
- , border: "none" // a body-border creates problems because it cannot be measured!
1698
1912
  });
1699
-
1913
+ // BODY
1914
+ if (browser.isIE6) {
1915
+ // IE6 CANNOT use the trick of setting absolute positioning on all 4 sides - must have 'height'
1916
+ $N.css({
1917
+ width: "100%"
1918
+ , height: "100%"
1919
+ , border: "none" // no border or padding allowed when using height = 100%
1920
+ , padding: 0 // ditto
1921
+ , margin: 0
1922
+ , position: "relative"
1923
+ });
1924
+ // convert body padding to an inset option - the border cannot be measured in IE6!
1925
+ if (!o.inset) o.inset = elDims( $N ).inset;
1926
+ }
1927
+ else { // use absolute positioning for BODY to allow borders & padding without overflow
1928
+ $N.css({
1929
+ width: "auto"
1930
+ , height: "auto"
1931
+ , margin: 0
1932
+ , position: "absolute" // allows for border and padding on BODY
1933
+ });
1934
+ // apply edge-positioning created above
1935
+ $N.css( o.outset );
1936
+ }
1700
1937
  // set current layout-container dimensions
1701
- $.extend(sC, elDims( $N ));
1938
+ $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT include insetX values
1702
1939
  }
1703
- else { // set required CSS for overflow and position
1704
- // ENSURE container will not 'scroll'
1705
- CSS = { overflow: hid, overflowX: hid, overflowY: hid }
1706
- var
1707
- p = $N.css("position")
1708
- , h = $N.css("height")
1709
- ;
1710
- // if this is a NESTED layout, then container/outer-pane ALREADY has position and height
1711
- if (!isChild) {
1712
- if (!p || !p.match(/fixed|absolute|relative/))
1713
- CSS.position = "relative"; // container MUST have a 'position'
1714
- /*
1715
- if (!h || h=="auto")
1716
- CSS.height = "100%"; // container MUST have a 'height'
1717
- */
1718
- }
1719
- $N.css( CSS );
1940
+ else {
1941
+ // container MUST have 'position'
1942
+ var p = $N.css("position");
1943
+ if (!p || !p.match(/(fixed|absolute|relative)/))
1944
+ $N.css("position","relative");
1720
1945
 
1721
1946
  // set current layout-container dimensions
1722
1947
  if ( $N.is(":visible") ) {
1723
- $.extend(sC, elDims( $N ));
1724
- if (o.showErrorMessages && sC.innerHeight < 1)
1725
- _log( lang.errContainerHeight.replace(/CONTAINER/, sC.ref), true );
1948
+ $.extend(sC, elDims( $N, o.inset )); // passing inset means DO NOT change insetX (padding) values
1949
+ if (sC.innerHeight < 1) // container has no 'height' - warn developer
1950
+ _log( o.errors.noContainerHeight.replace(/CONTAINER/, sC.ref) );
1726
1951
  }
1727
1952
  }
1953
+
1954
+ // if container has min-width/height, then enable scrollbar(s)
1955
+ if ( num($N, "minWidth") ) $N.parent().css("overflowX","auto");
1956
+ if ( num($N, "minHeight") ) $N.parent().css("overflowY","auto");
1957
+
1728
1958
  } catch (ex) {}
1729
1959
  }
1730
1960
 
@@ -1755,12 +1985,12 @@ $.fn.layout = function (opts) {
1755
1985
  var data, d, pane, key, val, i, c, o;
1756
1986
 
1757
1987
  // reprocess user's layout-options to have correct options sub-key structure
1758
- opts = $.layout.transformData( opts ); // panes = default subkey
1988
+ opts = $.layout.transformData( opts, true ); // panes = default subkey
1759
1989
 
1760
1990
  // auto-rename old options for backward compatibility
1761
1991
  opts = $.layout.backwardCompatibility.renameAllOptions( opts );
1762
1992
 
1763
- // if user-options has 'panes' key (pane-defaults), process it...
1993
+ // if user-options has 'panes' key (pane-defaults), clean it...
1764
1994
  if (!$.isEmptyObject(opts.panes)) {
1765
1995
  // REMOVE any pane-defaults that MUST be set per-pane
1766
1996
  data = $.layout.optionsMap.noDefault;
@@ -1776,7 +2006,7 @@ $.fn.layout = function (opts) {
1776
2006
  }
1777
2007
  }
1778
2008
 
1779
- // MOVE any NON-layout-options to opts.panes
2009
+ // MOVE any NON-layout-options from opts-root to opts.panes
1780
2010
  data = $.layout.optionsMap.layout;
1781
2011
  var rootKeys = $.layout.config.optionRootKeys;
1782
2012
  for (key in opts) {
@@ -1795,7 +2025,7 @@ $.fn.layout = function (opts) {
1795
2025
  $.each(_c.allPanes, function (i, pane) {
1796
2026
 
1797
2027
  // apply 'pane-defaults' to CONFIG.[PANE]
1798
- _c[pane] = $.extend( true, {}, _c.panes, _c[pane] );
2028
+ _c[pane] = $.extend(true, {}, _c.panes, _c[pane]);
1799
2029
 
1800
2030
  d = options.panes;
1801
2031
  o = options[pane];
@@ -1813,7 +2043,7 @@ $.fn.layout = function (opts) {
1813
2043
  }
1814
2044
  else {
1815
2045
  // border-panes use ALL keys in defaults.panes branch
1816
- o = options[pane] = $.extend({}, d, o); // re-apply pane-specific opts AFTER pane-defaults
2046
+ o = options[pane] = $.extend(true, {}, d, o); // re-apply pane-specific opts AFTER pane-defaults
1817
2047
  createFxOptions( pane );
1818
2048
  // ensure all border-pane-specific base-classes exist
1819
2049
  if (!o.resizerClass) o.resizerClass = "ui-layout-resizer";
@@ -1832,6 +2062,10 @@ $.fn.layout = function (opts) {
1832
2062
  z.resizer_normal = max(zo+2, z.resizer_normal); // MIN = +2
1833
2063
  }
1834
2064
 
2065
+ // DELETE 'panes' key now that we are done - values were copied to EACH pane
2066
+ delete options.panes;
2067
+
2068
+
1835
2069
  function createFxOptions ( pane ) {
1836
2070
  var o = options[pane]
1837
2071
  , d = options.panes;
@@ -1851,9 +2085,10 @@ $.fn.layout = function (opts) {
1851
2085
  || o.fxName // options.west.fxName
1852
2086
  || d.fxName // options.panes.fxName
1853
2087
  || "none" // MEANS $.layout.defaults.panes.fxName == "" || false || null || 0
2088
+ , fxExists = $.effects && ($.effects[fxName] || ($.effects.effect && $.effects.effect[fxName]))
1854
2089
  ;
1855
2090
  // validate fxName to ensure is valid effect - MUST have effect-config data in options.effects
1856
- if (fxName === "none" || !$.effects || !$.effects[fxName] || !options.effects[fxName])
2091
+ if (fxName === "none" || !options.effects[fxName] || !fxExists)
1857
2092
  fxName = o[sName] = "none"; // effect not loaded OR unrecognized fxName
1858
2093
 
1859
2094
  // set vars for effects subkeys to simplify logic
@@ -1871,7 +2106,8 @@ $.fn.layout = function (opts) {
1871
2106
  ;
1872
2107
  // create fxSettings[_open|_close|_size]
1873
2108
  o[sSettings] = $.extend(
1874
- {}
2109
+ true
2110
+ , {}
1875
2111
  , fx_all // effects.slide.all
1876
2112
  , fx_pane // effects.slide.west
1877
2113
  , d.fxSettings // options.panes.fxSettings
@@ -1887,9 +2123,6 @@ $.fn.layout = function (opts) {
1887
2123
  delete o.fxSpeed;
1888
2124
  delete o.fxSettings;
1889
2125
  }
1890
-
1891
- // DELETE 'panes' key now that we are done - values were copied to EACH pane
1892
- delete options.panes;
1893
2126
  }
1894
2127
 
1895
2128
  /**
@@ -1910,7 +2143,13 @@ $.fn.layout = function (opts) {
1910
2143
  }
1911
2144
  }
1912
2145
 
1913
- , initPanes = function () {
2146
+ /**
2147
+ * @param {Object=} evt
2148
+ */
2149
+ , initPanes = function (evt) {
2150
+ // stopPropagation if called by trigger("layoutinitpanes") - use evtPane utility
2151
+ evtPane(evt);
2152
+
1914
2153
  // NOTE: do north & south FIRST so we can measure their height - do center LAST
1915
2154
  $.each(_c.allPanes, function (idx, pane) {
1916
2155
  addPane( pane, true );
@@ -1935,22 +2174,8 @@ $.fn.layout = function (opts) {
1935
2174
  // to load asynchrously, which is BAD, so try skipping delay for now
1936
2175
 
1937
2176
  // process pane contents and callbacks, and init/resize child-layout if exists
1938
- $.each(_c.allPanes, function (i, pane) {
1939
- var o = options[pane];
1940
- if ($Ps[pane]) {
1941
- if (state[pane].isVisible) { // pane is OPEN
1942
- sizeContent(pane);
1943
- // trigger pane.onResize if triggerEventsOnLoad = true
1944
- if (o.triggerEventsOnLoad)
1945
- _runCallbacks("onresize_end", pane);
1946
- else // automatic if onresize called, otherwise call it specifically
1947
- // resize child - IF inner-layout already exists (created before this layout)
1948
- resizeChildLayout(pane);
1949
- }
1950
- // init childLayout - even if pane is not visible
1951
- if (o.initChildLayout && o.childOptions)
1952
- createChildLayout(pane);
1953
- }
2177
+ $.each(_c.allPanes, function (idx, pane) {
2178
+ afterInitPane(pane);
1954
2179
  });
1955
2180
  }
1956
2181
 
@@ -1967,13 +2192,13 @@ $.fn.layout = function (opts) {
1967
2192
  o = options[pane]
1968
2193
  , s = state[pane]
1969
2194
  , c = _c[pane]
1970
- , fx = s.fx
1971
2195
  , dir = c.dir
2196
+ , fx = s.fx
1972
2197
  , spacing = o.spacing_open || 0
1973
2198
  , isCenter = (pane === "center")
1974
2199
  , CSS = {}
1975
2200
  , $P = $Ps[pane]
1976
- , size, minSize, maxSize
2201
+ , size, minSize, maxSize, child
1977
2202
  ;
1978
2203
  // if pane-pointer already exists, remove the old one first
1979
2204
  if ($P)
@@ -1990,11 +2215,18 @@ $.fn.layout = function (opts) {
1990
2215
  // SAVE original Pane CSS
1991
2216
  if (!$P.data("layoutCSS")) {
1992
2217
  var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";
1993
- $P.data("layoutCSS", elCSS($P, props));
2218
+ $P.data("layoutCSS", styles($P, props));
1994
2219
  }
1995
2220
 
1996
2221
  // create alias for pane data in Instance - initHandles will add more
1997
- Instance[pane] = { name: pane, pane: $Ps[pane], content: $Cs[pane], options: options[pane], state: state[pane], child: children[pane] };
2222
+ Instance[pane] = {
2223
+ name: pane
2224
+ , pane: $Ps[pane]
2225
+ , content: $Cs[pane]
2226
+ , options: options[pane]
2227
+ , state: state[pane]
2228
+ , children: children[pane]
2229
+ };
1998
2230
 
1999
2231
  // add classes, attributes & events
2000
2232
  $P .data({
@@ -2018,8 +2250,8 @@ $.fn.layout = function (opts) {
2018
2250
  , slideOpen: ''
2019
2251
  , slideClose: ''
2020
2252
  , slideToggle: ''
2021
- , size: 'manualSizePane'
2022
- , sizePane: 'manualSizePane'
2253
+ , size: 'sizePane'
2254
+ , sizePane: 'sizePane'
2023
2255
  , sizeContent: ''
2024
2256
  , sizeHandles: ''
2025
2257
  , enableClosable: ''
@@ -2033,8 +2265,8 @@ $.fn.layout = function (opts) {
2033
2265
  , move: 'swapPanes'
2034
2266
  , removePane: 'removePane'
2035
2267
  , remove: 'removePane'
2036
- , createChildLayout: ''
2037
- , resizeChildLayout: ''
2268
+ , createChildren: ''
2269
+ , resizeChildren: ''
2038
2270
  , resizeAll: 'resizeAll'
2039
2271
  , resizeLayout: 'resizeAll'
2040
2272
  }
@@ -2054,6 +2286,7 @@ $.fn.layout = function (opts) {
2054
2286
  minSize = _parseSize(pane,o.minSize) || 1;
2055
2287
  maxSize = _parseSize(pane,o.maxSize) || 100000;
2056
2288
  if (size > 0) size = max(min(size, maxSize), minSize);
2289
+ s.autoResize = o.autoResize; // used with percentage sizes
2057
2290
 
2058
2291
  // state for border-panes
2059
2292
  s.isClosed = false; // true = pane is closed
@@ -2070,23 +2303,10 @@ $.fn.layout = function (opts) {
2070
2303
  s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
2071
2304
  s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic
2072
2305
 
2073
- // set css-position to account for container borders & padding
2074
- switch (pane) {
2075
- case "north": CSS.top = sC.insetTop;
2076
- CSS.left = sC.insetLeft;
2077
- CSS.right = sC.insetRight;
2078
- break;
2079
- case "south": CSS.bottom = sC.insetBottom;
2080
- CSS.left = sC.insetLeft;
2081
- CSS.right = sC.insetRight;
2082
- break;
2083
- case "west": CSS.left = sC.insetLeft; // top, bottom & height set by sizeMidPanes()
2084
- break;
2085
- case "east": CSS.right = sC.insetRight; // ditto
2086
- break;
2087
- case "center": // top, left, width & height set by sizeMidPanes()
2088
- }
2306
+ // init pane positioning
2307
+ setPanePosition( pane );
2089
2308
 
2309
+ // if pane is not visible,
2090
2310
  if (dir === "horz") // north or south pane
2091
2311
  CSS.height = cssH($P, size);
2092
2312
  else if (dir === "vert") // east or west pane
@@ -2096,6 +2316,12 @@ $.fn.layout = function (opts) {
2096
2316
  $P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes
2097
2317
  if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback
2098
2318
 
2319
+ // if manually adding a pane AFTER layout initialization, then...
2320
+ if (state.initialized) {
2321
+ initHandles( pane );
2322
+ initHotkeys( pane );
2323
+ }
2324
+
2099
2325
  // close or hide the pane if specified in settings
2100
2326
  if (o.initClosed && o.closable && !o.initHidden)
2101
2327
  close(pane, true, true); // true, true = force, noAnimation
@@ -2115,21 +2341,84 @@ $.fn.layout = function (opts) {
2115
2341
 
2116
2342
  // if manually adding a pane AFTER layout initialization, then...
2117
2343
  if (state.initialized) {
2118
- initHandles( pane );
2119
- initHotkeys( pane );
2120
- resizeAll(); // will sizeContent if pane is visible
2121
- if (s.isVisible) { // pane is OPEN
2122
- if (o.triggerEventsOnLoad)
2123
- _runCallbacks("onresize_end", pane);
2124
- else // automatic if onresize called, otherwise call it specifically
2125
- // resize child - IF inner-layout already exists (created before this layout)
2126
- resizeChildLayout(pane); // a previously existing childLayout
2127
- }
2128
- if (o.initChildLayout && o.childOptions)
2129
- createChildLayout(pane);
2344
+ afterInitPane( pane );
2130
2345
  }
2131
2346
  }
2132
2347
 
2348
+ , afterInitPane = function (pane) {
2349
+ var $P = $Ps[pane]
2350
+ , s = state[pane]
2351
+ , o = options[pane]
2352
+ ;
2353
+ if (!$P) return;
2354
+
2355
+ // see if there is a directly-nested layout inside this pane
2356
+ if ($P.data("layout"))
2357
+ refreshChildren( pane, $P.data("layout") );
2358
+
2359
+ // process pane contents and callbacks, and init/resize child-layout if exists
2360
+ if (s.isVisible) { // pane is OPEN
2361
+ if (state.initialized) // this pane was added AFTER layout was created
2362
+ resizeAll(); // will also sizeContent
2363
+ else
2364
+ sizeContent(pane);
2365
+
2366
+ if (o.triggerEventsOnLoad)
2367
+ _runCallbacks("onresize_end", pane);
2368
+ else // automatic if onresize called, otherwise call it specifically
2369
+ // resize child - IF inner-layout already exists (created before this layout)
2370
+ resizeChildren(pane, true); // a previously existing childLayout
2371
+ }
2372
+
2373
+ // init childLayouts - even if pane is not visible
2374
+ if (o.initChildren && o.children)
2375
+ createChildren(pane);
2376
+ }
2377
+
2378
+ /**
2379
+ * @param {string=} panes The pane(s) to process
2380
+ */
2381
+ , setPanePosition = function (panes) {
2382
+ panes = panes ? panes.split(",") : _c.borderPanes;
2383
+
2384
+ // create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
2385
+ $.each(panes, function (i, pane) {
2386
+ var $P = $Ps[pane]
2387
+ , $R = $Rs[pane]
2388
+ , o = options[pane]
2389
+ , s = state[pane]
2390
+ , side = _c[pane].side
2391
+ , CSS = {}
2392
+ ;
2393
+ if (!$P) return; // pane does not exist - skip
2394
+
2395
+ // set css-position to account for container borders & padding
2396
+ switch (pane) {
2397
+ case "north": CSS.top = sC.inset.top;
2398
+ CSS.left = sC.inset.left;
2399
+ CSS.right = sC.inset.right;
2400
+ break;
2401
+ case "south": CSS.bottom = sC.inset.bottom;
2402
+ CSS.left = sC.inset.left;
2403
+ CSS.right = sC.inset.right;
2404
+ break;
2405
+ case "west": CSS.left = sC.inset.left; // top, bottom & height set by sizeMidPanes()
2406
+ break;
2407
+ case "east": CSS.right = sC.inset.right; // ditto
2408
+ break;
2409
+ case "center": // top, left, width & height set by sizeMidPanes()
2410
+ }
2411
+ // apply position
2412
+ $P.css(CSS);
2413
+
2414
+ // update resizer position
2415
+ if ($R && s.isClosed)
2416
+ $R.css(side, sC.inset[side]);
2417
+ else if ($R && !s.isHidden)
2418
+ $R.css(side, sC.inset[side] + getPaneSize(pane));
2419
+ });
2420
+ }
2421
+
2133
2422
  /**
2134
2423
  * Initialize module objects, styling, size and position for all resize bars and toggler buttons
2135
2424
  *
@@ -2146,13 +2435,12 @@ $.fn.layout = function (opts) {
2146
2435
  $Ts[pane] = false;
2147
2436
  if (!$P) return; // pane does not exist - skip
2148
2437
 
2149
- var
2150
- o = options[pane]
2438
+ var o = options[pane]
2151
2439
  , s = state[pane]
2152
2440
  , c = _c[pane]
2441
+ , paneId = o.paneSelector.substr(0,1) === "#" ? o.paneSelector.substr(1) : ""
2153
2442
  , rClass = o.resizerClass
2154
2443
  , tClass = o.togglerClass
2155
- , side = c.side.toLowerCase()
2156
2444
  , spacing = (s.isVisible ? o.spacing_open : o.spacing_closed)
2157
2445
  , _pane = "-"+ pane // used for classNames
2158
2446
  , _state = (s.isVisible ? "-open" : "-closed") // used for classNames
@@ -2165,10 +2453,10 @@ $.fn.layout = function (opts) {
2165
2453
 
2166
2454
  //if (s.isVisible && o.resizable) ... handled by initResizable
2167
2455
  if (!s.isVisible && o.slidable)
2168
- $R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
2456
+ $R.attr("title", o.tips.Slide).css("cursor", o.sliderCursor);
2169
2457
 
2170
2458
  $R // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
2171
- .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
2459
+ .attr("id", paneId ? paneId +"-resizer" : "" )
2172
2460
  .data({
2173
2461
  parentLayout: Instance
2174
2462
  , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
@@ -2182,10 +2470,12 @@ $.fn.layout = function (opts) {
2182
2470
  .hover(onResizerEnter, onResizerLeave) // ALWAYS NEED resizer.mouseleave to balance toggler.mouseenter
2183
2471
  .appendTo($N) // append DIV to container
2184
2472
  ;
2473
+ if (o.resizerDblClickToggle)
2474
+ $R.bind("dblclick."+ sID, toggle );
2185
2475
 
2186
2476
  if ($T) {
2187
2477
  $T // if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler"
2188
- .attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
2478
+ .attr("id", paneId ? paneId +"-toggler" : "" )
2189
2479
  .data({
2190
2480
  parentLayout: Instance
2191
2481
  , layoutPane: Instance[pane] // NEW pointer to pane-alias-object
@@ -2236,7 +2526,7 @@ $.fn.layout = function (opts) {
2236
2526
  setAsOpen(pane); // onOpen will be called, but NOT onResize
2237
2527
  else {
2238
2528
  setAsClosed(pane); // onClose will be called
2239
- bindStartSlidingEvent(pane, true); // will enable events IF option is set
2529
+ bindStartSlidingEvents(pane, true); // will enable events IF option is set
2240
2530
  }
2241
2531
 
2242
2532
  });
@@ -2250,7 +2540,7 @@ $.fn.layout = function (opts) {
2250
2540
  * Initialize scrolling ui-layout-content div - if exists
2251
2541
  *
2252
2542
  * @see initPane() - or externally after an Ajax injection
2253
- * @param {string} [pane] The pane to process
2543
+ * @param {string} pane The pane to process
2254
2544
  * @param {boolean=} [resize=true] Size content after init
2255
2545
  */
2256
2546
  , initContent = function (pane, resize) {
@@ -2268,14 +2558,18 @@ $.fn.layout = function (opts) {
2268
2558
  ;
2269
2559
  if ($C && $C.length) {
2270
2560
  $C.data("layoutRole", "content");
2271
- // SAVE original Pane CSS
2561
+ // SAVE original Content CSS
2272
2562
  if (!$C.data("layoutCSS"))
2273
- $C.data("layoutCSS", elCSS($C, "height"));
2563
+ $C.data("layoutCSS", styles($C, "height"));
2274
2564
  $C.css( _c.content.cssReq );
2275
2565
  if (o.applyDemoStyles) {
2276
2566
  $C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div
2277
2567
  $P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane
2278
2568
  }
2569
+ // ensure no vertical scrollbar on pane - will mess up measurements
2570
+ if ($P.css("overflowX").match(/(scroll|auto)/)) {
2571
+ $P.css("overflow", "hidden");
2572
+ }
2279
2573
  state[pane].content = {}; // init content state
2280
2574
  if (resize !== false) sizeContent(pane);
2281
2575
  // sizeContent() is called AFTER init of all elements
@@ -2309,8 +2603,6 @@ $.fn.layout = function (opts) {
2309
2603
  , z = options.zIndexes
2310
2604
  , c = _c[pane]
2311
2605
  , side = c.dir=="horz" ? "top" : "left"
2312
- , opEdge = _c.oppositeEdge[pane]
2313
- , masks = pane +",center,"+ opEdge + (c.dir=="horz" ? ",west,east" : "")
2314
2606
  , $P = $Ps[pane]
2315
2607
  , $R = $Rs[pane]
2316
2608
  , base = o.resizerClass
@@ -2328,7 +2620,7 @@ $.fn.layout = function (opts) {
2328
2620
  ;
2329
2621
 
2330
2622
  if (!s.isClosed)
2331
- $R.attr("title", o.resizerTip)
2623
+ $R.attr("title", o.tips.Resize)
2332
2624
  .css("cursor", o.resizerCursor); // n-resize, s-resize, etc
2333
2625
 
2334
2626
  $R.draggable({
@@ -2355,7 +2647,8 @@ $.fn.layout = function (opts) {
2355
2647
  // TODO: dragging CANNOT be cancelled like this, so see if there is a way?
2356
2648
  if (false === _runCallbacks("ondrag_start", pane)) return false;
2357
2649
 
2358
- s.isResizing = true; // prevent pane from closing while resizing
2650
+ s.isResizing = true; // prevent pane from closing while resizing
2651
+ state.paneResizing = pane; // easy to see if ANY pane is resizing
2359
2652
  timer.clear(pane+"_closeSlider"); // just in case already triggered
2360
2653
 
2361
2654
  // SET RESIZER LIMITS - used in drag()
@@ -2370,7 +2663,7 @@ $.fn.layout = function (opts) {
2370
2663
  $('body').disableSelection();
2371
2664
 
2372
2665
  // MASK PANES CONTAINING IFRAMES, APPLETS OR OTHER TROUBLESOME ELEMENTS
2373
- showMasks( masks );
2666
+ showMasks( pane, { resizing: true });
2374
2667
  }
2375
2668
 
2376
2669
  , drag: function (e, ui) {
@@ -2398,7 +2691,7 @@ $.fn.layout = function (opts) {
2398
2691
  // ADD/REMOVE dragging-limit CLASS
2399
2692
  if (limit) {
2400
2693
  ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit
2401
- window.defaultStatus = (limit>0 && pane.match(/north|west/)) || (limit<0 && pane.match(/south|east/)) ? lang.maxSizeWarning : lang.minSizeWarning;
2694
+ window.defaultStatus = (limit>0 && pane.match(/(north|west)/)) || (limit<0 && pane.match(/(south|east)/)) ? o.tips.maxSizeWarning : o.tips.minSizeWarning;
2402
2695
  }
2403
2696
  else {
2404
2697
  ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit
@@ -2416,8 +2709,9 @@ $.fn.layout = function (opts) {
2416
2709
  $('body').enableSelection(); // RE-ENABLE TEXT SELECTION
2417
2710
  window.defaultStatus = ""; // clear 'resizing limit' message from statusbar
2418
2711
  $R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer
2419
- s.isResizing = false;
2420
- resizePanes(e, ui, pane, true, masks); // true = resizingDone
2712
+ s.isResizing = false;
2713
+ state.paneResizing = false; // easy to see if ANY pane is resizing
2714
+ resizePanes(e, ui, pane, true); // true = resizingDone
2421
2715
  }
2422
2716
 
2423
2717
  });
@@ -2433,7 +2727,7 @@ $.fn.layout = function (opts) {
2433
2727
  * @param {string} pane
2434
2728
  * @param {boolean=} [resizingDone=false]
2435
2729
  */
2436
- var resizePanes = function (evt, ui, pane, resizingDone, masks) {
2730
+ var resizePanes = function (evt, ui, pane, resizingDone) {
2437
2731
  var dragPos = ui.position
2438
2732
  , c = _c[pane]
2439
2733
  , o = options[pane]
@@ -2443,11 +2737,11 @@ $.fn.layout = function (opts) {
2443
2737
  switch (pane) {
2444
2738
  case "north": resizerPos = dragPos.top; break;
2445
2739
  case "west": resizerPos = dragPos.left; break;
2446
- case "south": resizerPos = sC.offsetHeight - dragPos.top - o.spacing_open; break;
2447
- case "east": resizerPos = sC.offsetWidth - dragPos.left - o.spacing_open; break;
2740
+ case "south": resizerPos = sC.layoutHeight - dragPos.top - o.spacing_open; break;
2741
+ case "east": resizerPos = sC.layoutWidth - dragPos.left - o.spacing_open; break;
2448
2742
  };
2449
2743
  // remove container margin from resizer position to get the pane size
2450
- var newSize = resizerPos - sC["inset"+ c.side];
2744
+ var newSize = resizerPos - sC.inset[c.side];
2451
2745
 
2452
2746
  // Disable OR Resize Mask(s) created in drag.start
2453
2747
  if (!resizingDone) {
@@ -2462,19 +2756,19 @@ $.fn.layout = function (opts) {
2462
2756
  // ondrag_end callback
2463
2757
  if (false !== _runCallbacks("ondrag_end", pane))
2464
2758
  manualSizePane(pane, newSize, false, true); // true = noAnimation
2465
- hideMasks(); // hide all masks, which include panes with 'content/iframe-masks'
2466
- if (s.isSliding && masks) // RE-SHOW only 'object-masks' so objects won't show through sliding pane
2467
- showMasks( masks, true ); // true = onlyForObjects
2759
+ hideMasks(true); // true = force hiding all masks even if one is 'sliding'
2760
+ if (s.isSliding) // RE-SHOW 'object-masks' so objects won't show through sliding pane
2761
+ showMasks( pane, { resizing: true });
2468
2762
  }
2469
2763
  };
2470
2764
  }
2471
2765
 
2472
2766
  /**
2473
- * sizeMask
2474
- *
2475
- * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane
2476
- * Called when mask created, and during livePaneResizing
2477
- */
2767
+ * sizeMask
2768
+ *
2769
+ * Needed to overlay a DIV over an IFRAME-pane because mask CANNOT be *inside* the pane
2770
+ * Called when mask created, and during livePaneResizing
2771
+ */
2478
2772
  , sizeMask = function () {
2479
2773
  var $M = $(this)
2480
2774
  , pane = $M.data("layoutMask") // eg: "west"
@@ -2497,14 +2791,36 @@ $.fn.layout = function (opts) {
2497
2791
  $Ms.each( sizeMask ); // resize all 'visible' masks
2498
2792
  }
2499
2793
 
2500
- , showMasks = function (panes, onlyForObjects) {
2501
- var a = panes ? panes.split(",") : $.layout.config.allPanes
2502
- , z = options.zIndexes
2503
- , o, s;
2504
- $.each(a, function(i,p){
2794
+ /**
2795
+ * @param {string} pane The pane being resized, animated or isSliding
2796
+ * @param {Object=} [args] (optional) Options: which masks to apply, and to which panes
2797
+ */
2798
+ , showMasks = function (pane, args) {
2799
+ var c = _c[pane]
2800
+ , panes = ["center"]
2801
+ , z = options.zIndexes
2802
+ , a = $.extend({
2803
+ objectsOnly: false
2804
+ , animation: false
2805
+ , resizing: true
2806
+ , sliding: state[pane].isSliding
2807
+ }, args )
2808
+ , o, s
2809
+ ;
2810
+ if (a.resizing)
2811
+ panes.push( pane );
2812
+ if (a.sliding)
2813
+ panes.push( _c.oppositeEdge[pane] ); // ADD the oppositeEdge-pane
2814
+
2815
+ if (c.dir === "horz") {
2816
+ panes.push("west");
2817
+ panes.push("east");
2818
+ }
2819
+
2820
+ $.each(panes, function(i,p){
2505
2821
  s = state[p];
2506
2822
  o = options[p];
2507
- if (s.isVisible && ( (!onlyForObjects && o.maskContents) || o.maskObjects )) {
2823
+ if (s.isVisible && ( o.maskObjects || (!a.objectsOnly && o.maskContents) )) {
2508
2824
  getMasks(p).each(function(){
2509
2825
  sizeMask.call(this);
2510
2826
  this.style.zIndex = s.isSliding ? z.pane_sliding+1 : z.pane_normal+1
@@ -2514,19 +2830,31 @@ $.fn.layout = function (opts) {
2514
2830
  });
2515
2831
  }
2516
2832
 
2517
- , hideMasks = function () {
2833
+ /**
2834
+ * @param {boolean=} force Hide masks even if a pane is sliding
2835
+ */
2836
+ , hideMasks = function (force) {
2518
2837
  // ensure no pane is resizing - could be a timing issue
2519
- var skip;
2520
- $.each( $.layout.config.borderPanes, function(i,p){
2521
- if (state[p].isResizing) {
2522
- skip = true;
2523
- return false; // BREAK
2524
- }
2525
- });
2526
- if (!skip)
2838
+ if (force || !state.paneResizing) {
2527
2839
  $Ms.hide(); // hide ALL masks
2840
+ }
2841
+ // if ANY pane is sliding, then DO NOT remove masks from panes with maskObjects enabled
2842
+ else if (!force && !$.isEmptyObject( state.panesSliding )) {
2843
+ var i = $Ms.length - 1
2844
+ , p, $M;
2845
+ for (; i >= 0; i--) {
2846
+ $M = $Ms.eq(i);
2847
+ p = $M.data("layoutMask");
2848
+ if (!options[p].maskObjects) {
2849
+ $M.hide();
2850
+ }
2851
+ }
2852
+ }
2528
2853
  }
2529
2854
 
2855
+ /**
2856
+ * @param {string} pane
2857
+ */
2530
2858
  , getMasks = function (pane) {
2531
2859
  var $Masks = $([])
2532
2860
  , $M, i = 0, c = $Ms.length
@@ -2543,11 +2871,13 @@ $.fn.layout = function (opts) {
2543
2871
  }
2544
2872
 
2545
2873
  /**
2546
- * createMasks
2547
- *
2548
- * Generates both DIV (ALWAYS used) and IFRAME (optional) elements as masks
2549
- * An IFRAME mask is created *under* the DIV when maskObjects=true, because a DIV cannot mask an applet
2550
- */
2874
+ * createMasks
2875
+ *
2876
+ * Generates both DIV (ALWAYS used) and IFRAME (optional) elements as masks
2877
+ * An IFRAME mask is created *under* the DIV when maskObjects=true, because a DIV cannot mask an applet
2878
+ *
2879
+ * @param {string} pane
2880
+ */
2551
2881
  , createMasks = function (pane) {
2552
2882
  var
2553
2883
  $P = $Ps[pane]
@@ -2569,9 +2899,11 @@ $.fn.layout = function (opts) {
2569
2899
  // styles common to both DIVs and IFRAMES
2570
2900
  css.display = "block";
2571
2901
  css.position = "absolute";
2902
+ css.background = "#FFF";
2572
2903
  if (isIframe) { // IFRAME-only props
2573
2904
  el.frameborder = 0;
2574
2905
  el.src = "about:blank";
2906
+ //el.allowTransparency = true; - for IE, but breaks masking ability!
2575
2907
  css.opacity = 0;
2576
2908
  css.filter = "Alpha(Opacity='0')";
2577
2909
  css.border = 0;
@@ -2606,11 +2938,17 @@ $.fn.layout = function (opts) {
2606
2938
  *
2607
2939
  * @param {boolean=} [destroyChildren=false] Destory Child-Layouts first?
2608
2940
  */
2609
- , destroy = function (destroyChildren) {
2941
+ , destroy = function (evt_or_destroyChildren, destroyChildren) {
2610
2942
  // UNBIND layout events and remove global object
2611
2943
  $(window).unbind("."+ sID); // resize & unload
2612
2944
  $(document).unbind("."+ sID); // keyDown (hotkeys)
2613
2945
 
2946
+ if (typeof evt_or_destroyChildren === "object")
2947
+ // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility
2948
+ evtPane(evt_or_destroyChildren);
2949
+ else // no event, so transfer 1st param to destroyChildren param
2950
+ destroyChildren = evt_or_destroyChildren;
2951
+
2614
2952
  // need to look for parent layout BEFORE we remove the container data, else skips a level
2615
2953
  //var parentPane = Instance.hasParentLayout ? $.layout.getParentPaneInstance( $N ) : null;
2616
2954
 
@@ -2647,7 +2985,7 @@ $.fn.layout = function (opts) {
2647
2985
 
2648
2986
  // clear the Instance of everything except for container & options (so could recreate)
2649
2987
  // RE-CREATE: myLayout = myLayout.container.layout( myLayout.options );
2650
- for (n in Instance)
2988
+ for (var n in Instance)
2651
2989
  if (!n.match(/^(container|options)$/)) delete Instance[ n ];
2652
2990
  // add a 'destroyed' flag to make it easy to check
2653
2991
  Instance.destroyed = true;
@@ -2655,8 +2993,10 @@ $.fn.layout = function (opts) {
2655
2993
  // if this is a child layout, CLEAR the child-pointer in the parent
2656
2994
  /* for now the pointer REMAINS, but with only container, options and destroyed keys
2657
2995
  if (parentPane) {
2658
- var layout = parentPane.pane.data("parentLayout");
2659
- parentPane.child = layout.children[ parentPane.name ] = null;
2996
+ var layout = parentPane.pane.data("parentLayout")
2997
+ , key = layout.options.instanceKey || 'error';
2998
+ // THIS SYNTAX MAY BE WRONG!
2999
+ parentPane.children[key] = layout.children[ parentPane.name ].children[key] = null;
2660
3000
  }
2661
3001
  */
2662
3002
 
@@ -2667,9 +3007,10 @@ $.fn.layout = function (opts) {
2667
3007
  * Remove a pane from the layout - subroutine of destroy()
2668
3008
  *
2669
3009
  * @see destroy()
2670
- * @param {string} pane The pane to process
2671
- * @param {boolean=} [remove=false] Remove the DOM element?
2672
- * @param {boolean=} [skipResize=false] Skip calling resizeAll()?
3010
+ * @param {(string|Object)} evt_or_pane The pane to process
3011
+ * @param {boolean=} [remove=false] Remove the DOM element?
3012
+ * @param {boolean=} [skipResize=false] Skip calling resizeAll()?
3013
+ * @param {boolean=} [destroyChild=true] Destroy Child-layouts? If not passed, obeys options setting
2673
3014
  */
2674
3015
  , removePane = function (evt_or_pane, remove, skipResize, destroyChild) {
2675
3016
  if (!isInitialized()) return;
@@ -2679,7 +3020,6 @@ $.fn.layout = function (opts) {
2679
3020
  , $R = $Rs[pane]
2680
3021
  , $T = $Ts[pane]
2681
3022
  ;
2682
- //alert( '$P.length = '+ $P.length );
2683
3023
  // NOTE: elements can still exist even after remove()
2684
3024
  // so check for missing data(), which is cleared by removed()
2685
3025
  if ($P && $.isEmptyObject( $P.data() )) $P = false;
@@ -2689,24 +3029,32 @@ $.fn.layout = function (opts) {
2689
3029
 
2690
3030
  if ($P) $P.stop(true, true);
2691
3031
 
2692
- // check for a child layout
2693
3032
  var o = options[pane]
2694
3033
  , s = state[pane]
2695
3034
  , d = "layout"
2696
3035
  , css = "layoutCSS"
2697
- , child = children[pane] || ($P ? $P.data(d) : 0) || ($C ? $C.data(d) : 0) || null
2698
- , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildLayout
3036
+ , pC = children[pane]
3037
+ , hasChildren = $.isPlainObject( pC ) && !$.isEmptyObject( pC )
3038
+ , destroy = destroyChild !== undefined ? destroyChild : o.destroyChildren
2699
3039
  ;
2700
-
2701
3040
  // FIRST destroy the child-layout(s)
2702
- if (destroy && child && !child.destroyed) {
2703
- child.destroy(true); // tell child-layout to destroy ALL its child-layouts too
2704
- if (child.destroyed) // destroy was successful
2705
- child = null; // clear pointer for logic below
3041
+ if (hasChildren && destroy) {
3042
+ $.each( pC, function (key, child) {
3043
+ if (!child.destroyed)
3044
+ child.destroy(true);// tell child-layout to destroy ALL its child-layouts too
3045
+ if (child.destroyed) // destroy was successful
3046
+ delete pC[key];
3047
+ });
3048
+ // if no more children, remove the children hash
3049
+ if ($.isEmptyObject( pC )) {
3050
+ pC = children[pane] = null; // clear children hash
3051
+ hasChildren = false;
3052
+ }
2706
3053
  }
2707
3054
 
2708
- if ($P && remove && !child)
2709
- $P.remove();
3055
+ // Note: can't 'remove' a pane element with non-destroyed children
3056
+ if ($P && remove && !hasChildren)
3057
+ $P.remove(); // remove the pane-element and everything inside it
2710
3058
  else if ($P && $P[0]) {
2711
3059
  // create list of ALL pane-classes that need to be removed
2712
3060
  var root = o.paneClass // default="ui-layout-pane"
@@ -2732,10 +3080,12 @@ $.fn.layout = function (opts) {
2732
3080
  ;
2733
3081
  // do NOT reset CSS if this pane/content is STILL the container of a nested layout!
2734
3082
  // the nested layout will reset its 'container' CSS when/if it is destroyed
2735
- if ($C && $C.data(d)) {
3083
+ if (hasChildren && $C) {
2736
3084
  // a content-div may not have a specific width, so give it one to contain the Layout
2737
3085
  $C.width( $C.width() );
2738
- child.resizeAll(); // now resize the Layout
3086
+ $.each( pC, function (key, child) {
3087
+ child.resizeAll(); // resize the Layout
3088
+ });
2739
3089
  }
2740
3090
  else if ($C)
2741
3091
  $C.css( $C.data(css) ).removeData(css).removeData("layoutRole");
@@ -2749,7 +3099,7 @@ $.fn.layout = function (opts) {
2749
3099
  if ($R) $R.remove();
2750
3100
 
2751
3101
  // CLEAR all pointers and state data
2752
- Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = children[pane] = false;
3102
+ Instance[pane] = $Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = false;
2753
3103
  s = { removed: true };
2754
3104
 
2755
3105
  if (!skipResize)
@@ -2763,6 +3113,9 @@ $.fn.layout = function (opts) {
2763
3113
  * ###########################
2764
3114
  */
2765
3115
 
3116
+ /**
3117
+ * @param {string} pane
3118
+ */
2766
3119
  , _hidePane = function (pane) {
2767
3120
  var $P = $Ps[pane]
2768
3121
  , o = options[pane]
@@ -2777,6 +3130,9 @@ $.fn.layout = function (opts) {
2777
3130
  $P.hide().removeData(_c.offscreenReset);
2778
3131
  }
2779
3132
 
3133
+ /**
3134
+ * @param {string} pane
3135
+ */
2780
3136
  , _showPane = function (pane) {
2781
3137
  var $P = $Ps[pane]
2782
3138
  , o = options[pane]
@@ -2799,8 +3155,8 @@ $.fn.layout = function (opts) {
2799
3155
  * Completely 'hides' a pane, including its spacing - as if it does not exist
2800
3156
  * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
2801
3157
  *
2802
- * @param {string} pane The pane being hidden, ie: north, south, east, or west
2803
- * @param {boolean=} [noAnimation=false]
3158
+ * @param {(string|Object)} evt_or_pane The pane being hidden, ie: north, south, east, or west
3159
+ * @param {boolean=} [noAnimation=false]
2804
3160
  */
2805
3161
  , hide = function (evt_or_pane, noAnimation) {
2806
3162
  if (!isInitialized()) return;
@@ -2816,6 +3172,7 @@ $.fn.layout = function (opts) {
2816
3172
  if (state.initialized && false === _runCallbacks("onhide_start", pane)) return;
2817
3173
 
2818
3174
  s.isSliding = false; // just in case
3175
+ delete state.panesSliding[pane];
2819
3176
 
2820
3177
  // now hide the elements
2821
3178
  if ($R) $R.hide(); // hide resizer-bar
@@ -2838,10 +3195,10 @@ $.fn.layout = function (opts) {
2838
3195
  /**
2839
3196
  * Show a hidden pane - show as 'closed' by default unless openPane = true
2840
3197
  *
2841
- * @param {string} pane The pane being opened, ie: north, south, east, or west
2842
- * @param {boolean=} [openPane=false]
2843
- * @param {boolean=} [noAnimation=false]
2844
- * @param {boolean=} [noAlert=false]
3198
+ * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
3199
+ * @param {boolean=} [openPane=false]
3200
+ * @param {boolean=} [noAnimation=false]
3201
+ * @param {boolean=} [noAlert=false]
2845
3202
  */
2846
3203
  , show = function (evt_or_pane, openPane, noAnimation, noAlert) {
2847
3204
  if (!isInitialized()) return;
@@ -2856,9 +3213,10 @@ $.fn.layout = function (opts) {
2856
3213
  // onshow_start callback - will CANCEL show if returns false
2857
3214
  if (false === _runCallbacks("onshow_start", pane)) return;
2858
3215
 
2859
- s.isSliding = false; // just in case
2860
3216
  s.isShowing = true; // used by onopen/onclose
2861
3217
  //s.isHidden = false; - will be set by open/close - if not cancelled
3218
+ s.isSliding = false; // just in case
3219
+ delete state.panesSliding[pane];
2862
3220
 
2863
3221
  // now show the elements
2864
3222
  //if ($R) $R.show(); - will be shown by open/close
@@ -2872,8 +3230,8 @@ $.fn.layout = function (opts) {
2872
3230
  /**
2873
3231
  * Toggles a pane open/closed by calling either open or close
2874
3232
  *
2875
- * @param {string} pane The pane being toggled, ie: north, south, east, or west
2876
- * @param {boolean=} [slide=false]
3233
+ * @param {(string|Object)} evt_or_pane The pane being toggled, ie: north, south, east, or west
3234
+ * @param {boolean=} [slide=false]
2877
3235
  */
2878
3236
  , toggle = function (evt_or_pane, slide) {
2879
3237
  if (!isInitialized()) return;
@@ -2906,22 +3264,22 @@ $.fn.layout = function (opts) {
2906
3264
  _hidePane(pane);
2907
3265
  s.isClosed = true;
2908
3266
  s.isVisible = false;
2909
- // UNUSED: if (setHandles) setAsClosed(pane, true); // true = force
3267
+ if (setHandles) setAsClosed(pane);
2910
3268
  }
2911
3269
 
2912
3270
  /**
2913
3271
  * Close the specified pane (animation optional), and resize all other panes as needed
2914
3272
  *
2915
- * @param {string} pane The pane being closed, ie: north, south, east, or west
2916
- * @param {boolean=} [force=false]
2917
- * @param {boolean=} [noAnimation=false]
2918
- * @param {boolean=} [skipCallback=false]
3273
+ * @param {(string|Object)} evt_or_pane The pane being closed, ie: north, south, east, or west
3274
+ * @param {boolean=} [force=false]
3275
+ * @param {boolean=} [noAnimation=false]
3276
+ * @param {boolean=} [skipCallback=false]
2919
3277
  */
2920
3278
  , close = function (evt_or_pane, force, noAnimation, skipCallback) {
2921
3279
  var pane = evtPane.call(this, evt_or_pane);
2922
3280
  // if pane has been initialized, but NOT the complete layout, close pane instantly
2923
3281
  if (!state.initialized && $Ps[pane]) {
2924
- _closePane(pane); // INIT pane as closed
3282
+ _closePane(pane, true); // INIT pane as closed
2925
3283
  return;
2926
3284
  }
2927
3285
  if (!isInitialized()) return;
@@ -2937,7 +3295,7 @@ $.fn.layout = function (opts) {
2937
3295
 
2938
3296
  // QUEUE in case another action/animation is in progress
2939
3297
  $N.queue(function( queueNext ){
2940
-
3298
+
2941
3299
  if ( !$P
2942
3300
  || (!o.closable && !s.isShowing && !s.isHiding) // invalid request // (!o.resizable && !o.closable) ???
2943
3301
  || (!force && s.isClosed && !s.isShowing) // already closed
@@ -2975,9 +3333,6 @@ $.fn.layout = function (opts) {
2975
3333
 
2976
3334
  // CLOSE THE PANE
2977
3335
  if (doFX) { // animate the close
2978
- // mask panes with objects
2979
- var masks = "center"+ (c.dir=="horz" ? ",west,east" : "");
2980
- showMasks( masks, true ); // true = ONLY mask panes with maskObjects=true
2981
3336
  lockPaneForFX(pane, true); // need to set left/top so animation will work
2982
3337
  $P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
2983
3338
  lockPaneForFX(pane, false); // undo
@@ -2995,7 +3350,7 @@ $.fn.layout = function (opts) {
2995
3350
  // SUBROUTINE
2996
3351
  function close_2 () {
2997
3352
  s.isMoving = false;
2998
- bindStartSlidingEvent(pane, true); // will enable if o.slidable = true
3353
+ bindStartSlidingEvents(pane, true); // will enable if o.slidable = true
2999
3354
 
3000
3355
  // if opposite-pane was autoClosed, see if it can be autoOpened now
3001
3356
  var altPane = _c.oppositeEdge[pane];
@@ -3004,9 +3359,6 @@ $.fn.layout = function (opts) {
3004
3359
  makePaneFit( altPane );
3005
3360
  }
3006
3361
 
3007
- // hide any masks shown while closing
3008
- hideMasks();
3009
-
3010
3362
  if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) {
3011
3363
  // onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
3012
3364
  if (!isShowing) _runCallbacks("onclose_end", pane);
@@ -3021,14 +3373,14 @@ $.fn.layout = function (opts) {
3021
3373
  * @param {string} pane The pane just closed, ie: north, south, east, or west
3022
3374
  */
3023
3375
  , setAsClosed = function (pane) {
3376
+ if (!$Rs[pane]) return; // handles not initialized yet!
3024
3377
  var
3025
3378
  $P = $Ps[pane]
3026
3379
  , $R = $Rs[pane]
3027
3380
  , $T = $Ts[pane]
3028
3381
  , o = options[pane]
3029
3382
  , s = state[pane]
3030
- , side = _c[pane].side.toLowerCase()
3031
- , inset = "inset"+ _c[pane].side
3383
+ , side = _c[pane].side
3032
3384
  , rClass = o.resizerClass
3033
3385
  , tClass = o.togglerClass
3034
3386
  , _pane = "-"+ pane // used for classNames
@@ -3037,13 +3389,12 @@ $.fn.layout = function (opts) {
3037
3389
  , _closed = "-closed"
3038
3390
  ;
3039
3391
  $R
3040
- .css(side, sC[inset]) // move the resizer
3392
+ .css(side, sC.inset[side]) // move the resizer
3041
3393
  .removeClass( rClass+_open +" "+ rClass+_pane+_open )
3042
3394
  .removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
3043
3395
  .addClass( rClass+_closed +" "+ rClass+_pane+_closed )
3044
- .unbind("dblclick."+ sID)
3045
3396
  ;
3046
- // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent?
3397
+ // DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvents?
3047
3398
  if (o.resizable && $.layout.plugins.draggable)
3048
3399
  $R
3049
3400
  .draggable("disable")
@@ -3057,7 +3408,7 @@ $.fn.layout = function (opts) {
3057
3408
  $T
3058
3409
  .removeClass( tClass+_open +" "+ tClass+_pane+_open )
3059
3410
  .addClass( tClass+_closed +" "+ tClass+_pane+_closed )
3060
- .attr("title", o.togglerTip_closed) // may be blank
3411
+ .attr("title", o.tips.Open) // may be blank
3061
3412
  ;
3062
3413
  // toggler-content - if exists
3063
3414
  $T.children(".content-open").hide();
@@ -3076,10 +3427,10 @@ $.fn.layout = function (opts) {
3076
3427
  /**
3077
3428
  * Open the specified pane (animation optional), and resize all other panes as needed
3078
3429
  *
3079
- * @param {string} pane The pane being opened, ie: north, south, east, or west
3080
- * @param {boolean=} [slide=false]
3081
- * @param {boolean=} [noAnimation=false]
3082
- * @param {boolean=} [noAlert=false]
3430
+ * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
3431
+ * @param {boolean=} [slide=false]
3432
+ * @param {boolean=} [noAnimation=false]
3433
+ * @param {boolean=} [noAlert=false]
3083
3434
  */
3084
3435
  , open = function (evt_or_pane, slide, noAnimation, noAlert) {
3085
3436
  if (!isInitialized()) return;
@@ -3107,8 +3458,8 @@ $.fn.layout = function (opts) {
3107
3458
  return;
3108
3459
  }
3109
3460
 
3110
- if (o.autoResize && s.size != o.size) // resize pane to original size set in options
3111
- sizePane(pane, o.size, true, true, true); // true=skipCallback/forceResize/noAnimation
3461
+ if (s.autoResize && s.size != o.size) // resize pane to original size set in options
3462
+ sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize
3112
3463
  else
3113
3464
  // make sure there is enough space available to open the pane
3114
3465
  setSizeLimits(pane, slide);
@@ -3125,8 +3476,8 @@ $.fn.layout = function (opts) {
3125
3476
 
3126
3477
  if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN!
3127
3478
  syncPinBtns(pane, false); // make sure pin-buttons are reset
3128
- if (!noAlert && o.noRoomToOpenTip)
3129
- alert(o.noRoomToOpenTip);
3479
+ if (!noAlert && o.tips.noRoomToOpen)
3480
+ alert(o.tips.noRoomToOpen);
3130
3481
  return queueNext(); // ABORT
3131
3482
  }
3132
3483
 
@@ -3135,7 +3486,7 @@ $.fn.layout = function (opts) {
3135
3486
  else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead
3136
3487
  bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false
3137
3488
  else if (o.slidable)
3138
- bindStartSlidingEvent(pane, false); // UNBIND trigger events
3489
+ bindStartSlidingEvents(pane, false); // UNBIND trigger events
3139
3490
 
3140
3491
  s.noRoom = false; // will be reset by makePaneFit if 'noRoom'
3141
3492
  makePaneFit(pane);
@@ -3153,12 +3504,9 @@ $.fn.layout = function (opts) {
3153
3504
  if (isShowing) s.isHidden = false;
3154
3505
 
3155
3506
  if (doFX) { // ANIMATE
3156
- // mask panes with objects
3157
- var masks = "center"+ (c.dir=="horz" ? ",west,east" : "");
3158
- if (s.isSliding) masks += ","+ _c.oppositeEdge[pane];
3159
- showMasks( masks, true ); // true = ONLY mask panes with maskObjects=true
3507
+ // mask adjacent panes with objects
3160
3508
  lockPaneForFX(pane, true); // need to set left/top so animation will work
3161
- $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
3509
+ $P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
3162
3510
  lockPaneForFX(pane, false); // undo
3163
3511
  if (s.isVisible) open_2(); // continue
3164
3512
  queueNext();
@@ -3180,7 +3528,6 @@ $.fn.layout = function (opts) {
3180
3528
 
3181
3529
  // NOTE: if isSliding, then other panes are NOT 'resized'
3182
3530
  if (!s.isSliding) { // resize all panes adjacent to this one
3183
- hideMasks(); // remove any masks shown while opening
3184
3531
  sizeMidPanes(_c[pane].dir=="vert" ? "center" : "", false); // false = NOT skipCallback
3185
3532
  }
3186
3533
 
@@ -3201,8 +3548,7 @@ $.fn.layout = function (opts) {
3201
3548
  , $T = $Ts[pane]
3202
3549
  , o = options[pane]
3203
3550
  , s = state[pane]
3204
- , side = _c[pane].side.toLowerCase()
3205
- , inset = "inset"+ _c[pane].side
3551
+ , side = _c[pane].side
3206
3552
  , rClass = o.resizerClass
3207
3553
  , tClass = o.togglerClass
3208
3554
  , _pane = "-"+ pane // used for classNames
@@ -3211,7 +3557,7 @@ $.fn.layout = function (opts) {
3211
3557
  , _sliding= "-sliding"
3212
3558
  ;
3213
3559
  $R
3214
- .css(side, sC[inset] + getPaneSize(pane)) // move the resizer
3560
+ .css(side, sC.inset[side] + getPaneSize(pane)) // move the resizer
3215
3561
  .removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
3216
3562
  .addClass( rClass+_open +" "+ rClass+_pane+_open )
3217
3563
  ;
@@ -3220,13 +3566,11 @@ $.fn.layout = function (opts) {
3220
3566
  else // in case 'was sliding'
3221
3567
  $R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
3222
3568
 
3223
- if (o.resizerDblClickToggle)
3224
- $R.bind("dblclick", toggle );
3225
3569
  removeHover( 0, $R ); // remove hover classes
3226
3570
  if (o.resizable && $.layout.plugins.draggable)
3227
3571
  $R .draggable("enable")
3228
3572
  .css("cursor", o.resizerCursor)
3229
- .attr("title", o.resizerTip);
3573
+ .attr("title", o.tips.Resize);
3230
3574
  else if (!s.isSliding)
3231
3575
  $R.css("cursor", "default"); // n-resize, s-resize, etc
3232
3576
 
@@ -3234,7 +3578,7 @@ $.fn.layout = function (opts) {
3234
3578
  if ($T) {
3235
3579
  $T .removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
3236
3580
  .addClass( tClass+_open +" "+ tClass+_pane+_open )
3237
- .attr("title", o.togglerTip_open); // may be blank
3581
+ .attr("title", o.tips.Close); // may be blank
3238
3582
  removeHover( 0, $T ); // remove hover classes
3239
3583
  // toggler-content - if exists
3240
3584
  $T.children(".content-closed").hide();
@@ -3335,7 +3679,7 @@ $.fn.layout = function (opts) {
3335
3679
  }
3336
3680
 
3337
3681
  /**
3338
- * @param {string} pane The pane being opened, ie: north, south, east, or west
3682
+ * @param {(string|Object)} evt_or_pane The pane being opened, ie: north, south, east, or west
3339
3683
  */
3340
3684
  , slideToggle = function (evt_or_pane) {
3341
3685
  var pane = evtPane.call(this, evt_or_pane);
@@ -3356,14 +3700,15 @@ $.fn.layout = function (opts) {
3356
3700
  , z = options.zIndexes
3357
3701
  ;
3358
3702
  if (doLock) {
3703
+ showMasks( pane, { animation: true, objectsOnly: true });
3359
3704
  $P.css({ zIndex: z.pane_animate }); // overlay all elements during animation
3360
3705
  if (pane=="south")
3361
- $P.css({ top: sC.insetTop + sC.innerHeight - $P.outerHeight() });
3706
+ $P.css({ top: sC.inset.top + sC.innerHeight - $P.outerHeight() });
3362
3707
  else if (pane=="east")
3363
- $P.css({ left: sC.insetLeft + sC.innerWidth - $P.outerWidth() });
3708
+ $P.css({ left: sC.inset.left + sC.innerWidth - $P.outerWidth() });
3364
3709
  }
3365
3710
  else { // animation DONE - RESET CSS
3366
- // TODO: see if this can be deleted. It causes a quick-close when sliding in Chrome
3711
+ hideMasks();
3367
3712
  $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
3368
3713
  if (pane=="south")
3369
3714
  $P.css({ top: "auto" });
@@ -3384,7 +3729,7 @@ $.fn.layout = function (opts) {
3384
3729
  * @param {string} pane The pane to enable/disable, 'north', 'south', etc.
3385
3730
  * @param {boolean} enable Enable or Disable sliding?
3386
3731
  */
3387
- , bindStartSlidingEvent = function (pane, enable) {
3732
+ , bindStartSlidingEvents = function (pane, enable) {
3388
3733
  var o = options[pane]
3389
3734
  , $P = $Ps[pane]
3390
3735
  , $R = $Rs[pane]
@@ -3395,22 +3740,27 @@ $.fn.layout = function (opts) {
3395
3740
  // make sure we have a valid event
3396
3741
  if (evtName.match(/mouseover/))
3397
3742
  evtName = o.slideTrigger_open = "mouseenter";
3398
- else if (!evtName.match(/click|dblclick|mouseenter/))
3743
+ else if (!evtName.match(/(click|dblclick|mouseenter)/))
3399
3744
  evtName = o.slideTrigger_open = "click";
3400
3745
 
3746
+ // must remove double-click-toggle when using dblclick-slide
3747
+ if (o.resizerDblClickToggle && evtName.match(/click/)) {
3748
+ $R[enable ? "unbind" : "bind"]('dblclick.'+ sID, toggle)
3749
+ }
3750
+
3401
3751
  $R
3402
3752
  // add or remove event
3403
3753
  [enable ? "bind" : "unbind"](evtName +'.'+ sID, slideOpen)
3404
3754
  // set the appropriate cursor & title/tip
3405
3755
  .css("cursor", enable ? o.sliderCursor : "default")
3406
- .attr("title", enable ? o.sliderTip : "")
3756
+ .attr("title", enable ? o.tips.Slide : "")
3407
3757
  ;
3408
3758
  }
3409
3759
 
3410
3760
  /**
3411
3761
  * Add or remove 'mouseleave' events to 'slide close' when pane is 'sliding' open or closed
3412
3762
  * Also increases zIndex when pane is sliding open
3413
- * See bindStartSlidingEvent for code to control 'slide open'
3763
+ * See bindStartSlidingEvents for code to control 'slide open'
3414
3764
  *
3415
3765
  * @see slideOpen(), slideClose()
3416
3766
  * @param {string} pane The pane to process, 'north', 'south', etc.
@@ -3426,19 +3776,26 @@ $.fn.layout = function (opts) {
3426
3776
  , $P = $Ps[pane]
3427
3777
  , $R = $Rs[pane]
3428
3778
  ;
3429
- s.isSliding = enable; // logic
3430
3779
  timer.clear(pane+"_closeSlider"); // just in case
3431
3780
 
3432
- // remove 'slideOpen' event from resizer
3433
- // ALSO will raise the zIndex of the pane & resizer
3434
- if (enable) bindStartSlidingEvent(pane, false);
3781
+ if (enable) {
3782
+ s.isSliding = true;
3783
+ state.panesSliding[pane] = true;
3784
+ // remove 'slideOpen' event from resizer
3785
+ // ALSO will raise the zIndex of the pane & resizer
3786
+ bindStartSlidingEvents(pane, false);
3787
+ }
3788
+ else {
3789
+ s.isSliding = false;
3790
+ delete state.panesSliding[pane];
3791
+ }
3435
3792
 
3436
3793
  // RE/SET zIndex - increases when pane is sliding-open, resets to normal when not
3437
3794
  $P.css("zIndex", enable ? z.pane_sliding : z.pane_normal);
3438
3795
  $R.css("zIndex", enable ? z.pane_sliding+2 : z.resizer_normal); // NOTE: mask = pane_sliding+1
3439
3796
 
3440
3797
  // make sure we have a valid event
3441
- if (!evtName.match(/click|mouseleave/))
3798
+ if (!evtName.match(/(click|mouseleave)/))
3442
3799
  evtName = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout'
3443
3800
 
3444
3801
  // add/remove slide triggers
@@ -3458,7 +3815,7 @@ $.fn.layout = function (opts) {
3458
3815
  // IF pane is not resizable (which already has a cursor and tip)
3459
3816
  // then set the a cursor & title/tip on resizer when sliding
3460
3817
  $R.css("cursor", enable ? o.sliderCursor : "default");
3461
- $R.attr("title", enable ? o.togglerTip_open : ""); // use Toggler-tip, eg: "Close Pane"
3818
+ $R.attr("title", enable ? o.tips.Close : ""); // use Toggler-tip, eg: "Close Pane"
3462
3819
  }
3463
3820
 
3464
3821
  // SUBROUTINE for mouseleave timer clearing
@@ -3479,8 +3836,7 @@ $.fn.layout = function (opts) {
3479
3836
  * @param {boolean=} [force=false]
3480
3837
  */
3481
3838
  , makePaneFit = function (pane, isOpening, skipCallback, force) {
3482
- var
3483
- o = options[pane]
3839
+ var o = options[pane]
3484
3840
  , s = state[pane]
3485
3841
  , c = _c[pane]
3486
3842
  , $P = $Ps[pane]
@@ -3516,17 +3872,15 @@ $.fn.layout = function (opts) {
3516
3872
  else if (s.minSize <= s.maxSize) { // pane CAN fit
3517
3873
  hasRoom = true;
3518
3874
  if (s.size > s.maxSize) // pane is too big - shrink it
3519
- sizePane(pane, s.maxSize, skipCallback, force, true); // true = noAnimation
3875
+ sizePane(pane, s.maxSize, skipCallback, true, force); // true = noAnimation
3520
3876
  else if (s.size < s.minSize) // pane is too small - enlarge it
3521
- sizePane(pane, s.minSize, skipCallback, force, true);
3877
+ sizePane(pane, s.minSize, skipCallback, true, force); // true = noAnimation
3522
3878
  // need s.isVisible because new pseudoClose method keeps pane visible, but off-screen
3523
3879
  else if ($R && s.isVisible && $P.is(":visible")) {
3524
3880
  // make sure resizer-bar is positioned correctly
3525
3881
  // handles situation where nested layout was 'hidden' when initialized
3526
- var side = c.side.toLowerCase()
3527
- , pos = s.size + sC["inset"+ c.side]
3528
- ;
3529
- if ($.layout.cssNum($R, side) != pos) $R.css( side, pos );
3882
+ var pos = s.size + sC.inset[c.side];
3883
+ if ($.layout.cssNum( $R, c.side ) != pos) $R.css( c.side, pos );
3530
3884
  }
3531
3885
 
3532
3886
  // if was previously hidden due to noRoom, then RESET because NOW there is room
@@ -3557,46 +3911,46 @@ $.fn.layout = function (opts) {
3557
3911
 
3558
3912
 
3559
3913
  /**
3560
- * sizePane / manualSizePane
3561
- * sizePane is called only by internal methods whenever a pane needs to be resized
3562
3914
  * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized'
3563
3915
  *
3564
- * @param {string} pane The pane being resized
3565
- * @param {number} size The *desired* new size for this pane - will be validated
3566
- * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
3567
- * @param {boolean=} [noAnimation=false]
3916
+ * @param {(string|Object)} evt_or_pane The pane being resized
3917
+ * @param {number} size The *desired* new size for this pane - will be validated
3918
+ * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
3919
+ * @param {boolean=} [noAnimation=false]
3920
+ * @param {boolean=} [force=false] Force resizing even if does not seem necessary
3568
3921
  */
3569
- , manualSizePane = function (evt_or_pane, size, skipCallback, noAnimation) {
3922
+ , manualSizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) {
3570
3923
  if (!isInitialized()) return;
3571
3924
  var pane = evtPane.call(this, evt_or_pane)
3572
3925
  , o = options[pane]
3573
3926
  , s = state[pane]
3574
3927
  // if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete...
3575
- , forceResize = o.livePaneResizing && !s.isResizing
3928
+ , forceResize = force || (o.livePaneResizing && !s.isResizing)
3576
3929
  ;
3577
3930
  // ANY call to manualSizePane disables autoResize - ie, percentage sizing
3578
- o.autoResize = false;
3931
+ s.autoResize = false;
3579
3932
  // flow-through...
3580
- sizePane(pane, size, skipCallback, forceResize, noAnimation); // will animate resize if option enabled
3933
+ sizePane(pane, size, skipCallback, noAnimation, forceResize); // will animate resize if option enabled
3581
3934
  }
3582
3935
 
3583
3936
  /**
3584
- * @param {string} pane The pane being resized
3585
- * @param {number} size The *desired* new size for this pane - will be validated
3586
- * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
3587
- * @param {boolean=} [force=false] Force resizing even if does not seem necessary
3588
- * @param {boolean=} [noAnimation=false]
3937
+ * sizePane is called only by internal methods whenever a pane needs to be resized
3938
+ *
3939
+ * @param {(string|Object)} evt_or_pane The pane being resized
3940
+ * @param {number} size The *desired* new size for this pane - will be validated
3941
+ * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
3942
+ * @param {boolean=} [noAnimation=false]
3943
+ * @param {boolean=} [force=false] Force resizing even if does not seem necessary
3589
3944
  */
3590
- , sizePane = function (evt_or_pane, size, skipCallback, force, noAnimation) {
3945
+ , sizePane = function (evt_or_pane, size, skipCallback, noAnimation, force) {
3591
3946
  if (!isInitialized()) return;
3592
3947
  var pane = evtPane.call(this, evt_or_pane) // probably NEVER called from event?
3593
3948
  , o = options[pane]
3594
3949
  , s = state[pane]
3595
3950
  , $P = $Ps[pane]
3596
3951
  , $R = $Rs[pane]
3597
- , side = _c[pane].side.toLowerCase()
3952
+ , side = _c[pane].side
3598
3953
  , dimName = _c[pane].sizeType.toLowerCase()
3599
- , inset = "inset"+ _c[pane].side
3600
3954
  , skipResizeWhileDragging = s.isResizing && !o.triggerEventsDuringLiveResize
3601
3955
  , doFX = noAnimation !== true && o.animatePaneSizing
3602
3956
  , oldSize, newSize
@@ -3619,6 +3973,8 @@ $.fn.layout = function (opts) {
3619
3973
  if (!force && size === oldSize)
3620
3974
  return queueNext();
3621
3975
 
3976
+ s.newSize = size;
3977
+
3622
3978
  // onresize_start callback CANNOT cancel resizing because this would break the layout!
3623
3979
  if (!skipCallback && state.initialized && s.isVisible)
3624
3980
  _runCallbacks("onresize_start", pane);
@@ -3639,12 +3995,14 @@ $.fn.layout = function (opts) {
3639
3995
  // reset zIndex after animation
3640
3996
  $P.css({ zIndex: (s.isSliding ? z.pane_sliding : z.pane_normal) });
3641
3997
  s.isMoving = false;
3998
+ delete s.newSize;
3642
3999
  sizePane_2(); // continue
3643
4000
  queueNext();
3644
4001
  });
3645
4002
  }
3646
4003
  else { // no animation
3647
4004
  $P.css( dimName, newSize ); // resize pane
4005
+ delete s.newSize;
3648
4006
  // if pane is visible, then
3649
4007
  if ($P.is(":visible"))
3650
4008
  sizePane_2(); // continue
@@ -3675,6 +4033,7 @@ $.fn.layout = function (opts) {
3675
4033
  , cssSize: newSize
3676
4034
  }]
3677
4035
  , lastTry = tries[0]
4036
+ , thisTry = {}
3678
4037
  , msg = 'Inaccurate size after resizing the '+ pane +'-pane.'
3679
4038
  ;
3680
4039
  while ( !lastTry.correct ) {
@@ -3691,15 +4050,12 @@ $.fn.layout = function (opts) {
3691
4050
  thisTry.actual = dimName=='width' ? $P.outerWidth() : $P.outerHeight();
3692
4051
  thisTry.correct = (size === thisTry.actual);
3693
4052
 
3694
- // if showDebugMessages, log attempts and alert the user of this *non-fatal error*
3695
- if (options.showDebugMessages) {
3696
- if ( tries.length === 1) {
3697
- _log(msg, false);
3698
- _log(lastTry, false);
3699
- }
3700
- _log(thisTry, false);
4053
+ // log attempts and alert the user of this *non-fatal error* (if showDebugMessages)
4054
+ if ( tries.length === 1) {
4055
+ _log(msg, false, true);
4056
+ _log(lastTry, false, true);
3701
4057
  }
3702
-
4058
+ _log(thisTry, false, true);
3703
4059
  // after 4 tries, is as close as its gonna get!
3704
4060
  if (tries.length > 3) break;
3705
4061
 
@@ -3714,7 +4070,7 @@ $.fn.layout = function (opts) {
3714
4070
 
3715
4071
  if (s.isVisible && $P.is(":visible")) {
3716
4072
  // reposition the resizer-bar
3717
- if ($R) $R.css( side, size + sC[inset] );
4073
+ if ($R) $R.css( side, size + sC.inset[side] );
3718
4074
  // resize the content-div
3719
4075
  sizeContent(pane);
3720
4076
  }
@@ -3738,16 +4094,16 @@ $.fn.layout = function (opts) {
3738
4094
  }
3739
4095
 
3740
4096
  // DEBUG - ALERT user/developer so they know there was a sizing problem
3741
- if (options.showDebugMessages && tries.length > 1)
3742
- _log(msg +'\nSee the Error Console for details.', true);
4097
+ if (tries.length > 1)
4098
+ _log(msg +'\nSee the Error Console for details.', true, true);
3743
4099
  }
3744
4100
  }
3745
4101
 
3746
4102
  /**
3747
- * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide()
3748
- * @param {string} panes The pane(s) being resized, comma-delmited string
3749
- * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
3750
- * @param {boolean=} [force=false]
4103
+ * @see initPanes(), sizePane(), resizeAll(), open(), close(), hide()
4104
+ * @param {(Array.<string>|string)} panes The pane(s) being resized, comma-delmited string
4105
+ * @param {boolean=} [skipCallback=false] Should the onresize callback be run?
4106
+ * @param {boolean=} [force=false]
3751
4107
  */
3752
4108
  , sizeMidPanes = function (panes, skipCallback, force) {
3753
4109
  panes = (panes ? panes : "east,west,center").split(",");
@@ -3762,27 +4118,36 @@ $.fn.layout = function (opts) {
3762
4118
  , isCenter= (pane=="center")
3763
4119
  , hasRoom = true
3764
4120
  , CSS = {}
4121
+ // if pane is not visible, show it invisibly NOW rather than for *each call* in this script
4122
+ , visCSS = $.layout.showInvisibly($P)
4123
+
3765
4124
  , newCenter = calcNewCenterPaneDims()
3766
4125
  ;
4126
+
3767
4127
  // update pane-state dimensions
3768
4128
  $.extend(s, elDims($P));
3769
4129
 
3770
4130
  if (pane === "center") {
3771
- if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight)
4131
+ if (!force && s.isVisible && newCenter.width === s.outerWidth && newCenter.height === s.outerHeight) {
4132
+ $P.css(visCSS);
3772
4133
  return true; // SKIP - pane already the correct size
4134
+ }
3773
4135
  // set state for makePaneFit() logic
3774
4136
  $.extend(s, cssMinDims(pane), {
3775
4137
  maxWidth: newCenter.width
3776
4138
  , maxHeight: newCenter.height
3777
4139
  });
3778
4140
  CSS = newCenter;
4141
+ s.newWidth = CSS.width;
4142
+ s.newHeight = CSS.height;
3779
4143
  // convert OUTER width/height to CSS width/height
3780
4144
  CSS.width = cssW($P, CSS.width);
3781
4145
  // NEW - allow pane to extend 'below' visible area rather than hide it
3782
4146
  CSS.height = cssH($P, CSS.height);
3783
4147
  hasRoom = CSS.width >= 0 && CSS.height >= 0; // height >= 0 = ALWAYS TRUE NOW
4148
+
3784
4149
  // during layout init, try to shrink east/west panes to make room for center
3785
- if (!state.initialized && o.minWidth > s.outerWidth) {
4150
+ if (!state.initialized && o.minWidth > newCenter.width) {
3786
4151
  var
3787
4152
  reqPx = o.minWidth - s.outerWidth
3788
4153
  , minE = options.east.minSize || 0
@@ -3802,12 +4167,13 @@ $.fn.layout = function (opts) {
3802
4167
  }
3803
4168
  // IF we found enough extra space, then resize the border panes as calculated
3804
4169
  if (reqPx === 0) {
3805
- if (sizeE != minE)
3806
- sizePane('east', newE, true, force, true); // true = skipCallback/noAnimation - initPanes will handle when done
3807
- if (sizeW != minW)
3808
- sizePane('west', newW, true, force, true);
4170
+ if (sizeE && sizeE != minE)
4171
+ sizePane('east', newE, true, true, force); // true = skipCallback/noAnimation - initPanes will handle when done
4172
+ if (sizeW && sizeW != minW)
4173
+ sizePane('west', newW, true, true, force); // true = skipCallback/noAnimation
3809
4174
  // now start over!
3810
4175
  sizeMidPanes('center', skipCallback, force);
4176
+ $P.css(visCSS);
3811
4177
  return; // abort this loop
3812
4178
  }
3813
4179
  }
@@ -3816,11 +4182,14 @@ $.fn.layout = function (opts) {
3816
4182
  // set state.min/maxWidth/Height for makePaneFit() logic
3817
4183
  if (s.isVisible && !s.noVerticalRoom)
3818
4184
  $.extend(s, elDims($P), cssMinDims(pane))
3819
- if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight)
4185
+ if (!force && !s.noVerticalRoom && newCenter.height === s.outerHeight) {
4186
+ $P.css(visCSS);
3820
4187
  return true; // SKIP - pane already the correct size
4188
+ }
3821
4189
  // east/west have same top, bottom & height as center
3822
4190
  CSS.top = newCenter.top;
3823
4191
  CSS.bottom = newCenter.bottom;
4192
+ s.newSize = newCenter.height
3824
4193
  // NEW - allow pane to extend 'below' visible area rather than hide it
3825
4194
  CSS.height = cssH($P, newCenter.height);
3826
4195
  s.maxHeight = CSS.height;
@@ -3834,7 +4203,8 @@ $.fn.layout = function (opts) {
3834
4203
  _runCallbacks("onresize_start", pane);
3835
4204
 
3836
4205
  $P.css(CSS); // apply the CSS to pane
3837
- sizeHandles(pane); // also update resizer length
4206
+ if (pane !== "center")
4207
+ sizeHandles(pane); // also update resizer length
3838
4208
  if (s.noRoom && !s.isClosed && !s.isHidden)
3839
4209
  makePaneFit(pane); // will re-open/show auto-closed/hidden pane
3840
4210
  if (s.isVisible) {
@@ -3845,6 +4215,13 @@ $.fn.layout = function (opts) {
3845
4215
  else if (!s.noRoom && s.isVisible) // no room for pane
3846
4216
  makePaneFit(pane); // will hide or close pane
3847
4217
 
4218
+ // reset visibility, if necessary
4219
+ $P.css(visCSS);
4220
+
4221
+ delete s.newSize;
4222
+ delete s.newWidth;
4223
+ delete s.newHeight;
4224
+
3848
4225
  if (!s.isVisible)
3849
4226
  return true; // DONE - next pane
3850
4227
 
@@ -3852,10 +4229,11 @@ $.fn.layout = function (opts) {
3852
4229
  * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
3853
4230
  * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
3854
4231
  * ALSO required when pane is an IFRAME because will NOT default to 'full width'
4232
+ * TODO: Can I use width:100% for a north/south iframe?
4233
+ * TODO: Sounds like a job for $P.outerWidth( sC.innerWidth ) SETTER METHOD
3855
4234
  */
3856
4235
  if (pane === "center") { // finished processing midPanes
3857
- var b = $.layout.browser;
3858
- var fix = b.isIE6 || (b.msie && !$.support.boxModel);
4236
+ var fix = browser.isIE6 || !browser.boxModel;
3859
4237
  if ($Ps.north && (fix || state.north.tagName=="IFRAME"))
3860
4238
  $Ps.north.css("width", cssW($Ps.north, sC.innerWidth));
3861
4239
  if ($Ps.south && (fix || state.south.tagName=="IFRAME"))
@@ -3871,20 +4249,36 @@ $.fn.layout = function (opts) {
3871
4249
 
3872
4250
  /**
3873
4251
  * @see window.onresize(), callbacks or custom code
4252
+ * @param {(Object|boolean)=} evt_or_refresh If 'true', then also reset pane-positioning
3874
4253
  */
3875
- , resizeAll = function () {
3876
- if (!state.initialized) {
3877
- _initLayoutElements();
3878
- return; // no need to resize since we just initialized!
3879
- }
4254
+ , resizeAll = function (evt_or_refresh) {
3880
4255
  var oldW = sC.innerWidth
3881
4256
  , oldH = sC.innerHeight
3882
4257
  ;
4258
+ // stopPropagation if called by trigger("layoutdestroy") - use evtPane utility
4259
+ evtPane(evt_or_refresh);
4260
+
3883
4261
  // cannot size layout when 'container' is hidden or collapsed
3884
- if (!$N.is(":visible:") ) return;
3885
- $.extend( state.container, elDims( $N ) ); // UPDATE container dimensions
4262
+ if (!$N.is(":visible")) return;
4263
+
4264
+ if (!state.initialized) {
4265
+ _initLayoutElements();
4266
+ return; // no need to resize since we just initialized!
4267
+ }
4268
+
4269
+ if (evt_or_refresh === true && $.isPlainObject(options.outset)) {
4270
+ // update container CSS in case outset option has changed
4271
+ $N.css( options.outset );
4272
+ }
4273
+ // UPDATE container dimensions
4274
+ $.extend(sC, elDims( $N, options.inset ));
3886
4275
  if (!sC.outerHeight) return;
3887
4276
 
4277
+ // if 'true' passed, refresh pane & handle positioning too
4278
+ if (evt_or_refresh === true) {
4279
+ setPanePosition();
4280
+ }
4281
+
3888
4282
  // onresizeall_start will CANCEL resizing if returns false
3889
4283
  // state.container has already been set, so user can access this info for calcuations
3890
4284
  if (false === _runCallbacks("onresizeall_start")) return false;
@@ -3892,28 +4286,25 @@ $.fn.layout = function (opts) {
3892
4286
  var // see if container is now 'smaller' than before
3893
4287
  shrunkH = (sC.innerHeight < oldH)
3894
4288
  , shrunkW = (sC.innerWidth < oldW)
3895
- , $P, o, s, dir
4289
+ , $P, o, s
3896
4290
  ;
3897
4291
  // NOTE special order for sizing: S-N-E-W
3898
4292
  $.each(["south","north","east","west"], function (i, pane) {
3899
4293
  if (!$Ps[pane]) return; // no pane - SKIP
3900
- s = state[pane];
3901
- o = options[pane];
3902
- dir = _c[pane].dir;
3903
-
3904
- if (o.autoResize && s.size != o.size) // resize pane to original size set in options
3905
- sizePane(pane, o.size, true, true, true); // true=skipCallback/forceResize/noAnimation
4294
+ o = options[pane];
4295
+ s = state[pane];
4296
+ if (s.autoResize && s.size != o.size) // resize pane to original size set in options
4297
+ sizePane(pane, o.size, true, true, true); // true=skipCallback/noAnimation/forceResize
3906
4298
  else {
3907
4299
  setSizeLimits(pane);
3908
4300
  makePaneFit(pane, false, true, true); // true=skipCallback/forceResize
3909
4301
  }
3910
4302
  });
3911
4303
 
3912
- sizeMidPanes("", true, true); // true=skipCallback, true=forceResize
4304
+ sizeMidPanes("", true, true); // true=skipCallback/forceResize
3913
4305
  sizeHandles(); // reposition the toggler elements
3914
4306
 
3915
4307
  // trigger all individual pane callbacks AFTER layout has finished resizing
3916
- o = options; // reuse alias
3917
4308
  $.each(_c.allPanes, function (i, pane) {
3918
4309
  $P = $Ps[pane];
3919
4310
  if (!$P) return; // SKIP
@@ -3928,42 +4319,29 @@ $.fn.layout = function (opts) {
3928
4319
  /**
3929
4320
  * Whenever a pane resizes or opens that has a nested layout, trigger resizeAll
3930
4321
  *
3931
- * @param {string} pane The pane just resized or opened
4322
+ * @param {(string|Object)} evt_or_pane The pane just resized or opened
3932
4323
  */
3933
- , resizeChildLayout = function (evt_or_pane) {
4324
+ , resizeChildren = function (evt_or_pane, skipRefresh) {
3934
4325
  var pane = evtPane.call(this, evt_or_pane);
3935
- if (!options[pane].resizeChildLayout) return;
3936
- var $P = $Ps[pane]
3937
- , $C = $Cs[pane]
3938
- , d = "layout"
3939
- , P = Instance[pane]
3940
- , L = children[pane]
3941
- ;
3942
- // user may have manually set EITHER instance pointer, so handle that
3943
- if (P.child && !L) {
3944
- // have to reverse the pointers!
3945
- var el = P.child.container;
3946
- L = children[pane] = (el ? el.data(d) : 0) || null; // set pointer _directly_ to layout instance
3947
- }
3948
-
3949
- // if a layout-pointer exists, see if child has been destroyed
3950
- if (L && L.destroyed)
3951
- L = children[pane] = null; // clear child pointers
3952
- // no child layout pointer is set - see if there is a child layout NOW
3953
- if (!L) L = children[pane] = $P.data(d) || ($C ? $C.data(d) : 0) || null; // set/update child pointers
3954
4326
 
3955
- // ALWAYS refresh the pane.child alias
3956
- P.child = children[pane];
4327
+ if (!options[pane].resizeChildren) return;
3957
4328
 
3958
- if (L) L.resizeAll();
4329
+ // ensure the pane-children are up-to-date
4330
+ if (!skipRefresh) refreshChildren( pane );
4331
+ var pC = children[pane];
4332
+ if ($.isPlainObject( pC )) {
4333
+ // resize one or more children
4334
+ $.each( pC, function (key, child) {
4335
+ if (!child.destroyed) child.resizeAll();
4336
+ });
4337
+ }
3959
4338
  }
3960
4339
 
3961
-
3962
4340
  /**
3963
4341
  * IF pane has a content-div, then resize all elements inside pane to fit pane-height
3964
4342
  *
3965
- * @param {string=} [panes=""] The pane(s) being resized
3966
- * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured?
4343
+ * @param {(string|Object)} evt_or_panes The pane(s) being resized
4344
+ * @param {boolean=} [remeasure=false] Should the content (header/footer) be remeasured?
3967
4345
  */
3968
4346
  , sizeContent = function (evt_or_panes, remeasure) {
3969
4347
  if (!isInitialized()) return;
@@ -4020,7 +4398,7 @@ $.fn.layout = function (opts) {
4020
4398
  function _measure () {
4021
4399
  var
4022
4400
  ignore = options[pane].contentIgnoreSelector
4023
- , $Fs = $C.nextAll().not(ignore || ':lt(0)') // not :lt(0) = ALL
4401
+ , $Fs = $C.nextAll().not(".ui-layout-mask").not(ignore || ":lt(0)") // not :lt(0) = ALL
4024
4402
  , $Fs_vis = $Fs.filter(':visible')
4025
4403
  , $F = $Fs_vis.filter(':last')
4026
4404
  ;
@@ -4047,7 +4425,7 @@ $.fn.layout = function (opts) {
4047
4425
  * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
4048
4426
  *
4049
4427
  * @see initHandles(), open(), close(), resizeAll()
4050
- * @param {string=} [panes=""] The pane(s) being resized
4428
+ * @param {(string|Object)=} evt_or_panes The pane(s) being resized
4051
4429
  */
4052
4430
  , sizeHandles = function (evt_or_panes) {
4053
4431
  var panes = evtPane.call(this, evt_or_panes)
@@ -4092,7 +4470,7 @@ $.fn.layout = function (opts) {
4092
4470
  $R.css({
4093
4471
  width: cssW($R, paneLen) // account for borders & padding
4094
4472
  , height: cssH($R, spacing) // ditto
4095
- , left: left > -9999 ? left : sC.insetLeft // handle offscreen-panes
4473
+ , left: left > -9999 ? left : sC.inset.left // handle offscreen-panes
4096
4474
  });
4097
4475
  }
4098
4476
  else { // east/west
@@ -4101,7 +4479,7 @@ $.fn.layout = function (opts) {
4101
4479
  $R.css({
4102
4480
  height: cssH($R, paneLen) // account for borders & padding
4103
4481
  , width: cssW($R, spacing) // ditto
4104
- , top: sC.insetTop + getPaneSize("north", true) // TODO: what if no North pane?
4482
+ , top: sC.inset.top + getPaneSize("north", true) // TODO: what if no North pane?
4105
4483
  //, top: $.layout.cssNum($Ps["center"], "top")
4106
4484
  });
4107
4485
  }
@@ -4176,7 +4554,7 @@ $.fn.layout = function (opts) {
4176
4554
  }
4177
4555
 
4178
4556
  // DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
4179
- if (!state.initialized && (o.initHidden || s.noRoom)) {
4557
+ if (!state.initialized && (o.initHidden || s.isHidden)) {
4180
4558
  $R.hide();
4181
4559
  if ($T) $T.hide();
4182
4560
  }
@@ -4185,7 +4563,7 @@ $.fn.layout = function (opts) {
4185
4563
 
4186
4564
 
4187
4565
  /**
4188
- * @param {string} pane
4566
+ * @param {(string|Object)} evt_or_pane
4189
4567
  */
4190
4568
  , enableClosable = function (evt_or_pane) {
4191
4569
  if (!isInitialized()) return;
@@ -4198,12 +4576,12 @@ $.fn.layout = function (opts) {
4198
4576
  $T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); })
4199
4577
  .css("visibility", "visible")
4200
4578
  .css("cursor", "pointer")
4201
- .attr("title", state[pane].isClosed ? o.togglerTip_closed : o.togglerTip_open) // may be blank
4579
+ .attr("title", state[pane].isClosed ? o.tips.Open : o.tips.Close) // may be blank
4202
4580
  .show();
4203
4581
  }
4204
4582
  /**
4205
- * @param {string} pane
4206
- * @param {boolean=} [hide=false]
4583
+ * @param {(string|Object)} evt_or_pane
4584
+ * @param {boolean=} [hide=false]
4207
4585
  */
4208
4586
  , disableClosable = function (evt_or_pane, hide) {
4209
4587
  if (!isInitialized()) return;
@@ -4222,7 +4600,7 @@ $.fn.layout = function (opts) {
4222
4600
 
4223
4601
 
4224
4602
  /**
4225
- * @param {string} pane
4603
+ * @param {(string|Object)} evt_or_pane
4226
4604
  */
4227
4605
  , enableSlidable = function (evt_or_pane) {
4228
4606
  if (!isInitialized()) return;
@@ -4231,11 +4609,11 @@ $.fn.layout = function (opts) {
4231
4609
  ;
4232
4610
  if (!$R || !$R.data('draggable')) return;
4233
4611
  options[pane].slidable = true;
4234
- if (s.isClosed)
4235
- bindStartSlidingEvent(pane, true);
4612
+ if (state[pane].isClosed)
4613
+ bindStartSlidingEvents(pane, true);
4236
4614
  }
4237
4615
  /**
4238
- * @param {string} pane
4616
+ * @param {(string|Object)} evt_or_pane
4239
4617
  */
4240
4618
  , disableSlidable = function (evt_or_pane) {
4241
4619
  if (!isInitialized()) return;
@@ -4247,7 +4625,7 @@ $.fn.layout = function (opts) {
4247
4625
  if (state[pane].isSliding)
4248
4626
  close(pane, false, true);
4249
4627
  else {
4250
- bindStartSlidingEvent(pane, false);
4628
+ bindStartSlidingEvents(pane, false);
4251
4629
  $R .css("cursor", "default")
4252
4630
  .attr("title", "");
4253
4631
  removeHover(null, $R[0]); // in case currently hovered
@@ -4256,7 +4634,7 @@ $.fn.layout = function (opts) {
4256
4634
 
4257
4635
 
4258
4636
  /**
4259
- * @param {string} pane
4637
+ * @param {(string|Object)} evt_or_pane
4260
4638
  */
4261
4639
  , enableResizable = function (evt_or_pane) {
4262
4640
  if (!isInitialized()) return;
@@ -4269,10 +4647,10 @@ $.fn.layout = function (opts) {
4269
4647
  $R.draggable("enable");
4270
4648
  if (!state[pane].isClosed)
4271
4649
  $R .css("cursor", o.resizerCursor)
4272
- .attr("title", o.resizerTip);
4650
+ .attr("title", o.tips.Resize);
4273
4651
  }
4274
4652
  /**
4275
- * @param {string} pane
4653
+ * @param {(string|Object)} evt_or_pane
4276
4654
  */
4277
4655
  , disableResizable = function (evt_or_pane) {
4278
4656
  if (!isInitialized()) return;
@@ -4292,8 +4670,8 @@ $.fn.layout = function (opts) {
4292
4670
  * Move a pane from source-side (eg, west) to target-side (eg, east)
4293
4671
  * If pane exists on target-side, move that to source-side, ie, 'swap' the panes
4294
4672
  *
4295
- * @param {string} pane1 The pane/edge being swapped
4296
- * @param {string} pane2 ditto
4673
+ * @param {(string|Object)} evt_or_pane1 The pane/edge being swapped
4674
+ * @param {string} pane2 ditto
4297
4675
  */
4298
4676
  , swapPanes = function (evt_or_pane1, pane2) {
4299
4677
  if (!isInitialized()) return;
@@ -4372,10 +4750,8 @@ $.fn.layout = function (opts) {
4372
4750
  , C = oPane.C
4373
4751
  , oldPane = oPane.pane
4374
4752
  , c = _c[pane]
4375
- , side = c.side.toLowerCase()
4376
- , inset = "inset"+ c.side
4377
4753
  // save pane-options that should be retained
4378
- , s = $.extend({}, state[pane])
4754
+ , s = $.extend(true, {}, state[pane])
4379
4755
  , o = options[pane]
4380
4756
  // RETAIN side-specific FX Settings - more below
4381
4757
  , fx = { resizerCursor: o.resizerCursor }
@@ -4399,8 +4775,8 @@ $.fn.layout = function (opts) {
4399
4775
  $Cs[pane] = C ? $(C) : false;
4400
4776
 
4401
4777
  // set options and state
4402
- options[pane] = $.extend({}, oPane.options, fx);
4403
- state[pane] = $.extend({}, oPane.state);
4778
+ options[pane] = $.extend(true, {}, oPane.options, fx);
4779
+ state[pane] = $.extend(true, {}, oPane.state);
4404
4780
 
4405
4781
  // change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west
4406
4782
  re = new RegExp(o.paneClass +"-"+ oldPane, "g");
@@ -4418,7 +4794,7 @@ $.fn.layout = function (opts) {
4418
4794
  manualSizePane(pane, size, true, true); // true/true = skipCallback/noAnimation
4419
4795
  }
4420
4796
  else // move the resizer here
4421
- $Rs[pane].css(side, sC[inset] + (state[pane].isVisible ? getPaneSize(pane) : 0));
4797
+ $Rs[pane].css(c.side, sC.inset[c.side] + (state[pane].isVisible ? getPaneSize(pane) : 0));
4422
4798
 
4423
4799
 
4424
4800
  // ADD CLASSNAMES & SLIDE-BINDINGS
@@ -4426,7 +4802,7 @@ $.fn.layout = function (opts) {
4426
4802
  setAsOpen(pane, true); // true = skipCallback
4427
4803
  else {
4428
4804
  setAsClosed(pane);
4429
- bindStartSlidingEvent(pane, true); // will enable events IF option is set
4805
+ bindStartSlidingEvents(pane, true); // will enable events IF option is set
4430
4806
  }
4431
4807
 
4432
4808
  // DESTROY the object
@@ -4560,11 +4936,11 @@ $.fn.layout = function (opts) {
4560
4936
  curCSS.overflow = of;
4561
4937
  newCSS.overflow = "visible";
4562
4938
  }
4563
- if (ofX && !ofX.match(/visible|auto/)) {
4939
+ if (ofX && !ofX.match(/(visible|auto)/)) {
4564
4940
  curCSS.overflowX = ofX;
4565
4941
  newCSS.overflowX = "visible";
4566
4942
  }
4567
- if (ofY && !ofY.match(/visible|auto/)) {
4943
+ if (ofY && !ofY.match(/(visible|auto)/)) {
4568
4944
  curCSS.overflowY = ofX;
4569
4945
  newCSS.overflowY = "visible";
4570
4946
  }
@@ -4626,9 +5002,7 @@ $.fn.layout = function (opts) {
4626
5002
  // validate that container exists
4627
5003
  var $N = $(this).eq(0); // FIRST matching Container element
4628
5004
  if (!$N.length) {
4629
- if (options.showErrorMessages)
4630
- _log( lang.errContainerMissing, true );
4631
- return null;
5005
+ return _log( options.errors.containerMissing );
4632
5006
  };
4633
5007
 
4634
5008
  // Users retrieve Instance of a layout with: $N.layout() OR $N.data("layout")
@@ -4674,11 +5048,14 @@ $.fn.layout = function (opts) {
4674
5048
  , sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto'
4675
5049
  , sizeContent: sizeContent // method - pass a 'pane'
4676
5050
  , swapPanes: swapPanes // method - pass TWO 'panes' - will swap them
5051
+ , showMasks: showMasks // method - pass a 'pane' OR list of panes - default = all panes with mask option set
5052
+ , hideMasks: hideMasks // method - ditto'
4677
5053
  // pane element methods
4678
5054
  , initContent: initContent // method - ditto
4679
5055
  , addPane: addPane // method - pass a 'pane'
4680
5056
  , removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem
4681
- , createChildLayout: createChildLayout// method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].childOptions
5057
+ , createChildren: createChildren // method - pass a 'pane' and (optional) layout-options (OVERRIDES options[pane].children
5058
+ , refreshChildren: refreshChildren // method - pass a 'pane' and a layout-instance
4682
5059
  // special pane option setting
4683
5060
  , enableClosable: enableClosable // method - pass a 'pane'
4684
5061
  , disableClosable: disableClosable // method - ditto
@@ -4697,8 +5074,8 @@ $.fn.layout = function (opts) {
4697
5074
  , runCallbacks: _runCallbacks // method - pass evtName & pane (if a pane-event), eg: trigger("onopen", "west")
4698
5075
  // alias collections of options, state and children - created in addPane and extended elsewhere
4699
5076
  , hasParentLayout: false // set by initContainer()
4700
- , children: children // pointers to child-layouts, eg: Instance.children["west"]
4701
- , north: false // alias group: { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], child: children[pane] }
5077
+ , children: children // pointers to child-layouts, eg: Instance.children.west.layoutName
5078
+ , north: false // alias group: { name: pane, pane: $Ps[pane], options: options[pane], state: state[pane], children: children[pane] }
4702
5079
  , south: false // ditto
4703
5080
  , west: false // ditto
4704
5081
  , east: false // ditto
@@ -4714,22 +5091,29 @@ $.fn.layout = function (opts) {
4714
5091
  }
4715
5092
 
4716
5093
 
5094
+ })( jQuery );
5095
+ // END Layout - keep internal vars internal!
5096
+
5097
+
5098
+
5099
+ // START Plugins - shared wrapper, no global vars
5100
+ (function ($) {
4717
5101
 
4718
5102
 
4719
5103
  /**
4720
5104
  * jquery.layout.state 1.0
4721
5105
  * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
4722
5106
  *
4723
- * Copyright (c) 2010
5107
+ * Copyright (c) 2012
4724
5108
  * Kevin Dalman (http://allpro.net)
4725
5109
  *
4726
5110
  * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
4727
5111
  * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
4728
5112
  *
4729
- * @dependancies: UI Layout 1.3.0.rc30.1 or higher
4730
- * @dependancies: $.ui.cookie (above)
5113
+ * @requires: UI Layout 1.3.0.rc30.1 or higher
5114
+ * @requires: $.ui.cookie (above)
4731
5115
  *
4732
- * @support: http://groups.google.com/group/jquery-ui-layout
5116
+ * @see: http://groups.google.com/group/jquery-ui-layout
4733
5117
  */
4734
5118
  /*
4735
5119
  * State-management options stored in options.stateManagement, which includes a .cookie hash
@@ -4776,8 +5160,7 @@ $.ui.cookie = {
4776
5160
  acceptsCookies: !!navigator.cookieEnabled
4777
5161
 
4778
5162
  , read: function (name) {
4779
- var
4780
- c = document.cookie
5163
+ var c = document.cookie
4781
5164
  , cs = c ? c.split(';') : []
4782
5165
  , pair // loop var
4783
5166
  ;
@@ -4785,22 +5168,25 @@ $.ui.cookie = {
4785
5168
  pair = $.trim(cs[i]).split('='); // name=value pair
4786
5169
  if (pair[0] == name) // found the layout cookie
4787
5170
  return decodeURIComponent(pair[1]);
4788
-
4789
5171
  }
4790
5172
  return null;
4791
5173
  }
4792
5174
 
4793
5175
  , write: function (name, val, cookieOpts) {
4794
- var
4795
- params = ''
4796
- , date = ''
5176
+ var params = ""
5177
+ , date = ""
4797
5178
  , clear = false
4798
5179
  , o = cookieOpts || {}
4799
- , x = o.expires
5180
+ , x = o.expires || null
5181
+ , t = $.type(x)
4800
5182
  ;
4801
- if (x && x.toUTCString)
5183
+ if (t === "date")
4802
5184
  date = x;
4803
- else if (x === null || typeof x === 'number') {
5185
+ else if (t === "string" && x > 0) {
5186
+ x = parseInt(x,10);
5187
+ t = "number";
5188
+ }
5189
+ if (t === "number") {
4804
5190
  date = new Date();
4805
5191
  if (x > 0)
4806
5192
  date.setDate(date.getDate() + x);
@@ -4809,15 +5195,15 @@ $.ui.cookie = {
4809
5195
  clear = true;
4810
5196
  }
4811
5197
  }
4812
- if (date) params += ';expires='+ date.toUTCString();
4813
- if (o.path) params += ';path='+ o.path;
4814
- if (o.domain) params += ';domain='+ o.domain;
4815
- if (o.secure) params += ';secure';
4816
- document.cookie = name +'='+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie
5198
+ if (date) params += ";expires="+ date.toUTCString();
5199
+ if (o.path) params += ";path="+ o.path;
5200
+ if (o.domain) params += ";domain="+ o.domain;
5201
+ if (o.secure) params += ";secure";
5202
+ document.cookie = name +"="+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie
4817
5203
  }
4818
5204
 
4819
5205
  , clear: function (name) {
4820
- $.ui.cookie.write(name, '', {expires: -1});
5206
+ $.ui.cookie.write(name, "", {expires: -1});
4821
5207
  }
4822
5208
 
4823
5209
  };
@@ -4840,9 +5226,11 @@ $.layout.plugins.stateManagement = true;
4840
5226
  // Add State-Management options to layout.defaults
4841
5227
  $.layout.config.optionRootKeys.push("stateManagement");
4842
5228
  $.layout.defaults.stateManagement = {
4843
- enabled: false // true = enable state-management, even if not using cookies
4844
- , autoSave: true // Save a state-cookie when page exits?
4845
- , autoLoad: true // Load the state-cookie when Layout inits?
5229
+ enabled: false // true = enable state-management, even if not using cookies
5230
+ , autoSave: true // Save a state-cookie when page exits?
5231
+ , autoLoad: true // Load the state-cookie when Layout inits?
5232
+ , animateLoad: true // animate panes when loading state into an active layout
5233
+ , includeChildren: true // recurse into child layouts to include their state as well
4846
5234
  // List state-data to save - must be pane-specific
4847
5235
  , stateKeys: "north.size,south.size,east.size,west.size,"+
4848
5236
  "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
@@ -4850,7 +5238,7 @@ $.layout.defaults.stateManagement = {
4850
5238
  , cookie: {
4851
5239
  name: "" // If not specified, will use Layout.name, else just "Layout"
4852
5240
  , domain: "" // blank = current domain
4853
- , path: "" // blank = current page, '/' = entire website
5241
+ , path: "" // blank = current page, "/" = entire website
4854
5242
  , expires: "" // 'days' to keep cookie - leave blank for 'session cookie'
4855
5243
  , secure: false
4856
5244
  }
@@ -4870,13 +5258,13 @@ $.layout.state = {
4870
5258
  *
4871
5259
  * @param {Object} inst
4872
5260
  * @param {(string|Array)=} keys
4873
- * @param {Object=} opts
5261
+ * @param {Object=} cookieOpts
4874
5262
  */
4875
5263
  saveCookie: function (inst, keys, cookieOpts) {
4876
5264
  var o = inst.options
4877
- , oS = o.stateManagement
4878
- , oC = $.extend(true, {}, oS.cookie, cookieOpts || null)
4879
- , data = inst.state.stateData = inst.readState( keys || oS.stateKeys ) // read current panes-state
5265
+ , sm = o.stateManagement
5266
+ , oC = $.extend(true, {}, sm.cookie, cookieOpts || null)
5267
+ , data = inst.state.stateData = inst.readState( keys || sm.stateKeys ) // read current panes-state
4880
5268
  ;
4881
5269
  $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC );
4882
5270
  return $.extend(true, {}, data); // return COPY of state.stateData data
@@ -4917,7 +5305,7 @@ $.layout.state = {
4917
5305
  }
4918
5306
  return c;
4919
5307
  }
4920
-
5308
+
4921
5309
  /**
4922
5310
  * Update layout options from the cookie, if one exists
4923
5311
  *
@@ -4925,52 +5313,107 @@ $.layout.state = {
4925
5313
  * @param {Object=} stateData
4926
5314
  * @param {boolean=} animate
4927
5315
  */
4928
- , loadState: function (inst, stateData, animate) {
4929
- stateData = $.layout.transformData( stateData ); // panes = default subkey
4930
- if ($.isEmptyObject( stateData )) return;
4931
- $.extend(true, inst.options, stateData); // update layout options
4932
- // if layout has already been initialized, then UPDATE layout state
4933
- if (inst.state.initialized) {
4934
- var pane, vis, o, s, h, c
4935
- , noAnimate = (animate===false)
5316
+ , loadState: function (inst, data, opts) {
5317
+ if (!$.isPlainObject( data ) || $.isEmptyObject( data )) return;
5318
+
5319
+ // normalize data & cache in the state object
5320
+ data = inst.state.stateData = $.layout.transformData( data ); // panes = default subkey
5321
+
5322
+ // add missing/default state-restore options
5323
+ var smo = inst.options.stateManagement;
5324
+ opts = $.extend({
5325
+ animateLoad: false //smo.animateLoad
5326
+ , includeChildren: smo.includeChildren
5327
+ }, opts );
5328
+
5329
+ if (!inst.state.initialized) {
5330
+ /*
5331
+ * layout NOT initialized, so just update its options
5332
+ */
5333
+ // MUST remove pane.children keys before applying to options
5334
+ // use a copy so we don't remove keys from original data
5335
+ var o = $.extend(true, {}, data);
5336
+ //delete o.center; // center has no state-data - only children
5337
+ $.each($.layout.config.allPanes, function (idx, pane) {
5338
+ if (o[pane]) delete o[pane].children;
5339
+ });
5340
+ // update CURRENT layout-options with saved state data
5341
+ $.extend(true, inst.options, o);
5342
+ }
5343
+ else {
5344
+ /*
5345
+ * layout already initialized, so modify layout's configuration
5346
+ */
5347
+ var noAnimate = !opts.animateLoad
5348
+ , o, c, h, state, open
4936
5349
  ;
4937
5350
  $.each($.layout.config.borderPanes, function (idx, pane) {
4938
- state = inst.state[pane];
4939
- o = stateData[ pane ];
4940
- if (typeof o != 'object') return; // no key, continue
5351
+ o = data[ pane ];
5352
+ if (!$.isPlainObject( o )) return; // no key, skip pane
5353
+
4941
5354
  s = o.size;
4942
5355
  c = o.initClosed;
4943
5356
  h = o.initHidden;
4944
- vis = state.isVisible;
5357
+ ar = o.autoResize
5358
+ state = inst.state[pane];
5359
+ open = state.isVisible;
5360
+
5361
+ // reset autoResize
5362
+ if (ar)
5363
+ state.autoResize = ar;
4945
5364
  // resize BEFORE opening
4946
- if (!vis)
4947
- inst.sizePane(pane, s, false, false);
5365
+ if (!open)
5366
+ inst._sizePane(pane, s, false, false, false); // false=skipCallback/noAnimation/forceResize
5367
+ // open/close as necessary - DO NOT CHANGE THIS ORDER!
4948
5368
  if (h === true) inst.hide(pane, noAnimate);
4949
- else if (c === false) inst.open (pane, false, noAnimate);
4950
5369
  else if (c === true) inst.close(pane, false, noAnimate);
5370
+ else if (c === false) inst.open (pane, false, noAnimate);
4951
5371
  else if (h === false) inst.show (pane, false, noAnimate);
4952
5372
  // resize AFTER any other actions
4953
- if (vis)
4954
- inst.sizePane(pane, s, false, noAnimate); // animate resize if option passed
5373
+ if (open)
5374
+ inst._sizePane(pane, s, false, false, noAnimate); // animate resize if option passed
4955
5375
  });
4956
- };
5376
+
5377
+ /*
5378
+ * RECURSE INTO CHILD-LAYOUTS
5379
+ */
5380
+ if (opts.includeChildren) {
5381
+ var paneStateChildren, childState;
5382
+ $.each(inst.children, function (pane, paneChildren) {
5383
+ paneStateChildren = data[pane] ? data[pane].children : 0;
5384
+ if (paneStateChildren && paneChildren) {
5385
+ $.each(paneChildren, function (stateKey, child) {
5386
+ childState = paneStateChildren[stateKey];
5387
+ if (child && childState)
5388
+ child.loadState( childState );
5389
+ });
5390
+ }
5391
+ });
5392
+ }
5393
+ }
4957
5394
  }
4958
5395
 
4959
5396
  /**
4960
5397
  * Get the *current layout state* and return it as a hash
4961
5398
  *
4962
- * @param {Object=} inst
4963
- * @param {(string|Array)=} keys
5399
+ * @param {Object=} inst // Layout instance to get state for
5400
+ * @param {object=} [opts] // State-Managements override options
4964
5401
  */
4965
- , readState: function (inst, keys) {
4966
- var
4967
- data = {}
5402
+ , readState: function (inst, opts) {
5403
+ // backward compatility
5404
+ if ($.type(opts) === 'string') opts = { keys: opts };
5405
+ if (!opts) opts = {};
5406
+ var sm = inst.options.stateManagement
5407
+ , ic = opts.includeChildren
5408
+ , recurse = ic !== undefined ? ic : sm.includeChildren
5409
+ , keys = opts.stateKeys || sm.stateKeys
4968
5410
  , alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
4969
5411
  , state = inst.state
4970
5412
  , panes = $.layout.config.allPanes
5413
+ , data = {}
4971
5414
  , pair, pane, key, val
5415
+ , ps, pC, child, array, count, branch
4972
5416
  ;
4973
- if (!keys) keys = inst.options.stateManagement.stateKeys; // if called by user
4974
5417
  if ($.isArray(keys)) keys = keys.join(",");
4975
5418
  // convert keys to an array and change delimiters from '__' to '.'
4976
5419
  keys = keys.replace(/__/g, ".").split(',');
@@ -4986,6 +5429,29 @@ $.layout.state = {
4986
5429
  val = true; // if sliding, then *really* isClosed
4987
5430
  ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
4988
5431
  }
5432
+
5433
+ // recurse into the child-layouts for each pane
5434
+ if (recurse) {
5435
+ $.each(panes, function (idx, pane) {
5436
+ pC = inst.children[pane];
5437
+ ps = state.stateData[pane];
5438
+ if ($.isPlainObject( pC ) && !$.isEmptyObject( pC )) {
5439
+ // ensure a key exists for this 'pane', eg: branch = data.center
5440
+ branch = data[pane] || (data[pane] = {});
5441
+ if (!branch.children) branch.children = {};
5442
+ $.each( pC, function (key, child) {
5443
+ // ONLY read state from an initialize layout
5444
+ if ( child.state.initialized )
5445
+ branch.children[ key ] = $.layout.state.readState( child );
5446
+ // if we have PREVIOUS (onLoad) state for this child-layout, KEEP IT!
5447
+ else if ( ps && ps.children && ps.children[ key ] ) {
5448
+ branch.children[ key ] = $.extend(true, {}, ps.children[ key ] );
5449
+ }
5450
+ });
5451
+ }
5452
+ });
5453
+ }
5454
+
4989
5455
  return data;
4990
5456
  }
4991
5457
 
@@ -4995,7 +5461,9 @@ $.layout.state = {
4995
5461
  , encodeJSON: function (JSON) {
4996
5462
  return parse(JSON);
4997
5463
  function parse (h) {
4998
- var D=[], i=0, k, v, t; // k = key, v = value
5464
+ var D=[], i=0, k, v, t // k = key, v = value
5465
+ , a = $.isArray(h)
5466
+ ;
4999
5467
  for (k in h) {
5000
5468
  v = h[k];
5001
5469
  t = typeof v;
@@ -5003,9 +5471,9 @@ $.layout.state = {
5003
5471
  v = '"'+ v +'"';
5004
5472
  else if (t == 'object') // SUB-KEY - recurse into it
5005
5473
  v = parse(v);
5006
- D[i++] = '"'+ k +'":'+ v;
5474
+ D[i++] = (!a ? '"'+ k +'":' : '') + v;
5007
5475
  }
5008
- return '{'+ D.join(',') +'}';
5476
+ return (a ? '[' : '{') + D.join(',') + (a ? ']' : '}');
5009
5477
  };
5010
5478
  }
5011
5479
 
@@ -5020,7 +5488,10 @@ $.layout.state = {
5020
5488
 
5021
5489
 
5022
5490
  , _create: function (inst) {
5023
- var _ = $.layout.state;
5491
+ var _ = $.layout.state
5492
+ , o = inst.options
5493
+ , sm = o.stateManagement
5494
+ ;
5024
5495
  // ADD State-Management plugin methods to inst
5025
5496
  $.extend( inst, {
5026
5497
  // readCookie - update options from cookie - returns hash of cookie data
@@ -5032,7 +5503,7 @@ $.layout.state = {
5032
5503
  // loadCookie - readCookie and use to loadState() - returns hash of cookie data
5033
5504
  , loadCookie: function () { return _.loadCookie(inst); }
5034
5505
  // loadState - pass a hash of state to use to update options
5035
- , loadState: function (stateData, animate) { _.loadState(inst, stateData, animate); }
5506
+ , loadState: function (stateData, opts) { _.loadState(inst, stateData, opts); }
5036
5507
  // readState - returns hash of current layout-state
5037
5508
  , readState: function (keys) { return _.readState(inst, keys); }
5038
5509
  // add JSON utility methods too...
@@ -5043,23 +5514,43 @@ $.layout.state = {
5043
5514
  // init state.stateData key, even if plugin is initially disabled
5044
5515
  inst.state.stateData = {};
5045
5516
 
5046
- // read and load cookie-data per options
5047
- var oS = inst.options.stateManagement;
5048
- if (oS.enabled) {
5049
- if (oS.autoLoad) // update the options from the cookie
5517
+ // autoLoad MUST BE one of: data-array, data-hash, callback-function, or TRUE
5518
+ if ( !sm.autoLoad ) return;
5519
+
5520
+ // When state-data exists in the autoLoad key USE IT,
5521
+ // even if stateManagement.enabled == false
5522
+ if ($.isPlainObject( sm.autoLoad )) {
5523
+ if (!$.isEmptyObject( sm.autoLoad )) {
5524
+ inst.loadState( sm.autoLoad );
5525
+ }
5526
+ }
5527
+ else if ( sm.enabled ) {
5528
+ // update the options from cookie or callback
5529
+ // if options is a function, call it to get stateData
5530
+ if ($.isFunction( sm.autoLoad )) {
5531
+ var d = {};
5532
+ try {
5533
+ d = sm.autoLoad( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
5534
+ } catch (e) {}
5535
+ if (d && $.isPlainObject( d ) && !$.isEmptyObject( d ))
5536
+ inst.loadState(d);
5537
+ }
5538
+ else // any other truthy value will trigger loadCookie
5050
5539
  inst.loadCookie();
5051
- else // don't modify options - just store cookie data in state.stateData
5052
- inst.state.stateData = inst.readCookie();
5053
5540
  }
5054
5541
  }
5055
5542
 
5056
5543
  , _unload: function (inst) {
5057
- var oS = inst.options.stateManagement;
5058
- if (oS.enabled) {
5059
- if (oS.autoSave) // save a state-cookie automatically
5544
+ var sm = inst.options.stateManagement;
5545
+ if (sm.enabled && sm.autoSave) {
5546
+ // if options is a function, call it to save the stateData
5547
+ if ($.isFunction( sm.autoSave )) {
5548
+ try {
5549
+ sm.autoSave( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
5550
+ } catch (e) {}
5551
+ }
5552
+ else // any truthy value will trigger saveCookie
5060
5553
  inst.saveCookie();
5061
- else // don't save a cookie, but do store state-data in state.stateData key
5062
- inst.state.stateData = inst.readState();
5063
5554
  }
5064
5555
  }
5065
5556
 
@@ -5076,15 +5567,15 @@ $.layout.onUnload.push( $.layout.state._unload );
5076
5567
  * jquery.layout.buttons 1.0
5077
5568
  * $Date: 2011-07-16 08:00:00 (Sat, 16 July 2011) $
5078
5569
  *
5079
- * Copyright (c) 2010
5570
+ * Copyright (c) 2012
5080
5571
  * Kevin Dalman (http://allpro.net)
5081
5572
  *
5082
5573
  * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
5083
5574
  * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
5084
5575
  *
5085
- * @dependancies: UI Layout 1.3.0.rc30.1 or higher
5576
+ * @requires: UI Layout 1.3.0.rc30.1 or higher
5086
5577
  *
5087
- * @support: http://groups.google.com/group/jquery-ui-layout
5578
+ * @see: http://groups.google.com/group/jquery-ui-layout
5088
5579
  *
5089
5580
  * Docs: [ to come ]
5090
5581
  * Tips: [ to come ]
@@ -5098,8 +5589,6 @@ $.layout.defaults.autoBindCustomButtons = false;
5098
5589
  // Specify autoBindCustomButtons as a layout-option, NOT a pane-option
5099
5590
  $.layout.optionsMap.layout.push("autoBindCustomButtons");
5100
5591
 
5101
- var lang = $.layout.language;
5102
-
5103
5592
  /*
5104
5593
  * Button methods
5105
5594
  */
@@ -5148,13 +5637,13 @@ $.layout.buttons = {
5148
5637
  , get: function (inst, selector, pane, action) {
5149
5638
  var $E = $(selector)
5150
5639
  , o = inst.options
5151
- , err = o.showErrorMessages
5640
+ , err = o.errors.addButtonError
5152
5641
  ;
5153
5642
  if (!$E.length) { // element not found
5154
- if (err) $.layout.msg(lang.errButton + lang.selector +": "+ selector, true);
5643
+ $.layout.msg(err +" "+ o.errors.selector +": "+ selector, true);
5155
5644
  }
5156
5645
  else if ($.inArray(pane, $.layout.config.borderPanes) < 0) { // invalid 'pane' sepecified
5157
- if (err) $.layout.msg(lang.errButton + lang.pane +": "+ pane, true);
5646
+ $.layout.msg(err +" "+ o.errors.pane +": "+ pane, true);
5158
5647
  $E = $(""); // NO BUTTON
5159
5648
  }
5160
5649
  else { // VALID
@@ -5214,7 +5703,7 @@ $.layout.buttons = {
5214
5703
  */
5215
5704
  , addOpen: function (inst, selector, pane, slide) {
5216
5705
  $.layout.buttons.get(inst, selector, pane, "open")
5217
- .attr("title", lang.Open)
5706
+ .attr("title", inst.options[pane].tips.Open)
5218
5707
  .click(function (evt) {
5219
5708
  inst.open(pane, !!slide);
5220
5709
  evt.stopPropagation();
@@ -5231,7 +5720,7 @@ $.layout.buttons = {
5231
5720
  */
5232
5721
  , addClose: function (inst, selector, pane) {
5233
5722
  $.layout.buttons.get(inst, selector, pane, "close")
5234
- .attr("title", lang.Close)
5723
+ .attr("title", inst.options[pane].tips.Close)
5235
5724
  .click(function (evt) {
5236
5725
  inst.close(pane);
5237
5726
  evt.stopPropagation();
@@ -5287,14 +5776,15 @@ $.layout.buttons = {
5287
5776
  var updown = $Pin.attr("pin");
5288
5777
  if (updown && doPin === (updown=="down")) return; // already in correct state
5289
5778
  var
5290
- pin = inst.options[pane].buttonClass +"-pin"
5779
+ o = inst.options[pane]
5780
+ , pin = o.buttonClass +"-pin"
5291
5781
  , side = pin +"-"+ pane
5292
5782
  , UP = pin +"-up "+ side +"-up"
5293
5783
  , DN = pin +"-down "+side +"-down"
5294
5784
  ;
5295
5785
  $Pin
5296
5786
  .attr("pin", doPin ? "down" : "up") // logic
5297
- .attr("title", doPin ? lang.Unpin : lang.Pin)
5787
+ .attr("title", doPin ? o.tips.Unpin : o.tips.Pin)
5298
5788
  .removeClass( doPin ? UP : DN )
5299
5789
  .addClass( doPin ? DN : UP )
5300
5790
  ;
@@ -5312,7 +5802,7 @@ $.layout.buttons = {
5312
5802
  */
5313
5803
  , syncPinBtns: function (inst, pane, doPin) {
5314
5804
  // REAL METHOD IS _INSIDE_ LAYOUT - THIS IS HERE JUST FOR REFERENCE
5315
- $.each(state[pane].pins, function (i, selector) {
5805
+ $.each(inst.state[pane].pins, function (i, selector) {
5316
5806
  $.layout.buttons.setPinState(inst, $(selector), pane, doPin);
5317
5807
  });
5318
5808
  }
@@ -5364,12 +5854,12 @@ $.layout.onLoad.push( $.layout.buttons._load );
5364
5854
  * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
5365
5855
  * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
5366
5856
  *
5367
- * @dependancies: UI Layout 1.3.0.rc30.1 or higher
5857
+ * @requires: UI Layout 1.3.0.rc30.1 or higher
5368
5858
  *
5369
- * @support: http://groups.google.com/group/jquery-ui-layout
5859
+ * @see: http://groups.google.com/group/jquery-ui-layout
5370
5860
  *
5371
- * @todo: Extend logic to handle other problematic zooming in browsers
5372
- * @todo: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event
5861
+ * TODO: Extend logic to handle other problematic zooming in browsers
5862
+ * TODO: Add hotkey/mousewheel bindings to _instantly_ respond to these zoom event
5373
5863
  */
5374
5864
 
5375
5865
  // tell Layout that the plugin is available
@@ -5426,7 +5916,7 @@ $.layout.browserZoom = {
5426
5916
  || !b.msie
5427
5917
  ) return false; // don't need to track zoom
5428
5918
 
5429
- if (s.deviceXDPI)
5919
+ if (s.deviceXDPI && s.systemXDPI) // syntax compiler hack
5430
5920
  return calc(s.deviceXDPI, s.systemXDPI);
5431
5921
  // everything below is just for future reference!
5432
5922
  if (b.webkit && (r = d.body.getBoundingClientRect))
@@ -5445,5 +5935,4 @@ $.layout.browserZoom = {
5445
5935
  $.layout.onReady.push( $.layout.browserZoom._init );
5446
5936
 
5447
5937
 
5448
-
5449
5938
  })( jQuery );