org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.extensions.ajax.markup.html.modal;

import com.github.openjson.JSONObject;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.json.JSONFunction;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.core.request.handler.PageProvider;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.AbstractRepeater;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.resource.CssResourceReference;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.resource.CoreLibrariesContributor;
import org.apache.wicket.util.io.IClusterable;
import org.apache.wicket.util.lang.EnumeratedType;
import org.apache.wicket.util.string.AppendingStringBuffer;

/**
 * Modal window component.
 * <p>
 * Modal window is a draggable window (with either div or iframe content) that prevent user from
 * interacting the rest of page (using a mask) until the window is closed.
 * <p>
 * If you want this to work under IE, don't attach this component to a &lt;span&gt; tag, make sure
 * you use a &lt;div&gt;.
 * <p>
 * The window is draggable and optionally resizable. The content can be either
 * <ul>
 * <li><b>a component</b> - you need to add the component to modal window (with id obtained using
 * <code>{@link #getContentId()}</code>, or
 * <li><b>a page</b> - you need to pass a <code>{@link PageCreator}</code> instance to a
 * <code>{@link #setPageCreator(ModalWindow.PageCreator)}</code> method.
 * </ul>
 * In case the content is a component, it is not rendered until the window is shown (method
 * <code>{@link #show(IPartialPageRequestHandler)})</code>. The window can be made
 * visible from an ajax handler using
 * <code>{@link #show(IPartialPageRequestHandler)}</code>.
 * <p>
 * To close the window there are multiple options. Static method
 * <code>{@link #close(IPartialPageRequestHandler)}</code> can be used to close the
 * window from a handler of ajax link inside the window. By default the close button in the upper
 * right corner of the window closes it. This behavior can be altered using
 * <code>{@link #setCloseButtonCallback(ModalWindow.CloseButtonCallback)}</code>. If you want to be
 * notified when the window is closed (either using the close button or calling
 * <code>{@link #close(IPartialPageRequestHandler)})</code>, you can use
 * <code>{@link #setWindowClosedCallback(ModalWindow.WindowClosedCallback)}</code>.
 * <p>
 * Title is specified using {@link #setTitle(String)}. Pass <code>true</code> to
 * <code>{@link #setEscapeModelStrings(boolean)}</code> to use unencoded markup in the title.
 * <br>
 * If the content is a page (iframe), the title can remain unset, in that case title from the page
 * inside window will be shown.
 * <p>
 * There are several options to specify the visual properties of the window. In all methods where
 * size is expected, width refers to width of entire window (including frame), height refers to the
 * height of window content (without frame).
 * <p>
 * <ul>
 * <li><code>{@link #setResizable(boolean)}</code> specifies, whether the window can be resized.
 * <li><code>{@link #setInitialWidth(int)}</code> and <code>{@link #setInitialHeight(int)}</code>
 * specify the initial width and height of window. If the window is resizable, the unit of these
 * dimensions is always "px". If the window is not resizable, the unit can be specified using
 * <code>{@link #setWidthUnit(String)}</code> and <code>{@link #setHeightUnit(String)}</code>. If
 * the window is not resizable and the content is a component (not a page), the initial height value
 * can be ignored and the actual height can be determined from the height of the content. To enable
 * this behavior use <code>{@link #setUseInitialHeight(boolean)}</code>.
 * <li>The window position (and size if the window is resizable) can be stored in a cookie, so that
 * it is preserved when window is close. The name of the cookie is specified via
 * <code>{@link #setCookieName(String)}</code>. If the name is <code>null</code>, position is not
 * stored (initial width and height are always used). Default cookie name is null (position is not
 * stored).
 * <li><code>{@link #setMinimalWidth(int)}</code> and <code>{@link #setMinimalHeight(int)}</code>
 * set the minimal dimensions of resizable window.
 * <li><code>{@link #setAutoSize(boolean)}</code> sets whether window size will be automatically
 * adjusted on opening to fit content's width and height. Default is false.<span
 * style="text-decoration: underline"> Doesn't work on IE 6.</span></li>
 * <li>Modal window can chose between two colors of frame.
 * <code>{@link #setCssClassName(String)}</code> sets the dialog css class, possible values are
 * <code>{@link #CSS_CLASS_BLUE}</code> for blue frame and <code>{@link #CSS_CLASS_GRAY}</code> for
 * gray frame.
 * <li>Mask (element that prevents user from interacting the rest of the page) can be either
 * transparent or semitransparent. <code>{@link #setMaskType(ModalWindow.MaskType)}</code> alters
 * this.
 * </ul>
 * Also it is recommended to put the modal window component in markup before any component (i.e.
 * AjaxLink or AjaxButton) that shows it.
 * <p>
 * If you want to use form in modal window component make sure that you put the modal window itself
 * in another form (nesting forms is legal in Wicket) and that the form on modal window is submitted
 * before the window get closed.
 * 
 * @author Matej Knopp
 */
