Java tutorial
/* * 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; } }