001    /*
002     * Copyright 2006 Mat Gessel <mat.gessel@gmail.com>
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package asquare.gwt.tk.client.ui;
017    
018    import java.util.List;
019    import java.util.Vector;
020    
021    import asquare.gwt.tk.client.ui.behavior.*;
022    import asquare.gwt.tk.client.ui.commands.FocusCommand;
023    import asquare.gwt.tk.client.util.DomUtil;
024    
025    import com.google.gwt.core.client.GWT;
026    import com.google.gwt.user.client.*;
027    import com.google.gwt.user.client.ui.HasFocus;
028    import com.google.gwt.user.client.ui.Widget;
029    import com.google.gwt.user.client.ui.impl.FocusImpl;
030    
031    /**
032     * A modal dialog featuring:
033     * <ul>
034     * <li>an optional caption</li>
035     * <li>support for widgets in the caption</li>
036     * <li>a background "{@link GlassPanel}" which blocks user interaction with
037     * the page (also stylable for the "light box" effect). </li>
038     * <li>automatic centering in browser's main viewport (regardless of document
039     * scroll)</li>
040     * <li>resizable: if you set the size of the content area the layout will
041     * maintain integrity on all platforms &amp; quirks/strict modes</li>
042     * <li>minimum size specification for content area (optional, default is 200 x
043     * 75px)</li>
044     * <li>focus containment &amp; management</li>
045     * <li>option to restore focus to triggering widget when dialog is hidden</li>
046     * </ul>
047     * The caption has the following properties:
048     * <ul>
049     * <li>inserted at top of dialog</li>
050     * <li>can be dragged to move the dialog</li>
051     * <li>disallows text selection</li>
052     * <li>has the <code>Caption</code> style name</li>
053     * </ul>
054     * <h3>Usage Notes</h3>
055     * <ul>
056     * <li>When adding a Panel, the panel's child widgets are not automatically
057     * added to the {@link #getFocusModel() focus model}. </li>
058     * <li>Call {@link #removeController(Controller)} with
059     * <code>{@link TabFocusController}.class</code> to disable built-in focus management.</li>
060     * <li>{@link #setWidth(String)} can result in a dialog which is wider than the
061     * caption. Use {@link #setContentWidth(String)} instead. </li>
062     * <li>IE6 ignores table cell heights in strict mode. This means that you can't
063     * set the dialog height and use "1px" to force minimum caption height.<br/>
064     * Workaround: set the height of the content cell and let the dialog auto-size.</li>
065     * <li>Opera: dialog does not receive keystrokes unless the cursor is over the
066     * dialog or a child of the dialog is focused.</li>
067     * </ul>
068     * <h3>CSS Style Rules</h3>
069     * <ul class='css'>
070     * <li>.tk-ModalDialog { the dialog itself }</li>
071     * <li>.tk-ModalDialog-glassPanel { the background panel behind the dialog }</li>
072     * <li>.tk-ModalDialog .Caption { the caption }</li>
073     * <li>.tk-ModalDialog-content { the content area }</li>
074     * <li>.tk-ModalDialog-dragging { applied to the dialog whilst dragging }</li>
075     * </ul>
076     * <h3>Example</h3>
077     * 
078     * <pre>
079     * final Button showDialogButton = new Button(&quot;Focus management&quot;);
080     * showDialogButton.addClickListener(new ClickListener()
081     * {
082     *      public void onClick(Widget sender)
083     *      {
084     *              final ModalDialog inputDialog = new ModalDialog();
085     *              inputDialog.setCaption(&quot;Input&quot;, false);
086     *              inputDialog.add(new Label(&quot;Enter a value&quot;));
087     *              inputDialog.add(new TextBox());
088     *              inputDialog.add(new Button(&quot;OK&quot;, new ClickListener()
089     *              {
090     *                      public void onClick(Widget sender)
091     *                      {
092     *                              inputDialog.hide();
093     *                      }
094     *              }));
095     * 
096     *              inputDialog.show(showDialogButton);
097     *      }
098     * });
099     * </pre>
100     */
101    public class ModalDialog extends CPopupPanel
102    {
103            public static final String STYLENAME_DIALOG = "tk-ModalDialog";
104            public static final String STYLENAME_GLASSPANEL = "tk-ModalDialog-glassPanel";
105            public static final String STYLENAME_CAPTION = "Caption";
106            public static final String STYLENAME_CONTENT = "tk-ModalDialog-content";
107            public static final String STYLENAME_DRAGGING = "tk-ModalDialog-dragging";
108            
109            protected static final FocusImpl s_focusImpl = (FocusImpl) GWT.create(FocusImpl.class);
110            
111            private final Element m_focusable = s_focusImpl.createFocusable();
112            private final GlassPanel m_glassPanel = new GlassPanel();
113            private final RowPanel m_panel = new RowPanel();
114            private final Element m_contentTd;
115            
116            private FocusModel m_focusModel;
117            private CaptionWrapper m_caption = null;
118            private int m_minContentWidth = 200;
119            private int m_minContentHeight = 75;
120            private HasFocus m_focusOnCloseWidget;
121            
122            public ModalDialog()
123            {
124                setFocusModel(new FocusModel());
125                setStyleName(STYLENAME_DIALOG);
126                    DOM.appendChild(getElement(), m_focusable);
127                    m_glassPanel.addStyleName(STYLENAME_GLASSPANEL);
128                    m_panel.addCell();
129                    m_panel.addCellStyleName(STYLENAME_CONTENT);
130                    m_contentTd = m_panel.getCellElement(0);
131                    super.setWidget(m_panel);
132            }
133            
134            /*
135             * (non-Javadoc)
136             * @see asquare.gwt.tk.client.ui.CPopupPanel#createControllers()
137             */
138            protected List createControllers()
139            {
140                    List result = new Vector();
141                    result.add(GWT.create(PositionDialogController.class));
142                    result.add(GWT.create(InitializeFocusController.class));
143                    result.add(GWT.create(TabFocusController.class));
144                    result.add(GWT.create(FocusOnCloseController.class));
145                    return result;
146            }
147            
148            /**
149             * A factory method which gives a subclass the opportunity to override default 
150             * controller creation.
151             * 
152             * @return a List with 0 or more controllers, or <code>null</code>
153             */
154            protected List createCaptionControllers()
155            {
156                    List result = new Vector();
157                    result.add(PreventSelectionController.getInstance());
158                    result.add(new DragStyleController(this, STYLENAME_DRAGGING));
159                    result.add(new DragController(new DragPopupGesture(this)));
160                    return result;
161            }
162            
163            /**
164             * Get the focus model for this dialog. 
165             */
166            public FocusModel getFocusModel()
167            {
168                    return m_focusModel;
169            }
170            
171            /**
172             * Set the focus model for this dialog. 
173             */
174            public void setFocusModel(FocusModel focusModel)
175            {
176                    m_focusModel = focusModel;
177                    TabFocusController tabFocusController = (TabFocusController) getController(TabFocusController.class);
178                    if (tabFocusController != null)
179                    {
180                            tabFocusController.setModel(focusModel);
181                    }
182            }
183            
184            /**
185             * Get the minimum height of the content panel. 
186             * 
187             * @return the minimum height in pixels
188             */
189            public int getContentMinHeight()
190            {
191                    return m_minContentHeight;
192            }
193            
194            /**
195             * Set the minimum height of the content panel. The default is <code>75 px</code>.
196             * Set to <code>0</code> to disable this feature.
197             * 
198             * @param minHeight the minimum height in pixels
199             */
200            public void setContentMinHeight(int minHeight)
201            {
202                    this.m_minContentHeight = minHeight;
203            }
204            
205            /**
206             * Get the minimum width of the content panel.
207             * 
208             * @return the minimum width in pixels
209             */
210            public int getContentMinWidth()
211            {
212                    return m_minContentWidth;
213            }
214            
215            /**
216             * Set the minimum width of the content panel. The default is <code>200 px</code>.
217             * Set to <code>0</code> to disable this feature.
218             * 
219             * @param minWidth the minimum width in pixels
220             */
221            public void setContentMinWidth(int minWidth)
222            {
223                    this.m_minContentWidth = minWidth;
224            }
225            
226            /**
227             * Set the desired width of the content panel. The minimum width property
228             * will take precedence if applicable.
229             * 
230             * @param width the width in CSS measurements
231             */
232            public void setContentWidth(String width)
233            {
234                    DOM.setAttribute(m_contentTd, "width", width);
235            }
236            
237            /**
238             * Set the desired height of the content panel. The minimum height property
239             * will take precedence if applicable.
240             * 
241             * @param height the height in CSS measurements
242             */
243            public void setContentHeight(String height)
244            {
245                    DOM.setAttribute(m_contentTd, "height", height);
246            }
247            
248            /**
249             * Get the actual width of the content panel. This does not work until the
250             * dialog has been shown.
251             * 
252             * @return the width in pixels
253             */
254            public int getContentOffsetWidth()
255            {
256                    return DOM.getIntAttribute(m_contentTd, "offsetWidth");
257            }
258            
259            /**
260             * Get the actual height of the content panel. This does not work until the
261             * dialog has been shown.
262             * 
263             * @return the height in pixels
264             */
265            public int getContentOffsetHeight()
266            {
267                    return DOM.getIntAttribute(m_contentTd, "offsetHeight");
268            }
269            
270            /**
271             * Adds a widget to the content area of this dialog. Multiple widgets may be
272             * added. The widget will be added to the focus model if it implements
273             * HasFocus and does not have a tabIndex < 0.
274             * 
275             * @param w a widget
276             */
277            public void add(Widget w)
278            {
279                    // pre: content row is created and is last row
280                    m_panel.addWidget(w, false);
281                    
282                    if (w instanceof HasFocus)
283                    {
284                            m_focusModel.add((HasFocus) w);
285                    }
286            }
287            
288            /**
289             * Not supported. Use {@link #add(Widget)} instead. 
290             * 
291             * @throws UnsupportedOperationException
292             */
293            public void setWidget(Widget w)
294            {
295                    throw new UnsupportedOperationException();
296            }
297            
298            /**
299             * Removes a widget from the content area of this dialog. Do not use for
300             * caption widget. Use {@link #setCaption(Widget) setCaption(null)} instead.
301             * 
302             * @param w the widget to remove
303             * @throws IllegalArgumentException if <code>w</code> is in the caption. 
304             */
305            public boolean remove(Widget w)
306            {
307                    if (m_caption != null && w == m_panel.getWidgetAt(0, 0))
308                            throw new IllegalArgumentException();
309                    
310                    if (w instanceof HasFocus)
311                    {
312                            m_focusModel.remove((HasFocus) w);
313                    }
314                    
315                    // pre: content row is created and is last row
316                    return m_panel.remove(w, false);
317            }
318            
319            /**
320             * Sets the contents of the caption to the specified text, clearing any
321             * previous contents from the caption.
322             * 
323             * @param text the caption text
324             * @param asHtml true to treat <code>text</code> as html
325             * @see #setCaption(Widget)
326             */
327            public void setCaption(String text, boolean asHtml)
328            {
329                    if (m_caption != null)
330                    {
331                            clearCaption();
332                    }
333                    createCaption();
334                    if (asHtml)
335                    {
336                            DOM.setInnerHTML(m_panel.getCellElement(0), text);
337                    }
338                    else
339                    {
340                            DOM.setInnerText(m_panel.getCellElement(0), text);
341                    }
342            }
343            
344            /**
345             * Set a widget as the sole child of the caption, clearing any
346             * previous contents from the caption.
347             * 
348             * @param w a widget
349             */
350            public void setCaption(Widget w)
351            {
352                    if (m_caption != null)
353                    {
354                            clearCaption();
355                    }
356                    if (w != null)
357                    {
358                            createCaption();
359                            m_panel.addWidgetTo(w, 0);
360                    }
361            }
362            
363            /*
364             * pre: m_caption == null
365             */
366            private void createCaption()
367            {
368                    m_panel.insertCell(0);
369                    m_caption = new CaptionWrapper(m_panel.getCellElement(0), createCaptionControllers());
370                    if (isAttached())
371                    {
372                            m_caption.onAttach();
373                    }
374            }
375            
376            /*
377             * pre: m_caption != null
378             */
379            private void clearCaption()
380            {
381                    if (isAttached())
382                    {
383                            m_caption.onDetach();
384                    }
385                    m_caption = null;
386                    m_panel.removeCell(0);
387            }
388            
389            /**
390             * Get the element that forms the content area of the dialog. 
391             */
392            public Element getContentElement()
393            {
394                    return m_contentTd;
395            }
396            
397            /**
398             * Get the GlassPanel which is displayed behind the dialog. 
399             */
400            public GlassPanel getGlassPanel()
401            {
402                    return m_glassPanel;
403            }
404            
405            /**
406             * Get the widget which will be focused after the dialog is closed. This
407             * property is only available while the dialog is visible.
408             * 
409             * @return a widget or <code>null</code>
410             */
411            public HasFocus getFocusOnCloseWidget()
412            {
413                    return m_focusOnCloseWidget;
414            }
415            
416            /**
417             * Shows the glasspanel and dialog then focuses the widget selected in 
418             * the focus model.
419             */
420            public void show()
421            {
422                    show(null);
423            }
424            
425            /**
426             * Shows the glasspanel and dialog then focuses the widget selected in 
427             * the focus model.
428             * 
429             * @param focusOnCloseWidget a widget to focus after this dialog is closed
430             */
431            public void show(HasFocus focusOnCloseWidget)
432            {
433                    m_focusOnCloseWidget = focusOnCloseWidget;
434                    m_glassPanel.show();
435                    Controller positionDialogController = getController(PositionDialogController.class);
436                    if (positionDialogController != null)
437                    {
438                            (( PositionDialogController) positionDialogController).beforeAttach(this);
439                    }
440                    super.show();
441            }
442            
443            /**
444             * Detaches the dialog from the DOM (it will be garbage collected if there
445             * are no references to it). Has no effect if the dialog is not showing. 
446             * 
447             * @see com.google.gwt.user.client.ui.PopupPanel#hide()
448             */
449            public void hide()
450            {
451                    super.hide();
452                    m_glassPanel.hide();
453                    m_focusOnCloseWidget = null;
454            }
455            
456            /*
457             *  (non-Javadoc)
458             * @see com.google.gwt.user.client.ui.Widget#onAttach()
459             */
460            protected void onAttach()
461            {
462                    if (isAttached())
463                            return;
464                    
465                    super.onAttach();
466                    
467                    if (m_caption != null)
468                            m_caption.onAttach();   
469            }
470            
471            /*
472             *  (non-Javadoc)
473             * @see com.google.gwt.user.client.ui.Widget#onDetach()
474             */
475            protected void onDetach()
476            {
477                    if(! isAttached())
478                            return;
479                    
480                    try
481                    {
482                            if (m_caption != null)
483                                    m_caption.onDetach();
484                    }
485                    finally
486                    {
487                            super.onDetach();
488                    }
489            }
490            
491            /**
492             * Provides event support for the caption element. 
493             */
494            protected class CaptionWrapper extends CWidget
495            {
496                    /*
497                     * The fun thing about GWT is that elements are not encapsulated. We can
498                     * take an element (e.g. td, div) from another container and create a
499                     * fly-weight widget with it to handle events. The parent container is none
500                     * the wiser. Of course, we have to call onAttach() to receive events and
501                     * onDetach() to prevent memory leaks.
502                     */
503                    protected CaptionWrapper(Element captionElement, List controllers)
504                    {
505                            super(captionElement, controllers);
506                            setStyleName(STYLENAME_CAPTION);
507                    }
508                    
509                    protected void onAttach()
510                    {
511                            super.onAttach();
512                    }
513                    
514                    protected void onDetach()
515                    {
516                            super.onDetach();
517                    }
518            }
519            
520            /**
521             * Sets the initial focus when the dialog is shown.
522             * <p>
523             * This class is not extensible because it depends on private data in the
524             * dialog. It can, however, be instantiated, added to and removed from it's
525             * parent dialog without causing problems.
526             */
527            public static final class InitializeFocusController extends ControllerAdaptor
528            {
529                    public InitializeFocusController()
530                    {
531                            super(InitializeFocusController.class);
532                    }
533                    
534                    public void plugIn(Widget widget)
535                    {
536                            final ModalDialog dialog = (ModalDialog) widget;
537                            HasFocus focusWidget = dialog.getFocusModel().getFocusWidget();
538                            Command focusCommand;
539                            if (focusWidget != null)
540                            {
541                                    focusCommand = new FocusCommand(focusWidget);
542                            }
543                            else
544                            {
545                                    focusCommand = new Command()
546                                    {
547                                            public void execute()
548                                            {
549                                                    s_focusImpl.focus(dialog.m_focusable);
550                                            }
551                                    };
552                            }
553                            DeferredCommand.add(focusCommand);
554                    }
555            }
556            
557            /**
558             * A controller which focuses a widget when the dialog is hidden. 
559             */
560            public static class FocusOnCloseController extends ControllerAdaptor
561            {
562                    public FocusOnCloseController()
563                    {
564                            super(FocusOnCloseController.class);
565                    }
566                    
567                    public void unplug(Widget widget)
568                    {
569                            HasFocus focusOnCloseWidget = ((ModalDialog) widget).getFocusOnCloseWidget();
570                            if (focusOnCloseWidget != null)
571                            {
572                                    DeferredCommand.add(new FocusCommand(focusOnCloseWidget));
573                            }
574                    }
575            }
576            
577            /**
578             * A controller which encapsulates dialog sizing and positioning logic.
579             * Although this class doesn't react to events, we're going to implement
580             * Controller to enable dynamic configuration via
581             * {@link ControllerSupport#getController(Class)}.
582             */
583            public static class PositionDialogController extends ControllerAdaptor
584            {
585                    private int m_viewportWidth;
586                    private int m_viewportHeight;
587                    
588                    public PositionDialogController()
589                    {
590                            super(PositionDialogController.class);
591                    }
592                    
593                    protected int getViewportWidth()
594                    {
595                            return m_viewportWidth;
596                    }
597                    
598                    protected void setViewportWidth(int centerX)
599                    {
600                            m_viewportWidth = centerX;
601                    }
602                    
603                    protected int getViewportHeight()
604                    {
605                            return m_viewportHeight;
606                    }
607                    
608                    protected void setViewportHeight(int centerY)
609                    {
610                            m_viewportHeight = centerY;
611                    }
612                    
613                    public void plugIn(Widget widget)
614                    {
615                            afterAttach((ModalDialog) widget);
616                    }
617                    
618                    protected int applyMinWidthConstraint(ModalDialog dialog, int contentWidth)
619                    {
620                            return Math.max(dialog.getContentMinWidth(), contentWidth);
621                    }
622                    
623                    protected int applyMaxWidthConstraint(ModalDialog dialog, int contentWidth)
624                    {
625                            return Math.min(getViewportWidth() / 2, contentWidth);
626                    }
627                    
628                    protected int applyMinHeightConstraint(ModalDialog dialog, int contentHeight)
629                    {
630                            return Math.max(dialog.getContentMinHeight(), contentHeight);
631                    }
632                    
633                    /**
634                     * Template method for updating the content width.
635                     * <ul>
636                     * <li>post: the dialog content width (style attribute) will be updated
637                     * <li>post: the <code>dialogWidth</code> property will be finalized
638                     * </ul>
639                     */
640                    protected int updateContentWidth(ModalDialog dialog, int contentWidth)
641                    {
642                            int dialogWidth = dialog.getOffsetWidth();
643                            int contentWidthInitial = dialog.getContentOffsetWidth();
644                            
645                            if (contentWidth != contentWidthInitial)
646                            {
647                                    /*
648                                     * Note: setting the content width does *not* result in
649                                     * immediate re-layout of parent dialog. The dialog width
650                                     * property will be unchanged if we refetch it. 
651                                     */
652                                    dialog.setContentWidth(contentWidth + "px");
653                                    
654                                    /*
655                                     * Refetch the content width. The non-reflowable content (e.g. a
656                                     * wide image or long TextBox) may force a width greater than
657                                     * the intended value. This returns the old value in IE6.
658                                     */
659                                    contentWidth = dialog.getContentOffsetWidth();
660                                    
661                                    /*
662                                     * The change to the content width will result in a change in the
663                                     * overall dialog's width. Try to predict the width after the
664                                     * pending re-layout.
665                                     */
666                                    final int padding = dialogWidth - contentWidthInitial;
667                                    dialogWidth = contentWidth + padding;
668                            }
669                            
670                            return dialogWidth;
671                    }
672                    
673                    /**
674                     * Template method for updating the content height.
675                     * <ul>
676                     * <li>post: the dialog content height (style attribute) will be updated
677                     * <li>post: the <code>dialogHeight</code> property will be finalized
678                     * </ul>
679                     */
680                    protected int updateContentHeight(ModalDialog dialog, int contentHeight)
681                    {
682                            int dialogHeight = dialog.getOffsetHeight();
683                            int contentHeightInitial = dialog.getContentOffsetHeight();
684                            
685                            if (contentHeight != contentHeightInitial)
686                            {
687                                    /*
688                                     * Refetch the content height. The non-reflowable content 
689                                     * may force a height greater than the intended value.
690                                     */
691                                    contentHeight = dialog.getContentOffsetHeight();
692                                    
693                                    /*
694                                     * The change to the content height will result in a change in the
695                                     * overall dialog's height. Try to predict the height after the
696                                     * pending re-layout.
697                                     */
698                                    final int padding = dialogHeight - contentHeightInitial;
699                                    dialogHeight = contentHeight + padding;
700                            }
701                            
702                            return dialogHeight;
703                    }
704                    
705                    /**
706                     * Template method for setting the dialog's final position. This
707                     * implementation prevents the dialog being positioned above or left of
708                     * the origin.
709                     * 
710                     * @param dialog
711                     * @param dialogWidth
712                     * @param dialogHeight
713                     */
714                    protected void setDialogPosition(ModalDialog dialog, int dialogWidth, int dialogHeight)
715                    {
716                            int left = DomUtil.getViewportScrollX() + getViewportWidth() / 2 - dialogWidth / 2;
717                            int top = DomUtil.getViewportScrollY() + getViewportHeight() / 2 - dialogHeight / 2;
718                            
719                            // set the position
720                            dialog.setPopupPosition((left < 0) ? 0 : left, (top < 0) ? 0 : top);
721                    }
722                    
723                    public void beforeAttach(ModalDialog dialog)
724                    {
725                            /*
726                             * Attaching the dialog sometimes temporarily add a scroll bar,
727                             * throwing off the viewport dimensions. This can happen even if a
728                             * scroll bar is never displayed. 
729                             */
730                            setViewportWidth(DomUtil.getViewportWidth());
731                            setViewportHeight(DomUtil.getViewportHeight());
732                            
733                            /*
734                             * Guard against flicker when repositioning dialog. 
735                             * This may not be necessary, but it can't hurt. 
736                             */
737                            DomUtil.setStyleAttribute(dialog, "visibility", "hidden");
738                            
739                            /*
740                             * This should eliminate scrollbar flicker in Opera/FF[Mac] unless
741                             * the dialog height > viewport height.
742                             */
743                            dialog.setPopupPosition(0, 0);
744                    }
745                    
746                    public void afterAttach(ModalDialog dialog)
747                    {
748                            int dialogWidth, dialogHeight;
749                            
750                            /**
751                             * Apply width constraints
752                             * Get/estimate dialog width. 
753                             */
754                            dialogWidth = (updateContentWidth(dialog, applyMinWidthConstraint(dialog, applyMaxWidthConstraint(dialog, dialog.getContentOffsetWidth()))));
755                            
756                            /*
757                             * Apply height constraint last because width constraints 
758                             * may have changed content height. 
759                             * Get/estimate dialog height. 
760                             */
761                            dialogHeight = updateContentHeight(dialog, applyMinHeightConstraint(dialog, dialog.getContentOffsetHeight()));
762                            
763                            setDialogPosition(dialog, dialogWidth, dialogHeight);
764                            DomUtil.setStyleAttribute(dialog, "visibility", "visible");
765                    }
766            }
767            
768            public static class PositionDialogControllerIE6 extends PositionDialogController
769            {
770                    protected int updateContentWidth(ModalDialog dialog, int contentWidth)
771                    {
772                            int dialogWidth = dialog.getOffsetWidth();
773                            
774                            if (contentWidth != dialog.getContentOffsetWidth())
775                            {
776                                    dialog.setContentWidth(contentWidth + "px");
777                                    
778                                    /*
779                                     * This magic call forces IE to update the layout. 
780                                     */
781                                    dialogWidth = dialog.getOffsetWidth();
782                            }
783                            
784                            return dialogWidth;
785                    }
786            }
787    }