public class ModalWindow extends Panel {
    private static final long serialVersionUID = 1L;

    /** CSS class for window with blue border. */
    public final static String CSS_CLASS_BLUE = "w_blue";

    /** CSS class for window with gray border. */
    public final static String CSS_CLASS_GRAY = "w_silver";

    private static final ResourceReference JAVASCRIPT = new JavaScriptResourceReference(ModalWindow.class,
            "res/modal.js");

    private static final ResourceReference CSS = new CssResourceReference(ModalWindow.class, "res/modal.css");

    /** the default id of the content component */
    public static final String CONTENT_ID = "content";

    /** True while the ModalWindows is showing */
    private boolean shown = false;

    /** empty container - used when no component is added */
    private WebMarkupContainer empty;

    private int minimalWidth = 200;
    private int minimalHeight = 200;
    private String cssClassName = CSS_CLASS_BLUE;
    private int initialWidth = 600;
    private int initialHeight = 400;
    private boolean useInitialHeight = true;
    private boolean resizable = true;
    private String widthUnit = "px";
    private String heightUnit = "px";
    private String cookieName;
    private IModel<String> title = null;
    private MaskType maskType = MaskType.SEMI_TRANSPARENT;
    private boolean autoSize = false;
    private boolean unloadConfirmation = true;

    private PageCreator pageCreator = null;
    private CloseButtonCallback closeButtonCallback = null;
    private WindowClosedCallback windowClosedCallback = null;

    /**
     * Interface for lazy page creation. The advantage of creating page using this interface over
     * just passing a page instance is that page created in <code>{@link #createPage()}</code> will
     * have the pagemap automatically set to the pagemap specified for
     * <code>{@link ModalWindow}</code>.
     * 
     * @author Matej Knopp
     */
    public interface PageCreator extends IClusterable {
        /**
         * Creates a new instance of content page.
         * 
         * @return new page instance
         */
        Page createPage();
    }

    /**
     * Callback for close button that contains a method that is invoked after the button has been
     * clicked. If no callback instance is specified using
     * <code>{@link ModalWindow#setCloseButtonCallback(ModalWindow.CloseButtonCallback)}</code>, no
     * ajax request will be fired. Clicking the button will just close the window.
     * 
     * @author Matej Knopp
     */
    public interface CloseButtonCallback extends IClusterable {
        /**
         * Methods invoked after the button has been clicked. The invocation is done using an ajax
         * call, so <code>{@link org.apache.wicket.ajax.AjaxRequestTarget}</code> instance is
         * available.
         * 
         * @param target
         *            <code>{@link org.apache.wicket.ajax.AjaxRequestTarget}</code> instance bound
         *            with the ajax request.
         * 
         * @return True if the window can be closed (will close the window), false otherwise
         */
        boolean onCloseButtonClicked(AjaxRequestTarget target);
    }

    /**
     * Callback called after the window has been closed. If no callback instance is specified using
     * {@link ModalWindow#setWindowClosedCallback(ModalWindow.WindowClosedCallback)}, no ajax
     * request will be fired.
     * 
     * @author Matej Knopp
     */
    public interface WindowClosedCallback extends IClusterable {
        /**
         * Called after the window has been closed.
         * 
         * @param target
         *            <code>{@link org.apache.wicket.ajax.AjaxRequestTarget}</code> instance bound
         *            with the ajax request.
         */
        void onClose(AjaxRequestTarget target);
    }

    /**
     * Creates a new modal window component.
     * 
     * @param id
     *            Id of component
     */
    public ModalWindow(final String id) {
        super(id);
        init();
    }

    /**
     * Creates a new modal window component.
     * 
     * @param id
     *            Id of component
     * @param model
     *            Model
     */
    public ModalWindow(final String id, final IModel<?> model) {
        super(id, model);
        init();
    }

