package er.ajax; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WORequest; import com.webobjects.appserver.WOResponse; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; /** * AjaxDraggable makes HTML elements draggable. Use in conjunction with * {@link AjaxDroppable}. * * When omitContainer is false (default), the contents nested inside of the * AjaxDraggable will be made draggable. Otherwise an existing DOM element with * the id specified via the id binding will be made draggable. * * For the scriptaculous options see * http://wiki.github.com/madrobby/scriptaculous/draggable * * @binding id the id of the element to drag. When omitContainer is false, this * is the id of the container surrounding the component content. When * unspecified, a unique id will be generated. * @binding omitContainer if set to true, the container element will be omitted. * The DOM id of the object to be made draggable must be specified with * the id binding. Defaults to false. * @binding elementName the element to use for the container. defaults to "div". * @binding class the css class of the container * @binding style the css styles of the container * @binding draggableObject a java object which is passed to the AjaxDroppable * when this draggable is dropped onto it. * @binding draggableID * @binding starteffect Effect, defaults to Effect.Opacity. Defines the effect * to use when the draggable starts being dragged * @binding reverteffect Effect, default to Effect.Move. Defines the effect to * use when the draggable reverts back to its starting position * @binding endeffect Effect, defaults to Effect.Opacity. Defines the effect to * use when the draggable stops being dragged * @binding zindex integer value, defaults to 1000. The css z-index of the * draggable item * @binding revert boolean or function reference, defaults to false. If set to * true, the element returns to its original position when the drags * ends. Revert can also be an arbitrary function reference, called * when the drag ends. Specifying 'failure' will instruct the draggable * not to revert if successfully dropped in a droppable. * @binding snap set to false no snapping occurs. Otherwise takes one of the * following forms – Δi: one delta value for both horizontal and * vertical snap, [Δx, Δy]: delta values for horizontal and vertical * snap, function(x, y, draggable_object) { return [x, y]; }: a * function that receives the proposed new top left coordinate pair and * returns the coordinate pair to actually be used. * @binding ghosting boolean, defaults to false. Clones the element and drags * the clone, leaving the original in place until the clone is dropped * @binding handle string or DOM reference, not set by default. Sets whether the * element should only be draggable by an embedded handle. The value * must be an element reference or element id. The value may also be a * string referencing a CSS class value. The first * child/grandchild/etc. element found within the element that has this * CSS class value will be used as the handle. * @binding change Called just as onDrag (which is the preferred callback). Gets * the Draggable instance as its parameter. * @binding keyPress * @binding scroll can be either a dom ID or a dom reference. In case of a dom * reference the value must not be quoted. Set binding to "window" to * scroll the window when the draggable reaches the window boundary. * Set binding to "'someID'" to scroll the element with ID "someID" */ public class AjaxDraggable extends AjaxComponent { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; private static final String COMPONENT_DRAGGABLES_MAP_KEY = "AjaxComponentDraggablesMap"; private String _id; public AjaxDraggable(WOContext context) { super(context); } @Override public void awake() { super.awake(); } @Override public void reset() { _id = null; super.reset(); } @Override public boolean isStateless() { return true; } @SuppressWarnings("unchecked") public static Object draggableObjectForPage(WOComponent page, String draggableID) { Object droppedObject = null; Map<WOComponent, Map<String, Object>> componentDraggablesMap = (Map<WOComponent, Map<String, Object>>)page.context().session().objectForKey(AjaxDraggable.COMPONENT_DRAGGABLES_MAP_KEY); if (componentDraggablesMap != null) { Map<String, Object> draggablesMap = componentDraggablesMap.get(page); if (draggablesMap != null) { droppedObject = draggablesMap.get(draggableID); } } return droppedObject; } @Override @SuppressWarnings("unchecked") public void appendToResponse(WOResponse res, WOContext ctx) { if (canGetValueForBinding("draggableObject")) { Object draggableObject = valueForBinding("draggableObject"); WOComponent page = context().page(); Map<WOComponent, Map<String, Object>> componentDraggablesMap = (Map<WOComponent, Map<String, Object>>) ctx.session().objectForKey(AjaxDraggable.COMPONENT_DRAGGABLES_MAP_KEY); if (componentDraggablesMap == null) { componentDraggablesMap = new WeakHashMap<WOComponent, Map<String, Object>>(); ctx.session().setObjectForKey(componentDraggablesMap, AjaxDraggable.COMPONENT_DRAGGABLES_MAP_KEY); } Map<String, Object> draggablesMap = componentDraggablesMap.get(page); if (draggablesMap == null) { draggablesMap = new HashMap<>(); componentDraggablesMap.put(page, draggablesMap); } String id = draggableID(); if (draggableObject == null) { draggablesMap.remove(id); } else { draggablesMap.put(id, draggableObject); } } super.appendToResponse(res, ctx); } public NSDictionary<String, String> createAjaxOptions() { NSMutableArray<AjaxOption> ajaxOptionsArray = new NSMutableArray<>(); // PROTOTYPE OPTIONS ajaxOptionsArray.addObject(new AjaxOption("starteffect", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("reverteffect", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("endeffect", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("zindex", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("revert", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("snap", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("ghosting", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("handle", AjaxOption.DEFAULT)); ajaxOptionsArray.addObject(new AjaxOption("change", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("keyPress", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("scroll", AjaxOption.SCRIPT)); NSMutableDictionary<String, String> options = AjaxOption.createAjaxOptionsDictionary(ajaxOptionsArray, this); return options; } public String id() { if (_id == null) { if (canGetValueForBinding("id") && valueForBinding("id") != null) { _id = (String) valueForBinding("id"); } else { // only try to set the id binding if the id is auto-generated. // canSetValueForBinding will report true for all OGNL expressions _id = safeElementID(); if (canSetValueForBinding("id")) { setValueForBinding(_id, "id"); } } } return _id; } public String elementName() { return (String) valueForBinding("elementName", "div"); } public String draggableID() { String draggableID; if (canGetValueForBinding("draggableID")) { draggableID = (String) valueForBinding("draggableID"); } else { draggableID = id(); } return draggableID; } @Override protected void addRequiredWebResources(WOResponse res) { addScriptResourceInHead(res, "prototype.js"); addScriptResourceInHead(res, "effects.js"); addScriptResourceInHead(res, "dragdrop.js"); addScriptResourceInHead(res, "wonder.js"); } @Override public WOActionResults handleRequest(WORequest request, WOContext context) { return null; } }