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