    /**
     * Initialize
     */
    private void init() {
        setVersioned(false);
        cookieName = null;

        add(empty = new WebMarkupContainer(getContentId()));

        add(newCloseButtonBehavior());
        add(new WindowClosedBehavior());

        // install a default callback that will force
        // WindowClosedBehavior to be executed
        setWindowClosedCallback((WindowClosedCallback) target -> {
            // noop
        });

    }

    @Override
    public void renderHead(final IHeaderResponse response) {
        super.renderHead(response);

        CoreLibrariesContributor.contributeAjax(getApplication(), response);
        response.render(JavaScriptHeaderItem.forReference(JAVASCRIPT));

        ResourceReference cssResource = newCssResource();
        if (cssResource != null) {
            response.render(CssHeaderItem.forReference(cssResource));
        }
    }

    /**
     * Allows to override CSS contribution. Returning null means the CSS will be contributed via
     * other sources, e.g. a global CSS resource.
     * 
     * @return The CSS resource reference or null if CSS is contributed via other means.
     * @see #setCssClassName(String)
     */
    protected ResourceReference newCssResource() {
        return CSS;
    }

    /**
     * Is this window currently showing.
     * 
     * @return the shown
     */
    public boolean isShown() {
        return shown;
    }

    /**
     * Sets the <code>{@link PageCreator}</code> instance. The instance is only used when no custom
     * component has been added to the dialog.
     * 
     * @param creator
     *            <code>{@link PageCreator}</code> instance
     * @return this
     */
    public ModalWindow setPageCreator(final PageCreator creator) {
        setContent(empty);
        pageCreator = creator;
        return this;
    }

    /**
     * Sets the <code>{@link CloseButtonCallback}</code> instance.
     * 
     * @param callback
     *            Callback instance
     * @return this
     */
    public ModalWindow setCloseButtonCallback(final CloseButtonCallback callback) {
        closeButtonCallback = callback;
        return this;
    }

    /**
     * Sets the <code>@{link {@link WindowClosedCallback}</code> instance.
     * 
     * @param callback
     *            Callback instance
     * @return this
     */
    public ModalWindow setWindowClosedCallback(final WindowClosedCallback callback) {
        windowClosedCallback = callback;
        return this;
    }

    /**
     * Shows the modal window.
     * 
     * @param target
     *            Request target associated with current ajax request.
     */
    public void show(final IPartialPageRequestHandler target) {
        if (shown == false) {
            getContent().setVisible(true);
            target.add(this);
            target.appendJavaScript(getWindowOpenJavaScript());
            shown = true;
        }
    }

    /**
     * Hides the modal window. This can be called from within the modal window, however, the modal
     * window must have configured WindowClosedCallback. Otherwise use the
     * {@link #close(IPartialPageRequestHandler)} method.
     * 
     * @param target
     *            Request target associated with current ajax request.
     */
    public static void closeCurrent(final IPartialPageRequestHandler target) {
        target.appendJavaScript(getCloseJavacriptInternal());
    }

    /**
     * Closes the modal window.
     * 
     * @param target
     *            Request target associated with current ajax request.
     */
    public void close(final IPartialPageRequestHandler target) {
        getContent().setVisible(false);
        if (isCustomComponent()) {
            target.add(getContent());
        }
        target.appendJavaScript(getCloseJavacript());
        shown = false;
    }

    /**
     * Method that allows alternate script for showing the window.
     * 
     * @return the script that actually shows the window.
     */
    protected CharSequence getShowJavaScript() {
        return "window.setTimeout(function(){\n" + "  Wicket.Window.create(settings).show();\n" + "}, 0);\n";
    }

    private static String getCloseJavacriptInternal() {
        return "var win;\n" //
                + "try {\n" + "   win = window.parent.Wicket.Window;\n" + "} catch (ignore) {\n" + "}\n"
                + "if (typeof(win) == \"undefined\" || typeof(win.current) == \"undefined\") {\n" + "  try {\n"
                + "     win = window.Wicket.Window;\n" + "  } catch (ignore) {\n" + "  }\n" + "}\n"
                + "if (win && win.current) {\n" + " var close = function(w) { w.setTimeout(function() {\n"
                + "      win.current.close();\n" + "   }, 0);  };\n"
                + "   try { close(window.parent); } catch (ignore) { close(window); }\n" + "}";
    }

