org.wicketstuff.jquery.dnd.DnDSortableBehavior.java Source code

Java tutorial

Introduction

Here is the source code for org.wicketstuff.jquery.dnd.DnDSortableBehavior.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.wicketstuff.jquery.dnd;

import java.util.ArrayList;
import java.util.HashMap;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.IBehaviorListener;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.StringHeaderItem;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.util.template.PackageTextTemplate;
import org.wicketstuff.jquery.JQueryBehavior;
import org.wicketstuff.jquery.Options;

// TODO: disable callback to serverside if clientsideonly
@SuppressWarnings("serial")
public class DnDSortableBehavior extends JQueryBehavior implements IBehaviorListener {
    public static final ResourceReference DNDSORTABLEBEHAVIOR_JS = new PackageResourceReference(
            DnDSortableBehavior.class, DnDSortableBehavior.class.getSimpleName() + ".js");

    protected Options options_;

    protected ArrayList<MarkupContainer> containers_;

    public DnDSortableBehavior() {
        this(null);
    }

    /**
     * Create a DnDSortableBehavior with default options override.
     * <ul>
     * <li>options include every optionsof the js component (see <a
     * href="http://interface.eyecon.ro/docs/sort">http://interface.eyecon.ro/docs/sort</a> for the
     * base list of options).</li>
     * <li>"containerclass" : the CSS' class of every container to be sortable (default is bind
     * component (handler) + "_dndContainer"</li>
     * <li>"startOnLoad" : boolean, true => sortable feature is started on page load (default) else,
     * the client side must call the JSFunctionName4Start.</li>
     * <ul>
     * 
     * @param options
     *            the overriden options to use
     */
    public DnDSortableBehavior(Options options) {
        super();
        if (options == null) {
            options = new Options();
        }
        options_ = options;
        options_.set("accept", "dndItem", false).set("helperclass", "sortHelper", false)
                .set("activeclass", "sortableactive", false).set("hoverclass", "sortablehover", false)
                // .set("handle", ".dndItem", false)
                .set("tolerance", "pointer", false).set("startOnLoad", Boolean.TRUE, false);
        containers_ = new ArrayList<MarkupContainer>();
    }

    @Override
    public void renderHead(Component component, IHeaderResponse response) {
        super.renderHead(component, response);
        response.render(JavaScriptHeaderItem.forReference(INTERFACE_JS));
        response.render(JavaScriptHeaderItem.forReference(DNDSORTABLEBEHAVIOR_JS));
        response.render(StringHeaderItem.forString(getHead(false)));
    }

    public CharSequence getRebindScript() {
        return getHead(true);
    }

    private CharSequence getHead(boolean rebind) {
        // load the css template we created form the res package
        PackageTextTemplate template = new PackageTextTemplate(DnDSortableBehavior.class,
                DnDSortableBehavior.class.getSimpleName() + (rebind ? "-rebind.tmpl" : "-head.tmpl"));

        // create a variable subsitution map
        CharSequence itemSelector = "." + options_.get("accept");
        CharSequence handleSelector = (CharSequence) options_.get("handle");
        if (handleSelector == null) {
            // only for CSS
            handleSelector = itemSelector;
        }
        HashMap<String, Object> params = new HashMap<String, Object>();
        params.put("containerSelector", "." + getContainerCSSClass());
        params.put("helperclass", options_.get("helperclass", "").toString());
        params.put("handleSelector", handleSelector);
        params.put("itemSelector", itemSelector);
        params.put("options", options_.toString(true));
        params.put("callbackUrl", getCallbackUrl());
        params.put("dndHandlerStart", getJSFunctionName4Start());
        params.put("dndHandlerStop", getJSFunctionName4Stop());
        // params.put("startOnLoad", options_.get("startOnLoad", "true").toString());
        // perform subsitution and return the result
        return template.asString(params);
    }

    private CharSequence getContainerCSSClass() {
        CharSequence back = (CharSequence) options_.get("containerclass", null);
        if (back == null) {
            back = getComponent().getId() + "_dndContainer";
        }
        return back;
    }

    @Override
    public String getOnReadyScript() {
        return "$( " + getJSFunctionName4Start() + " );";
    }

    /**
     * @return the name of the javascript function to start the behavior on client side.
     */
    public CharSequence getJSFunctionName4Start() {
        return getComponent().getId() + "_dndStart";
    }

    /**
     * @return the name of the javascript function to stop the behavior on client side.
     */
    public CharSequence getJSFunctionName4Stop() {
        return getComponent().getId() + "_dndStop";
    }

