package lt.inventi.wicket.component.bootstrap;

import java.util.HashMap;
import java.util.Map;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.request.cycle.RequestCycle;

import de.agilecoders.wicket.util.Json;

import lt.inventi.wicket.resource.ResourceSettings;

public class ModalBehavior extends Behavior {

    private final WebMarkupContainer modalContainer;
    private final ModalBehaviorConfig modalConfig;

    private AjaxEventBehavior hideBehavior, showBehavior;

    public ModalBehavior(WebMarkupContainer modalContainer) {
        this(modalContainer, new ModalBehaviorConfig());
    }

    public ModalBehavior(WebMarkupContainer modalContainer, ModalBehaviorConfig modalConfig) {
        this.modalContainer = modalContainer;
        this.modalConfig = modalConfig;
    }

    @Override
    public void bind(Component component) {
        component.setOutputMarkupId(true);
        modalContainer.setOutputMarkupId(true);

        modalContainer.add(new AttributeAppender("class", " modal hide"));
        component.add(new AttributeModifier("data-toggle", "modal"));
        // markup Id might get modified after #bind (e.g. #onInitialize, #onBeforeRender)
        component.add(new AttributeModifier("data-target", new AbstractReadOnlyModel<String>() {
            @Override
            public String getObject() {
                return "#" + modalContainer.getMarkupId();
            }
        }));

        if (modalConfig.hasVisibilityModel()) {
            hideBehavior = new AjaxEventBehavior("hidden") {
                @Override
                protected void onEvent(AjaxRequestTarget target) {
                    modalConfig.onClose();
                }
            };
            showBehavior = new AjaxEventBehavior("shown") {
                @Override
                protected void onEvent(AjaxRequestTarget target) {
                    modalConfig.onShow();
                }
            };

            modalContainer.add(hideBehavior, showBehavior);
        }
    }

    @Override
    public void onRemove(Component component) {
        AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class);
        if (target != null) {
            // Hide the modal if behavior is removed during an Ajax request
            StringBuilder js = new StringBuilder("var m = $('#%s').data('modal');");
            js.append("if (m && m.isShown) { m.hide(); }");
            target.prependJavaScript(String.format(js.toString(), modalContainer.getMarkupId()));
        }
        if (modalConfig.hasVisibilityModel()) {
            modalContainer.remove(hideBehavior, showBehavior);
        }
    }

    @Override
    public void renderHead(Component component, IHeaderResponse response) {
        super.renderHead(component, response);

        response.render(JavaScriptHeaderItem.forReference(ResourceSettings.get().js().bootstrapJs.bsModal));

        String mId = modalContainer.getMarkupId();
        response.render(OnDomReadyHeaderItem.forScript("$('#" + mId + "').modal(" + getOptionsJson() + ")"));

        if (modalConfig.hasVisibilityModel()) {
            response.render(OnDomReadyHeaderItem.forScript(callbackScript(mId, "hidden", hideBehavior)));
            response.render(OnDomReadyHeaderItem.forScript(callbackScript(mId, "shown", showBehavior)));
        }
    }

    private static String callbackScript(String mId, String event, AjaxEventBehavior behavior) {
        return String.format("$('#%s').on('%s',function(){Wicket.Ajax.ajax({'u':'%s','c':'%s'});})",
            mId, event, behavior.getCallbackUrl(), mId);
    }

    private String getOptionsJson() {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("show", modalConfig.isShown());
        params.put("backdrop", modalConfig.backdrop());
        return Json.stringify(params);
    }

}

@@ -1542,24 +1542,102 @@
 al hide%22));%0A
+        modalContainer.add(new AttributeAppender(%22tabindex%22, %22-1%22));%0A        %0A
         comp

Add tabindex=-1 to modal