    /**
     * Method that allows alternate script for closing the window.
     * 
     * @return the script that actually closes the window.
     */
    protected String getCloseJavacript() {
        return getCloseJavacriptInternal();
    }

    /**
     * Returns the id of content component.
     * 
     * <pre>
     * ModalWindow window = new ModalWindow(parent, &quot;window&quot;);
     * new MyPanel(window, window.getContentId());
     * </pre>
     * 
     * @return Id of content component.
     */
    public String getContentId() {
        return CONTENT_ID;
    }

    /**
     * Sets the minimal width of window. This value is only used if the window is resizable. The
     * width is specified in pixels and it is the width of entire window (including frame).
     * 
     * @param minimalWidth
     *            Minimal window width.
     * @return this
     */
    public ModalWindow setMinimalWidth(final int minimalWidth) {
        this.minimalWidth = minimalWidth;
        return this;
    }

    /**
     * Returns the minimal width of window (in pixels).
     * 
     * @return Minimal width of window
     */
    public int getMinimalWidth() {
        return minimalWidth;
    }

    /**
     * Sets the minimal height of window. This value is only used if window is resizable. The height
     * is specified in pixels and it is the height of window content (without frame).
     * 
     * @param minimalHeight
     *            Minimal height
     * @return this
     */
    public ModalWindow setMinimalHeight(final int minimalHeight) {
        this.minimalHeight = minimalHeight;
        return this;
    }

    /**
     * Returns the minimal height of window (in pixels).
     * 
     * @return Minimal height of window
     */
    public int getMinimalHeight() {
        return minimalHeight;
    }

    /**
     * Sets the CSS class name for this window. This class affects the look of window frame.
     * Possible values (if you don't make your style sheet) are <code>{@link #CSS_CLASS_BLUE}</code>
     * and <code>{@link #CSS_CLASS_GRAY}</code>.
     * 
     * @param cssClassName
     * @return this
     * @see #newCssResource()
     */
    public ModalWindow setCssClassName(final String cssClassName) {
        this.cssClassName = cssClassName;
        return this;
    }

    /**
     * Returns the CSS class name for this window.
     * 
     * @return CSS class name
     */
    public String getCssClassName() {
        return cssClassName;
    }

    /**
     * Sets the initial width of the window. The width refers to the width of entire window
     * (including frame). If the window is resizable, the width unit is always "px". If the window
     * is not resizable, the unit can be specified using {@link #setWidthUnit(String)}. If cookie
     * name is set and window is resizable, the initial width may be ignored in favor of width
     * stored in cookie.
     * 
     * @param initialWidth
     *            Initial width of the window
     * @return this
     */
    public ModalWindow setInitialWidth(final int initialWidth) {
        this.initialWidth = initialWidth;
        return this;
    }

    /**
     * Returns the initial width of the window.
     * 
     * @return Initial height of the window
     */
    public int getInitialWidth() {
        return initialWidth;
    }

    /**
     * Sets the initial height of the window. The height refers to the height of window content
     * (without frame). If the window is resizable, the height unit is always "px". If the window is
     * not resizable, the unit can be specified using {@link #setHeightUnit(String)}. If cookie name
     * is set and window is resizable, the initial height may be ignored in favor of height stored
     * in cookie.
     * 
     * @param initialHeight
     *            Initial height of the window
     * @return this
     */
    public ModalWindow setInitialHeight(final int initialHeight) {
        this.initialHeight = initialHeight;
        return this;
    }

    /**
     * Returns the initial height of the window.
     * 
     * @return Initial height of the window
     */
    public int getInitialHeight() {
        return initialHeight;
    }

    /**
     * Sets whether to use initial height or preserve the real content height. This can only be used
     * if the content is a component (not a page) and the window is not resizable.
     * 
     * @param useInitialHeight
     *            Whether to use initial height instead of preserving content height instead of
     *            using initial height
     * @return this
     */
    public ModalWindow setUseInitialHeight(final boolean useInitialHeight) {
        this.useInitialHeight = useInitialHeight;
        return this;
    }

    /**
     * Returns true if the initial height should be used (in favor of preserving real content
     * height).
     * 
     * @return True if initial height should be used, false is real content height should be
     *         preserved (valid only if the window is not resizable and the content is a component
     *         (not a page)
     */
    public boolean isUseInitialHeight() {
        return useInitialHeight;
    }