    @Override
    public final void respond(AjaxRequestTarget target) {
        try {
            Request req = RequestCycle.get().getRequest();
            if (logger().isDebugEnabled()) {
                logger().debug("params : {}", req.getRequestParameters());
            }
            onDnD(target,
                    // req.getParameter("itemId"),
                    req.getQueryParameters().getParameterValue("srcContainerId").toString(),
                    req.getQueryParameters().getParameterValue("srcPosition").toInt(),
                    req.getQueryParameters().getParameterValue("destContainerId").toString(),
                    req.getQueryParameters().getParameterValue("destPosition").toInt());
        } catch (RuntimeException exc) {
            throw exc;
        } catch (Exception exc) {
            throw new RuntimeException("wrap: " + exc.getMessage(), exc);
        }
    }

    /**
     * Call when a component has been moved on client side. The default implementation log (as
     * debug) the incomming parameters, search the component and forward to onDnD(AjaxRequestTarget
     * target, String itemId, String srcContainerId, int srcPos, String destContainerId, int
     * destPos).
     * 
     * @param target
     *            a target, provide if a response,
     * @param srcContainerId
     *            the html id of source container from where item come, (null if not previously
     *            registered by via registerContainer(...)).
     * @param srcPos
     *            the position/index of item into srcContainer before moving.
     * @param destContainerId
     *            the html id of destination container where item is, (null if not previously
     *            registered by via registerContainer(...)).
     * @param destPos
     *            the position/index of item into srcContainer after moving.
     */
    public void onDnD(AjaxRequestTarget target, String srcContainerId, int srcPos, String destContainerId,
            int destPos) {
        if (logger().isDebugEnabled()) {
            logger().debug("srcContainerId={}, srcPos={}, destContainerId={}, destPos={}",
                    new Object[] { srcContainerId, srcPos, destContainerId, destPos });
        }
        MarkupContainer srcContainer = null;
        MarkupContainer destContainer = null;
        for (MarkupContainer container : containers_) {
            if ((srcContainerId != null) && srcContainerId.equals(container.getMarkupId())) {
                srcContainer = container;
            }
            if ((destContainerId != null) && destContainerId.equals(container.getMarkupId())) {
                destContainer = container;
            }
            if ((srcContainer != null) && (destContainer != null)) {
                break;
            }
        }
        // if (srcContainer != null) {
        // item = findByMarkupId(srcContainer, itemId);
        // }
        // if ((item == null) && (destContainer != null)) {
        // item = findByMarkupId(destContainer, itemId);
        // }
        boolean updateContainers = onDnD(target, srcContainer, srcPos, destContainer, destPos);
        if (updateContainers && (target != null)) {
            // target is null in testcase
            // (optional) if you need to keep in sync component, markupId on serverside and client
            // side
            target.add(srcContainer);
            if (srcContainer != destContainer) {
                target.add(destContainer);
            }
            target.appendJavaScript(getJSFunctionName4Start() + "();");
        }
    }

    /**
     * Call when a component has been moved on client side (to be overwrited). The default
     * implementation log (as debug) the incomming parameters.
     * 
     * @param target
     *            a target, provide if a response,
     * @param srcContainer
     *            the source container from where item come, (null if not previously registered by
     *            via registerContainer(...)).
     * @param srcPos
     *            the position/index of item into srcContainer before moving.
     * @param destContainer
     *            the destination container where item is, (null if not previously registered by via
     *            registerContainer(...)).
     * @param destPos
     *            the position/index of item into srcContainer after moving.
     * @return false if you don't need to keep in sync component, markupId on serverside and client
     *         side, else return true to send to client side the srcContainer and destContainer and
     *         to update the handler (consume more resource, server, network, client).
     */
    public boolean onDnD(AjaxRequestTarget target, MarkupContainer srcContainer, int srcPos,
            MarkupContainer destContainer, int destPos) {
        if (logger().isDebugEnabled()) {
            logger().debug("srcContainer={}, srcPos={}, destContainer={}, destPos={}",
                    new Object[] { srcContainer, srcPos, destContainer, destPos });
        }
        return false;
    }

    /**
     * Register a container as a container for draggable/droppable items. (add the css class and
     * markupId to be find on clientside).
     * 
     * @param v
     *            the container to register.
     * @return this
     */
    protected DnDSortableBehavior registerContainer(MarkupContainer v) {
        v.add(new AttributeAppender("class", new Model<String>(String.valueOf(getContainerCSSClass())), " "));
        v.setOutputMarkupId(true);
        containers_.add(v);
        return this;
    }

    /**
     * Register a component as draggable/moveable item. (add the css class and markupId to be find
     * on clientside).
     * 
     * @param v
     *            the component to register.
     * @return this
     */
    protected DnDSortableBehavior registerItem(Component v) {
        v.add(new AttributeAppender("class", new Model<String>(String.valueOf(options_.get("accept"))), " "));
        v.setOutputMarkupId(true);
        return this;
    }

}