Ext.ux.InfoPanel extension class code:
1 // vim: ts=2:sw=2:nu:fdc=4:nospell
2
3 // Create user extensions namespace (Ext.ux)
4 Ext.namespace('Ext.ux');
5
6 /**
7 * Ext.ux.InfoPanel Extension Class
8 *
9 * @author Ing. Jozef Sakalos
10 * @version $Id: Ext.ux.InfoPanel.js 18 2007-06-13 17:55:16Z jozo $
11 *
12 * @class Ext.ux.InfoPanel
13 * @extends Ext.ContentPanel
14 * @constructor
15 * Creates new Ext.ux.InfoPanel
16 * @param {String/HTMLElement/Element} el The container element for this panel
17 * @param {String/Object} config A string to set only the title or a config object
18 * @param {String} content (optional) Set the HTML content for this panel
19 * @cfg {Boolean} animate false to switch animation of expand/collapse off (defaults to true)
20 * @cfg {String} bodyClass css class added to the body in addition to the default class(es)
21 * @cfg {String/HTMLElement/Element} bodyEl This element is used as body of panel.
22 * @cfg {Boolean} collapsed false to start with the expanded body (defaults to true)
23 * @cfg {Boolean} collapseOnUnpin unpinned panel is collapsed when possible (defaults to true)
24 * @cfg {Boolean} collapsible false to disable collapsibility (defaults to true)
25 * @cfg {Boolean} draggable true to allow panel dragging (defaults to false)
26 * @cfg {Float} duration Duration of animation in seconds (defaults to 0.35)
27 * @cfg {String} easingCollapse Easing to use for collapse animation (e.g. 'backIn')
28 * @cfg {String} easingExpand Easing to use for expand animation (e.g. 'backOut')
29 * @cfg {String} icon Path for icon to display in the title
30 * @cfg {Integer} minWidth minimal width in pixels of the resizable panel (no default)
31 * @cfg {Integer} maxWidth maximal width in pixels of the resizable panel (no default)
32 * @cfg {Integer} minHeight minimal height in pixels of the resizable panel (no default)
33 * @cfg {Integer} maxHeight maximal height in pixels of the resizable panel (no default)
34 * @cfg {String} panelClass Set to override the default 'x-dock-panel' class.
35 * @cfg {Boolean} pinned true to start in pinned state (implies collapsed:false) (defaults to false)
36 * @cfg {Boolean} resizable true to allow use resize width of the panel.
37 * Handles are transparent. (defaults to false)
38 * @cfg {String} shadowMode defaults to 'sides'.
39 * @cfg {Boolean} showPin Show the pin button - makes sense only if panel is part of Accordion
40 * @cfg {String} trigger 'title' or 'button'. Click where expands/collapses the panel (defaults to 'title')
41 * @cfg {Boolean} useShadow Use shadows for undocked panels or panels w/o dock. (defaults to true)
42 */
43 Ext.ux.InfoPanel = function(el, config, content) {
44
45 // {{{
46 // basic setup
47 var oldHtml = content || null;
48 if(config && config.content) {
49 oldHtml = oldHtml || config.content;
50 delete(config.content);
51 }
52
53 // save autoScroll to this.bodyScroll
54 if(config && config.autoScroll) {
55 this.bodyScroll = config.autoScroll;
56 delete(config.autoScroll);
57 }
58
59 // call parent constructor
60 Ext.ux.InfoPanel.superclass.constructor.call(this, el, config);
61
62 this.desktop = Ext.get(this.desktop) || Ext.get(document.body);
63
64 // shortcut of DomHelper
65 var dh = Ext.DomHelper, oldTitleEl;
66
67 this.el.clean();
68 this.el.addClass(this.panelClass);
69
70 // handle autoCreate
71 if(this.autoCreate) {
72 oldHtml = this.el.dom.innerHTML;
73 this.el.update('');
74 this.desktop.appendChild(this.el);
75 this.el.removeClass('x-layout-inactive-content');
76 }
77 // handle markup
78 else {
79 this.el.clean();
80 if(this.el.dom.firstChild && !this.bodyEl) {
81 this.title = this.title || this.el.dom.firstChild.innerHTML;
82 if(this.el.dom.firstChild.nextSibling) {
83 this.body = Ext.get(this.el.dom.firstChild.nextSibling);
84 }
85 oldTitleEl = this.el.dom.firstChild;
86 oldTitleEl = oldTitleEl.parentNode.removeChild(oldTitleEl);
87 oldTitleEl = null;
88 }
89 }
90
91 // get body element
92 if(this.bodyEl) {
93 this.body = Ext.get(this.bodyEl);
94 this.el.appendChild(this.body);
95 }
96 // }}}
97 // {{{
98 // create title element
99 var create = {
100 tag:'div', unselectable:'on', cls:'x-unselectable x-layout-panel-hd x-dock-panel-title', children: [
101 {tag:'table', cellspacing:0, children: [
102 {tag:'tr', children: [
103 {tag:'td', width:'100%', children: [
104 {tag:'div', cls:'x-dock-panel x-layout-panel-hd-text x-dock-panel-title-text'}
105 ]}
106 , {tag:'td', children:[
107 {tag:'div', cls:'x-dock-panel x-dock-panel-tools'}
108 ]}
109 ]}
110 ]}
111 ]};
112 this.titleEl = dh.insertFirst(this.el.dom, create, true);
113 this.titleEl.addClassOnOver('x-dock-panel-title-over');
114 this.titleEl.enableDisplayMode();
115 this.titleTextEl = Ext.get(this.titleEl.select('.x-dock-panel-title-text').elements[0]);
116 this.tools = Ext.get(this.titleEl.select('.x-dock-panel-tools').elements[0]);
117
118 this.tm = Ext.util.TextMetrics.createInstance(this.titleTextEl);
119 // }}}
120 // {{{
121 // set title
122 if(this.title) {
123 this.setTitle(this.title);
124 }
125 // }}}
126 // {{{
127 // create pin button
128 if(this.showPin) {
129 this.stickBtn = this.createTool(this.tools.dom, 'x-layout-stick');
130 this.stickBtn.enableDisplayMode();
131 this.stickBtn.on('click', function(e, target) {
132 e.stopEvent();
133 this.pinned = ! this.pinned;
134 this.updateVisuals();
135 this.fireEvent('pinned', this, this.pinned);
136 }, this);
137 this.stickBtn.hide();
138 }
139 // }}}
140 // {{{
141 // create collapse button
142 if(this.collapsible) {
143 this.collapseBtn = this.createTool(this.tools.dom
144 , (this.collapsed ? 'x-layout-collapse-east' : 'x-layout-collapse-south')
145 );
146 this.collapseBtn.enableDisplayMode();
147 if('title' === this.trigger) {
148 this.titleEl.addClass('x-window-header-text');
149 this.titleEl.on({
150 click:{scope: this, fn:this.toggle}
151 , selectstart:{scope: this, fn: function(e) {
152 e.preventDefault();
153 return false;
154 }}
155 }, this);
156 }
157 else {
158 this.collapseBtn.on("click", this.toggle, this);
159 }
160 }
161 // }}}
162 // {{{
163 // create body if it doesn't exist yet
164 if(!this.body) {
165 this.body = dh.append(this.el, {
166 tag: 'div'
167 , cls: this.bodyClass || null
168 , html: oldHtml || ''
169 }, true);
170 }
171 this.body.enableDisplayMode();
172 if(this.collapsed && !this.pinned) {
173 this.body.hide();
174 }
175 else if(this.pinned) {
176 this.body.show();
177 this.collapsed = false;
178 this.updateVisuals();
179 }
180 this.body.addClass(this.bodyClass);
181 this.body.addClass('x-dock-panel-body-undocked');
182
183 // bodyScroll
184 // autoScroll -> bodyScroll is experimental due to IE bugs
185 if(!Ext.isIE) {
186 this.body.setStyle('overflow', this.bodyScroll === true ? 'auto' : 'hidden');
187 }
188 // }}}
189 // {{{
190 // add events
191 this.addEvents({
192 /**
193 * @event beforecollapse
194 * Fires before collapse is taking place. Return false to cancel collapse
195 * @param {Ext.ux.InfoPanel} this
196 */
197 beforecollapse: true
198 /**
199 * @event collapse
200 * Fires after collapse
201 * @param {Ext.ux.InfoPanel} this
202 */
203 , collapse: true
204 /**
205 * @event beforecollapse
206 * Fires before expand is taking place. Return false to cancel expand
207 * @param {Ext.ux.InfoPanel} this
208 */
209 , beforeexpand: true
210 /**
211 * @event expand
212 * Fires after expand
213 * @param {Ext.ux.InfoPanel} this
214 */
215 , expand: true
216 /**
217 * @event pinned
218 * Fires when panel is pinned/unpinned
219 * @param {Ext.ux.InfoPanel} this
220 * @param {Boolean} pinned true if the panel is pinned
221 */
222 , pinned: true
223 /**
224 * @event animationcompleted
225 * Fires when animation is completed
226 * @param {Ext.ux.InfoPanel} this
227 */
228 , animationcompleted: true
229 /**
230 * @event boxchange
231 * Fires when the panel is resized
232 * @param {Ext.ux.InfoPanel} this
233 * @param {Object} box
234 */
235 , boxchange: true
236
237 /**
238 * @event redize
239 * Fires when info panel is resized
240 * @param {Ext.ux.InfoPanel} this
241 * @param {Integer} width New width
242 * @param {Integer} height New height
243 */
244 , resize: true
245
246 /**
247 * @event destroy
248 * Fires after the panel is destroyed
249 * @param {Ext.ux.InfoPanel} this
250 */
251 , destroy: true
252
253 });
254 // }}}
255 // {{{
256 // setup dragging, resizing, and shadow
257 this.setDraggable(this.draggable);
258 this.setResizable(!this.collapsed);
259 this.setShadow(this.useShadow);
260
261 /*
262 // todo revise
263 if(this.minHeight && !this.docked) {
264 this.setHeight(this.minHeight);
265 }
266
267 if(this.minWidth && !this.docked) {
268 this.setWidth(this.minWidth);
269 }
270 */
271 // }}}
272
273 this.id = this.el.id;
274
275 }; // end of constructor
276
277 // extend
278 Ext.extend(Ext.ux.InfoPanel, Ext.ContentPanel, {
279
280 // {{{
281 // defaults
282 collapsible: true
283 , collapsed: true
284 , collapseOnUnpin: true
285 , pinned: false
286 , trigger: 'title'
287 , animate: true
288 , duration: 0.35
289 , draggable: false
290 , resizable: false
291 , docked: false
292 , useShadow: false
293 , bodyClass: 'x-dock-panel-body'
294 , panelClass: 'x-dock-panel'
295 , shadowMode: 'sides'
296 , dragPadding: {
297 left:8
298 , right:16
299 , top:0
300 , bottom:8
301 }
302 , lastWidth: 0
303 , lastHeight: 0
304 , autoScroll: false
305 // }}}
306 // {{{
307 /**
308 * Called internally to create collapse button
309 * Calls utility method of Ext.LayoutRegion createTool
310 * @param {Element/HTMLElement/String} parentEl element to create the tool in
311 * @param {String} className class of the tool
312 */
313 , createTool : function(parentEl, className){
314 return Ext.LayoutRegion.prototype.createTool(parentEl, className);
315 }
316 // }}}
317 // {{{
318 /**
319 * Set title of the InfoPanel
320 * @param {String} title Title to set
321 * @return {Ext.ux.InfoPanel} this
322 */
323 , setTitle: function(title) {
324 this.title = title;
325 this.titleTextEl.update(title);
326 if(this.icon) {
327 this.titleTextEl.set({
328 style:
329 'background-image:url('
330 + this.icon
331 + ');background-repeat:no-repeat;background-position:3px 50%;padding-left:23px;'
332 });
333 }
334 return this;
335 }
336 // }}}
337 // {{{
338 /**
339 * Get current title
340 * @return {String} Current title
341 */
342 , getTitle: function() {
343 return this.title;
344 }
345 // }}}
346 // {{{
347 /**
348 * Returns body element
349 * This overrides the ContentPanel getEl for convenient access to the body element
350 * @return {Element} this.body
351 */
352 , getEl: function() {
353 return this.body;
354 }
355 // }}}
356 // {{{
357 /**
358 * Update the innerHTML of this element, optionally searching for and processing scripts
359 * @param {String} html The new HTML
360 * @param {Boolean} loadScripts (optional) true to look for and process scripts
361 * @param {Function} callback For async script loading you can be noticed when the update completes
362 * @return {Ext.Element} this
363 */
364 , update: function(html, loadScripts, callback) {
365 this.body.update(html, loadScripts, callback);
366 return this;
367 }
368 // }}}
369 // {{{
370 /**
371 * Expands the panel
372 * @return {Ext.ux.InfoPanel} this
373 */
374 , expand: function() {
375
376 // do nothing if already expanded
377 if(!this.collapsed) {
378 return this;
379 }
380
381 // fire beforeexpand event
382 if(false === this.fireEvent('beforeexpand', this)) {
383 return this;
384 }
385
386 // reset collapsed flag
387 this.collapsed = false;
388
389 // hide shadow
390 if(!this.docked) {
391 this.setShadow(false);
392 }
393
394 // enable resizing
395 if(this.resizer && !this.docked) {
396 this.setResizable(true);
397 }
398
399 // animate expand
400 if(this.animate) {
401 this.body.slideIn('t', {
402 easing: this.easingExpand || null
403 , scope: this
404 , duration: this.duration
405 , callback: this.updateVisuals
406 });
407 }
408
409 // don't animate, just show
410 else {
411 this.body.show();
412 this.updateVisuals();
413 this.fireEvent('animationcompleted', this);
414 }
415
416 // fire expand event
417 this.fireEvent('expand', this);
418
419 return this;
420
421 }
422 // }}}
423 // {{{
424 /**
425 * Toggles the expanded/collapsed states
426 * @return {Ext.ux.InfoPanel} this
427 */
428 , toggle: function() {
429 if(this.collapsed) {
430 this.expand();
431 }
432 else {
433 this.collapse();
434 }
435 return this;
436 }
437 // }}}
438 // {{{
439 /**
440 * Collapses the panel
441 * @return {Ext.ux.InfoPanel} this
442 */
443 , collapse: function() {
444
445 // do nothing if already collapsed or pinned
446 if(this.collapsed || this.pinned) {
447 return this;
448 }
449
450 // fire beforecollapse event
451 if(false === this.fireEvent('beforecollapse', this)) {
452 return this;
453 }
454
455 if(this.bodyScroll /*&& !Ext.isIE*/) {
456 this.body.setStyle('overflow','hidden');
457 }
458
459 // set collapsed flag
460 this.collapsed = true;
461
462 // hide shadow
463 this.setShadow(false);
464
465 // disable resizing of collapsed panel
466 if(this.resizer) {
467 this.setResizable(false);
468 }
469
470 // animate collapse
471 if(this.animate) {
472 this.body.slideOut('t', {
473 easing: this.easingCollapse || null
474 , scope: this
475 , duration: this.duration
476 , callback: this.updateVisuals
477 });
478 }
479
480 // don't animate, just hide
481 else {
482 this.body.hide();
483 this.updateVisuals();
484 this.fireEvent('animationcompleted', this);
485 }
486
487 // fire collapse event
488 this.fireEvent('collapse', this);
489
490 return this;
491
492 }
493 // }}}
494 // {{{
495 /**
496 * Called internally to update class of the collapse button
497 * as part of expand and collapse methods
498 *
499 * @return {Ext.ux.InfoPanel} this
500 */
501 , updateVisuals: function() {
502
503 // handle collapsed state
504 if(this.collapsed) {
505 if(this.showPin) {
506 this.collapseBtn.show();
507 this.stickBtn.hide();
508 }
509 Ext.fly(this.collapseBtn.dom.firstChild).replaceClass(
510 'x-layout-collapse-south', 'x-layout-collapse-east'
511 );
512 this.body.replaceClass('x-dock-panel-body-expanded', 'x-dock-panel-body-collapsed');
513 this.titleEl.replaceClass('x-dock-panel-title-expanded', 'x-dock-panel-title-collapsed');
514 }
515
516 // handle expanded state
517 else {
518 if(this.showPin) {
519 if(this.pinned) {
520 Ext.fly(this.stickBtn.dom.firstChild).replaceClass('x-layout-stick', 'x-layout-stuck');
521 this.titleEl.addClass('x-dock-panel-title-pinned');
522 }
523 else {
524 Ext.fly(this.stickBtn.dom.firstChild).replaceClass('x-layout-stuck', 'x-layout-stick');
525 this.titleEl.removeClass('x-dock-panel-title-pinned');
526 }
527 this.collapseBtn.hide();
528 this.stickBtn.show();
529 }
530 else {
531 Ext.fly(this.collapseBtn.dom.firstChild).replaceClass(
532 'x-layout-collapse-east', 'x-layout-collapse-south'
533 );
534 }
535 this.body.replaceClass('x-dock-panel-body-collapsed', 'x-dock-panel-body-expanded');
536 this.titleEl.replaceClass('x-dock-panel-title-collapsed', 'x-dock-panel-title-expanded');
537 }
538
539 // show shadow if necessary
540 if(!this.docked) {
541 this.setShadow(true);
542 }
543
544 if(this.bodyScroll && !this.docked && !this.collapsed /*&& !Ext.isIE*/) {
545 this.body.setStyle('overflow', 'auto');
546 }
547
548 this.constrainToDesktop();
549
550 // fire animationcompleted event
551 this.fireEvent('animationcompleted', this);
552
553 // clear visibility style of body's children
554 var kids = this.body.select('div[className!=x-grid-viewport],input{visibility}');
555 // var kids = this.body.select('div,input{visibility}');
556 kids.setStyle.defer(1, kids, ['visibility','']);
557
558 // restore visibility of grid-viewport if any
559 // var gvp = this.body.select('div.x-grid-viewport');
560 // gvp.setStyle('visibility', this.collapsed ? 'hidden' : 'visible');
561
562 return this;
563 }
564 // }}}
565 // {{{
566 /**
567 * Creates toolbar
568 * @param {Array} config Configuration for Ext.Toolbar
569 * @param {Boolean} bottom true to create bottom toolbar. (defaults to false = top toolbar)
570 * @return {Ext.Toolbar} Ext.Toolbar object
571 */
572 , createToolbar: function(config, bottom) {
573 var create = {tag:'div'}, tbEl;
574 config = config || null;
575 if(bottom) {
576 tbEl = Ext.DomHelper.append(this.body, create, true);
577 tbEl.addClass('x-dock-panel-toolbar-bottom');
578 }
579 else {
580 tbEl = Ext.DomHelper.insertFirst(this.body, create, true);
581 tbEl.addClass('x-dock-panel-toolbar');
582 }
583 this.toolbar = new Ext.Toolbar(tbEl, config);
584 return this.toolbar;
585 }
586 // }}}
587 // {{{
588 /**
589 * Set the panel draggable
590 * Uses lazy creation of dd object
591 * @param {Boolean} enable pass false to disable dragging
592 * @return {Ext.ux.InfoPanel} this
593 */
594 , setDraggable: function(enable) {
595
596 if(!this.draggable) {
597 return this;
598 }
599
600 // lazy create proxy
601 var dragTitleEl;
602 if(!this.proxy) {
603 this.proxy = this.el.createProxy('x-dlg-proxy');
604
605 // setup title
606 dragTitleEl = Ext.DomHelper.append(this.proxy, {tag:'div'}, true);
607 dragTitleEl.update(this.el.dom.firstChild.innerHTML);
608 dragTitleEl.dom.className = this.el.dom.firstChild.className;
609 if(this.collapsed && Ext.isIE) {
610 dragTitleEl.dom.style.borderBottom = "0";
611 }
612
613 this.proxy.hide();
614 this.proxy.setOpacity(0.5);
615 this.dd = new Ext.dd.DDProxy(this.el.dom, 'PanelDrag', {
616 dragElId: this.proxy.id
617 , scroll: false
618 });
619 this.dd.scroll = false;
620 this.dd.afterDrag = function() {
621 if(this.panel && this.panel.shadow && !this.panel.docked) {
622 this.panel.shadow.show(this.panel.el);
623 }
624 };
625
626 this.constrainToDesktop();
627 Ext.EventManager.onWindowResize(this.moveToViewport, this);
628 }
629
630 this.dd.panel = this;
631 this.dd.setHandleElId(this.titleEl.id);
632 if(false === enable) {
633 this.dd.lock();
634 }
635 else {
636 this.dd.unlock();
637 }
638
639 return this;
640 }
641 // }}}
642 // {{{
643 /**
644 * Set the panel resizable
645 * Uses lazy creation of the resizer object
646 * @param {Boolean} pass false to disable resizing
647 * @return {Ext.ux.InfoPanel} this
648 */
649 , setResizable: function(enable) {
650
651 if(!this.resizable) {
652 return this;
653 }
654
655 // {{{
656 // lazy create resizer
657 if(!this.resizer) {
658
659 // {{{
660 // create resizer
661 this.resizer = new Ext.Resizable(this.el, {
662 handles: 's w e sw se'
663 , minWidth: this.minWidth || this.tm.getWidth(this.getTitle()) + 56 || 48
664 , maxWidth: this.maxWidth || 9999
665 , minHeight: this.minHeight || 48
666 , maxHeight: this.maxHeight || 9999
667 , transparent: true
668 , draggable: false
669 });
670 // }}}
671 // {{{
672 // install event handlers
673 this.resizer.on({
674 beforeresize: {
675 scope:this
676 , fn: function(resizer, e) {
677 var viewport = this.getViewport();
678 var box = this.getBox();
679
680 var pos = resizer.activeHandle.position;
681
682 // left constraint
683 if(pos.match(/west/)) {
684 resizer.minX = viewport.x + (this.dragPadding.left || 8);
685 }
686
687 // down constraint
688 if(pos.match(/south/)) {
689 resizer.oldMaxHeight = resizer.maxHeight;
690 resizer.maxHeight = viewport.y + viewport.height - box.y - (this.dragPadding.bottom || 8);
691 }
692
693 // right constraint
694 if(pos.match(/east/)) {
695 resizer.oldMaxWidth = resizer.maxWidth;
696 resizer.maxWidth = viewport.x + viewport.width - box.x - (this.dragPadding.right || 10);
697 }
698 }}
699 , resize: {
700 scope: this
701 , fn: function(resizer, width, height, e) {
702 resizer.maxHeight = resizer.oldMaxHeight || resizer.maxHeight;
703 resizer.maxWidth = resizer.oldMaxWidth || resizer.maxWidth;
704 this.setSize(width, height);
705 this.constrainToDesktop();
706 this.fireEvent('boxchange', this, this.el.getBox());
707 this.fireEvent('resize', this, width, height);
708 }}
709 });
710 // }}}
711
712 }
713 // }}}
714
715 this.resizer.enabled = enable;
716
717 // this is custom override of Ext.Resizer
718 this.resizer.showHandles(enable);
719
720 return this;
721 }
722 // }}}
723 // {{{
724 /**
725 * Called internally to clip passed width and height to viewport
726 * @param {Integer} w width
727 * @param {Integer} h height
728 * @return {Object} {width:safeWidth, height:safeHeight}
729 */
730 , safeSize: function(w, h) {
731 var viewport = this.getViewport();
732 var box = this.getBox();
733 var gap = 0;
734 var safeSize = {width:w, height:h};
735
736 safeSize.height =
737 box.y + h + this.dragPadding.bottom + gap > viewport.height + viewport.y
738 ? viewport.height - box.y + viewport.y - this.dragPadding.bottom - gap
739 : safeSize.height
740 ;
741
742 safeSize.width =
743 box.x + w + this.dragPadding.right + gap > viewport.width + viewport.x
744 ? viewport.width - box.x + viewport.x - this.dragPadding.right - gap
745 : safeSize.width
746 ;
747
748 return safeSize;
749 }
750 // }}}
751 // {{{
752 /**
753 * Called internally to get current viewport
754 * @param {Element/HTMLElement/String} desktop Element to get size and position of
755 * @return {Object} viewport {x:x, y:y, width:width, height:height} x and y are page coords
756 */
757 , getViewport: function(desktop) {
758
759 desktop = desktop || this.desktop || document.body;
760 var viewport = Ext.get(desktop).getViewSize();
761 var xy;
762 if(document.body === desktop.dom) {
763 viewport.x = 0;
764 viewport.y = 0;
765 }
766 else {
767 xy = desktop.getXY();
768 viewport.x = isNaN(xy[0]) ? 0 : xy[0];
769 viewport.y = isNaN(xy[1]) ? 0 : xy[1];
770 }
771
772 return viewport;
773 }
774 // }}}
775 // {{{
776 /**
777 * Sets the size of the panel. Demanded size is clipped to the viewport
778 *
779 * @param {Integer} w width to set
780 * @param {Integer} h height to set
781 * @return {Ext.ux.InfoPanel} this
782 */
783 , setSize: function(w, h) {
784 var safeSize = this.safeSize(w, h);
785 this.setWidth(safeSize.width);
786 this.setHeight(safeSize.height);
787
788 if(!this.docked) {
789 this.setShadow(true);
790 }
791 }
792 // }}}
793 // {{{
794 /**
795 * Sets the width of the panel. Demanded width is clipped to the viewport
796 *
797 * @param {Integer} w width to set
798 * @return {Ext.ux.InfoPanel} this
799 */
800 , setWidth: function(w) {
801 this.el.setWidth(w);
802 this.body.setStyle('width','');
803 if(!this.docked) {
804 this.setShadow(true);
805 }
806 this.lastWidth = w;
807
808 return this;
809 }
810 // }}}
811 // {{{
812 /**
813 * Sets the height of the panel. Demanded height is clipped to the viewport
814 *
815 * @param {Integer} h height to set
816 * @return {Ext.ux.InfoPanel} this
817 */
818 , setHeight: function(h) {
819 var newH = h - this.titleEl.getHeight();
820 if(0 < newH) {
821 this.body.setHeight(newH);
822 }
823 else {
824 this.body.setStyle('height','');
825 }
826
827 if(!this.docked) {
828 this.setShadow(true);
829 }
830 // this.lastHeight = h;
831 this.el.setStyle('height','');
832
833 return this;
834 }
835 // }}}
836 // {{{
837 /**
838 * Called internally to set x, y, width and height of the panel
839 *
840 * @param {Object} box
841 * @return {Ext.ux.InfoPanel} this
842 */
843 , setBox: function(box) {
844 this.el.setBox(box);
845 this.moveToViewport();
846 this.setSize(box.width, box.height);
847
848 return this;
849 }
850 // }}}
851 // {{{
852 /**
853 * Called internally to get the box of the panel
854 *
855 * @return {Object} box
856 */
857 , getBox: function() {
858 return this.el.getBox();
859 }
860 // }}}
861 // {{{
862 /**
863 * Turns shadow on/off
864 * Uses lazy creation of the shadow object
865 * @param {Boolean} shadow pass false to hide, true to show the shadow
866 * @return {Ext.ux.InfoPanel} this
867 */
868 , setShadow: function(shadow) {
869
870 // if I have shadow but shouldn't use it
871 if(this.shadow && !this.useShadow) {
872 this.shadow.hide();
873 return this;
874 }
875
876 // if I shouldn't use shadow
877 if(!this.useShadow) {
878 return this;
879 }
880
881 // if I don't have shadow
882 if(!this.shadow) {
883 this.shadow = new Ext.Shadow({mode:this.shadowMode});
884 }
885
886 // show or hide
887 var zindex;
888 if(shadow) {
889 this.shadow.show(this.el);
890
891 // fix the Ext shadow z-index bug
892 zindex = parseInt(this.el.getStyle('z-index'), 10);