    /**
     * Sets whether the user will be able to resize the window.
     * 
     * @param resizable
     *            Whether the window is resizable
     * @return this
     */
    public ModalWindow setResizable(final boolean resizable) {
        this.resizable = resizable;
        return this;
    }

    /**
     * Returns whether the window is resizable.
     * 
     * @return True if the window is resizable, false otherwise
     */
    public boolean isResizable() {
        return resizable;
    }

    /**
     * Sets a flag whether to ask the user before leaving the page.
     * 
     * @param unloadConfirmation
     *            a flag whether to ask the user before leaving the page
     * @return {@code this} instance, for chaining
     */
    public ModalWindow showUnloadConfirmation(final boolean unloadConfirmation) {
        this.unloadConfirmation = unloadConfirmation;
        return this;
    }

    /**
     * Returns whether the user should be asked before leaving the page.
     * 
     * @return {@code true} if the user should be asked if the last action causes leaving the page,
     *         {@code false} otherwise
     */
    public boolean showUnloadConfirmation() {
        return unloadConfirmation;
    }

    /**
     * Sets the CSS unit used for initial window width. This is only applicable when the window is
     * not resizable.
     * 
     * @param widthUnit
     *            CSS unit for initial window width.
     * @return this
     */
    public ModalWindow setWidthUnit(final String widthUnit) {
        this.widthUnit = widthUnit;
        return this;
    }

    /**
     * Returns the CSS unit for initial window width.
     * 
     * @return CSS unit for initial window width.
     */
    public String getWidthUnit() {
        return widthUnit;
    }

    /**
     * Sets the CSS unit used for initial window height. This is only applicable when the window is
     * not resizable.
     * 
     * @param heightUnit
     *            CSS unit for initial window height.
     * @return this
     */
    public ModalWindow setHeightUnit(final String heightUnit) {
        this.heightUnit = heightUnit;
        return this;
    }

    /**
     * Retrns the CSS unit for initial window height.
     * 
     * @return CSS unit for initial window height.
     */
    public String getHeightUnit() {
        return heightUnit;
    }

    /**
     * Sets the name of the cookie that is used to remember window position (and size if the window
     * is resizable).
     * 
     * @param cookieName
     *            Name of the cookie
     * @return this
     */
    public ModalWindow setCookieName(final String cookieName) {
        if ((cookieName != null) && (cookieName.contains(",") || cookieName.contains("|"))) {
            throw new IllegalArgumentException("Cookie name may not contain ',' or '|' characters.");
        }
        this.cookieName = cookieName;
        return this;
    }

    /**
     * Returns the name of cookie that is used to remember window position (and size if the window
     * is resizable).
     * 
     * @return Name of the cookie
     */
    public String getCookieName() {
        return cookieName;
    }

    /**
     * Sets the title of window. If the window is a page, title can be <code>null</code>. In that
     * case it will display the title document inside the window.
     * 
     * @param title
     *            Title of the window
     * @return this
     */
    public ModalWindow setTitle(final String title) {
        this.title = new Model<>(title);
        return this;
    }

    /**
     * Sets the title of window. If the window is a page, title can be <code>null</code>. In that
     * case it will display the title document inside the window.
     * 
     * @param title
     *            Title of the window
     * @return this
     */
    public ModalWindow setTitle(IModel<String> title) {
        title = wrap(title);
        this.title = title;
        return this;
    }

    /**
     * Returns the title of the window.
     * 
     * @return Title of the window
     */
    public IModel<String> getTitle() {
        return title;
    }

    /**
     * Mask is the element behind the window, that prevents user from interacting the rest of page.
     * Mask can be either
     * <ul>
     * <li><code>{@link #TRANSPARENT}</code> - the mask is invisible
     * <li><code>{@link #SEMI_TRANSPARENT}</code> - the mask is black with small opacity (10%)
     * </ul>
     * 
     * @author Matej Knopp
     */
    public static final class MaskType extends EnumeratedType {
        private static final long serialVersionUID = 1L;

        /** Transparent mask (not visible). */
        public static final MaskType TRANSPARENT = new MaskType("TRANSPARENT");

        /** Visible mask (black with low opacity). */
        public static final MaskType SEMI_TRANSPARENT = new MaskType("SEMI_TRANSPARENT");

        /**
         * Constructor.
         * 
         * @param name
         */
        public MaskType(final String name) {
            super(name);
        }
    }

    /**
     * Sets the mask type of the window.
     * 
     * @param mask
     *            The mask type
     * @return this
     */
    public ModalWindow setMaskType(final MaskType mask) {
        maskType = mask;
        return this;
    }

    /**
     * Returns the mask type of the window
     * 
     * @return The mask type
     */
    public MaskType getMaskType() {
        return maskType;
    }

    /**
     * Creates the page.
     * 
     * @return Page instance or null if page couldn't be created.
     */
    private Page createPage() {
        if (pageCreator == null) {
            return null;
        } else {
            return pageCreator.createPage();
        }
    }

    /**
     * @see org.apache.wicket.Component#onBeforeRender()
     */
    @Override
    protected void onBeforeRender() {
        shown = makeContentVisible();

        getContent().setOutputMarkupId(true);
        getContent().setVisible(shown);

        super.onBeforeRender();
    }

    /**
     * You may subclass this method in case you don't want to show up the window on normal page
     * refresh.
     * 
     * @return true, if the window shall be shown
     */
    protected boolean makeContentVisible() {
        // if user is refreshing whole page, the window will not be shown
        if (getWebRequest().isAjax() == false) {
            return false;
        } else {
            return shown;
        }
    }

    /**
     * @see org.apache.wicket.markup.html.panel.Panel#onComponentTag(org.apache.wicket.markup.ComponentTag)
     */
    @Override
    protected void onComponentTag(final ComponentTag tag) {
        super.onComponentTag(tag);
        tag.put("style", "display:none");
    }

    /**
     * Returns a content component. In case user haven't specified any content component, it returns
     * an empty WebMarkupContainer.
     * 
     * @return Content component
     */
    protected final Component getContent() {
        return get(getContentId());
    }

    /**
     * Returns true if user has added own component to the window.
     * 
     * @return True if user has added own component to the window, false otherwise.
     */
    protected boolean isCustomComponent() {
        return getContent() != empty;
    }

    /**
     * @see org.apache.wicket.MarkupContainer#remove(org.apache.wicket.Component)
     */
    @Override
    public ModalWindow remove(final Component component) {
        super.remove(component);
        if (component.getId().equals(getContentId())) {
            add(empty = new WebMarkupContainer(getContentId()));
        }

        return this;
    }

    /**
     * Sets the content of the modal window.
     * 
     * @param component
     * @return this;
     */
    public ModalWindow setContent(final Component component) {
        if (component.getId().equals(getContentId()) == false) {
            throw new WicketRuntimeException("Modal window content id is wrong. Component ID:" + component.getId()
                    + "; content ID: " + getContentId());
        } else if (component instanceof AbstractRepeater) {
            throw new WicketRuntimeException(
                    "A repeater component cannot be used as the content of a modal window, please use repeater's parent");
        }

        component.setOutputMarkupPlaceholderTag(true);
        component.setVisible(false);
        replace(component);
        shown = false;
        pageCreator = null;
        return this;
    }

    /**
     * @author Matej Knopp
     */
    private class WindowClosedBehavior extends AbstractDefaultAjaxBehavior {
        private static final long serialVersionUID = 1L;

        @Override
        protected void respond(final AjaxRequestTarget target) {
            shown = false;

            if (windowClosedCallback != null) {
                windowClosedCallback.onClose(target);
            }
        }
    }

    /**
     * @author Matej Knopp
     */
    protected class CloseButtonBehavior extends AbstractDefaultAjaxBehavior {
        private static final long serialVersionUID = 1L;

        public CloseButtonBehavior() {
        }

        @Override
        protected final void respond(final AjaxRequestTarget target) {
            if ((closeButtonCallback == null) || (closeButtonCallback.onCloseButtonClicked(target))) {
                close(target);
            }
        }
    }

    /**
     * Returns the markup id of the component.
     * 
     * @return component id
     */
    private String getContentMarkupId() {
        return getContent().getMarkupId();
    }

    /**
     * Returns the javascript used to open the window. Subclass
     * {@link #postProcessSettings(AppendingStringBuffer)} to modify the JavaScript if needed.
     * 
     * See WICKET-12
     * 
     * @return javascript that opens the window
     */
    protected final String getWindowOpenJavaScript() {
        JSONObject settings = new JSONObject();

        settings.put("minWidth", getMinimalWidth());
        settings.put("minHeight", getMinimalHeight());
        settings.put("className", getCssClassName());
        settings.put("width", getInitialWidth());

        if ((isUseInitialHeight() == true) || (isCustomComponent() == false)) {
            settings.put("height", getInitialHeight());
        } else {
            // WICKET-6613 null would remove the key, so use false instead 
            settings.put("height", false);
        }

        settings.put("resizable", isResizable());

        if (isResizable() == false) {
            settings.put("widthUnit", getWidthUnit());
            settings.put("heightUnit", getHeightUnit());
        }

        if (isCustomComponent() == false) {
            Page page = createPage();
            if (page == null) {
                throw new WicketRuntimeException("Error creating page for modal dialog.");
            }
            CharSequence pageUrl;
            RequestCycle requestCycle = RequestCycle.get();

            page.getSession().getPageManager().touchPage(page);
            if (page.isPageStateless()) {
                pageUrl = requestCycle.urlFor(page.getClass(), page.getPageParameters());
            } else {
                IRequestHandler handler = new RenderPageRequestHandler(new PageProvider(page));
                pageUrl = requestCycle.urlFor(handler);
            }

            settings.put("src", pageUrl);
        } else {
            settings.put("element", new JSONFunction("document.getElementById(\"" + getContentMarkupId() + "\")"));
        }

        if (getCookieName() != null) {
            settings.put("cookieId", getCookieName());
        }

        String title = getTitle() != null ? getTitle().getObject() : null;
        if (title != null) {
            settings.put("title", getDefaultModelObjectAsString(title));
        }

        if (getMaskType() == MaskType.TRANSPARENT) {
            settings.put("mask", "transparent");
        } else if (getMaskType() == MaskType.SEMI_TRANSPARENT) {
            settings.put("mask", "semi-transparent");
        }

        settings.put("autoSize", autoSize);

        settings.put("unloadConfirmation", showUnloadConfirmation());

        // set true if we set a windowclosedcallback
        boolean haveCloseCallback = false;

        // in case user is interested in window close callback or we have a pagemap to clean attach
        // notification request
        if (windowClosedCallback != null) {
            WindowClosedBehavior behavior = getBehaviors(WindowClosedBehavior.class).get(0);
            settings.put("onClose", new JSONFunction("function() { " + behavior.getCallbackScript() + " }"));

            haveCloseCallback = true;
        }

        // in case we didn't set windowclosecallback, we need at least callback on close button, to
        // close window property (thus cleaning the shown flag)
        if ((closeButtonCallback != null) || (haveCloseCallback == false)) {
            CloseButtonBehavior behavior = getBehaviors(CloseButtonBehavior.class).get(0);
            settings.put("onCloseButton",
                    new JSONFunction("function() { " + behavior.getCallbackScript() + "; return false; }"));
        }

        postProcessSettings(settings);

        AppendingStringBuffer buffer = new AppendingStringBuffer(500);
        buffer.append("var settings = ");
        buffer.append(settings.toString());
        buffer.append(";");

        buffer.append(getShowJavaScript());
        return buffer.toString();
    }

    /**
     * Method that allows tweaking the settings
     * 
     * @param settings
     * @return settings json
     */
    protected void postProcessSettings(JSONObject settings) {
    }

    /**
     * Detach the 'title' model
     * 
     * @see org.apache.wicket.Component#onDetach()
     */
    @Override
    protected void onDetach() {
        super.onDetach();

        if (title != null) {
            title.detach();
        }
    }

    /**
     * Sets whether window size will be automatically adjusted on opening to fit content's width and
     * height. <span style="text-decoration: underline">Doesn't work on IE 6.</span>
     * 
     * @param autoSize
     *            Whether window size will be automatically adjusted
     * @return this
     */
    public ModalWindow setAutoSize(final boolean autoSize) {
        this.autoSize = autoSize;
        return this;
    }

    /**
     * Returns whether window will be opened in autosize mode.
     * 
     * @return True if the window will be opened open in autosize mode, false otherwise
     */
    public boolean isAutoSize() {
        return autoSize;
    }

    /**
     * Gives the possibility to provide custom
     * {@link org.apache.wicket.ajax.attributes.IAjaxCallListener}
     * 
     * @return the behavior that should be used for the window close button
     */
    protected CloseButtonBehavior newCloseButtonBehavior() {
        return new CloseButtonBehavior();
    }
}