You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by sy...@apache.org on 2005/09/28 12:25:30 UTC

svn commit: r292158 [1/4] - in /cocoon: blocks/ajax/ blocks/ajax/trunk/ blocks/ajax/trunk/WEB-INF/ blocks/ajax/trunk/WEB-INF/xconf/ blocks/ajax/trunk/java/ blocks/ajax/trunk/java/org/ blocks/ajax/trunk/java/org/apache/ blocks/ajax/trunk/java/org/apache...

Author: sylvain
Date: Wed Sep 28 03:24:51 2005
New Revision: 292158

URL: http://svn.apache.org/viewcvs?rev=292158&view=rev
Log:
New Ajax block, and prototypic CForms suggestion-lists

Added:
    cocoon/blocks/ajax/
    cocoon/blocks/ajax/trunk/
    cocoon/blocks/ajax/trunk/WEB-INF/
    cocoon/blocks/ajax/trunk/WEB-INF/xconf/
    cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf   (with props)
    cocoon/blocks/ajax/trunk/java/
    cocoon/blocks/ajax/trunk/java/org/
    cocoon/blocks/ajax/trunk/java/org/apache/
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/effects.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/prototype.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/scriptaculous.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/slider.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/unittest.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/util.js   (with props)
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/transformation/
    cocoon/blocks/ajax/trunk/java/org/apache/cocoon/transformation/BrowserUpdateTransformer.java   (with props)
    cocoon/blocks/ajax/trunk/pom.xml   (with props)
    cocoon/blocks/ajax/trunk/samples/
    cocoon/blocks/ajax/trunk/samples/ajax.xsamples
    cocoon/blocks/ajax/trunk/samples/display-suggestions.xml   (with props)
    cocoon/blocks/ajax/trunk/samples/file-browser-suggest.xml   (with props)
    cocoon/blocks/ajax/trunk/samples/file-browser.xml   (with props)
    cocoon/blocks/ajax/trunk/samples/flow.js   (with props)
    cocoon/blocks/ajax/trunk/samples/sitemap.xmap   (with props)
    cocoon/blocks/ajax/trunk/samples/welcome.xml   (with props)
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/SelectionListFilter.java   (with props)
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/SuggestionListGenerator.java   (with props)
    cocoon/blocks/forms/trunk/samples/forms/ajax_suggest_form.xml   (with props)
    cocoon/blocks/forms/trunk/samples/forms/ajax_suggest_template.xml   (with props)
    cocoon/blocks/forms/trunk/samples/suggest-list.xsl   (with props)
Removed:
    cocoon/trunk/src/java/org/apache/cocoon/transformation/BrowserUpdateTransformer.java
Modified:
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinitionBuilder.java
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/Field.java
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinition.java
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/FieldDefinitionBuilder.java
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java
    cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml
    cocoon/blocks/forms/trunk/samples/flow/forms_flow_example.js
    cocoon/blocks/forms/trunk/samples/forms/form1_template.xml
    cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml
    cocoon/blocks/forms/trunk/samples/sitemap.xmap
    cocoon/blocks/forms/trunk/samples/welcome.xml
    cocoon/trunk/src/java/org/apache/cocoon/components/flow/WebContinuation.java
    cocoon/trunk/src/java/org/apache/cocoon/components/flow/javascript/fom/FOM_WebContinuation.java

Added: cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf (added)
+++ cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf Wed Sep 28 03:24:51 2005
@@ -0,0 +1,3 @@
+<components>
+  <!-- nothing special here for now -->
+</components>

Propchange: cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/WEB-INF/xconf/cocoon-ajax.xconf
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java Wed Sep 28 03:24:51 2005
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.cocoon.ajax;
+
+import org.apache.cocoon.environment.Request;
+
+/**
+ * Helper class to handle Ajax requests
+ * 
+ * @version $Id$
+ * @since 2.1.8
+ */
+public class AjaxHelper {
+    
+    private AjaxHelper() {
+        // Forbid instanciation
+    }
+
+    /**
+     * The request parameter that, if set, indicates an Ajax request.
+     */
+    public static final String AJAX_REQUEST_PARAMETER = "cocoon-ajax";
+    
+    /**
+     * The namespace URI of the "browser update" xml dialect
+     */
+    public static final String BROWSER_UPDATE_URI = "http://apache.org/cocoon/browser-update/1.0";
+
+    /**
+     * Is the request an Ajax request?
+     * 
+     * @param req the request
+     * @return true if this is an Ajax request
+     */
+    public static boolean isAjaxRequest(Request req) {
+        return req.getParameter(AJAX_REQUEST_PARAMETER) != null;
+    }
+
+}

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxHelper.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java Wed Sep 28 03:24:51 2005
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.cocoon.ajax;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.matching.Matcher;
+import org.apache.cocoon.sitemap.PatternException;
+
+/**
+ * A matcher that tests if the current request is an Ajax request. This matcher
+ * provides no sitemap variables.
+ * <p>
+ * Example:
+ * <pre>
+ *   &lt;map:match type="ajax-request"&gt;
+ *     ... ajax request ...
+ *   &lt;/map:match&gt;
+ * </pre>
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class AjaxRequestMatcher implements Matcher, ThreadSafe {
+
+    public Map match(String pattern, Map objectModel, Parameters parameters) throws PatternException {
+        Request req = ObjectModelHelper.getRequest(objectModel);
+        
+        return AjaxHelper.isAjaxRequest(req) ? Collections.EMPTY_MAP : null;
+    }
+}

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestMatcher.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java Wed Sep 28 03:24:51 2005
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.cocoon.ajax;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.selection.AbstractSwitchSelector;
+import org.apache.commons.lang.BooleanUtils;
+
+/**
+ * Choose a select branch depending on if the current request is an Ajax request.
+ * The test value can be either "<code>true</code>" or "<code>false</code>".
+ * 
+ * <pre>
+ *   &lt;map:select type="ajax-request"&gt;
+ *     &lt;map:when test="true"&gt;
+ *       ... ajax request ...
+ *     &lt;/map:when&gt;
+ *     &lt;map:otherwise&gt;
+ *       ... non ajax request ...
+ *     &lt;/map:otherwise&gt;
+ *   &lt;/map:select&gt;
+ * </pre>
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class AjaxRequestSelector extends AbstractSwitchSelector implements ThreadSafe {
+    
+    public Object getSelectorContext(Map objectModel, Parameters parameters) {
+        Request req = ObjectModelHelper.getRequest(objectModel);
+        return BooleanUtils.toBooleanObject(AjaxHelper.isAjaxRequest(req));
+    }
+
+    public boolean select(String expression, Object selectorContext) {
+        boolean test = BooleanUtils.toBoolean(expression);
+
+        return test == ((Boolean)selectorContext).booleanValue();
+    }
+}

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/AjaxRequestSelector.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java Wed Sep 28 03:24:51 2005
@@ -0,0 +1,180 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.cocoon.ajax;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.transformation.AbstractTransformer;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.RedundantNamespacesFilter;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * 
+ * @version $Id$
+ */
+
+public class BrowserUpdateTransformer extends AbstractTransformer {
+    
+    public static final String AJAXMODE_PARAM = "cocoon-ajax";
+    
+    public static final String BU_NSURI = "http://apache.org/cocoon/browser-update/1.0";
+    
+    private boolean ajaxRequest = false;
+    
+    private int replaceDepth = 0;
+    
+    private boolean inUpdateTag = false;
+    private String updateTagId = null;
+    
+    Locator locator;
+
+    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException {
+
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        this.ajaxRequest = request.getParameter(AJAXMODE_PARAM) != null;
+    }
+    
+    public void setDocumentLocator(Locator locator) {
+        super.setDocumentLocator(locator);
+        this.locator = locator;
+    }
+
+    public void startDocument() throws SAXException {
+        
+        if (ajaxRequest) {
+            // Add the namespace filter to our own output.
+            // This is needed as flattening bu:* elements can lead to some weird reordering of
+            // namespace declarations...
+            RedundantNamespacesFilter nsPipe = new RedundantNamespacesFilter();
+            if (this.xmlConsumer != null) {
+                nsPipe.setConsumer(this.xmlConsumer);
+            } else {
+                nsPipe.setContentHandler(this.contentHandler);
+            }
+            setConsumer(nsPipe);
+        }
+        
+        super.startDocument();
+        if (ajaxRequest) {
+            // Add a root element. The original one is very likely to be stripped
+            super.startPrefixMapping("bu", BU_NSURI);
+            super.startElement(BU_NSURI, "document", "bu:document", new AttributesImpl());
+        }
+    }
+    
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        // Passthrough if not in ajax mode or if in a bu:replace
+        if (!this.ajaxRequest || this.replaceDepth > 0) {
+            super.startPrefixMapping(prefix, uri);
+        }
+    }
+
+    public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
+        if (BU_NSURI.equals(uri) && "replace".equals(loc)) {
+            // Keep the id attribute. It may be null, in which case the one of the
+            // child element will be used.
+            this.updateTagId = attrs.getValue("id");
+            this.inUpdateTag = true;
+            if (this.ajaxRequest && this.replaceDepth == 0) {
+                // Pass this element through
+                super.startElement(uri, loc, raw, attrs);
+            }
+            replaceDepth++;
+        } else {
+            // Passthrough if not in ajax mode or if in a bu:replace
+            
+            // Is the enclosing element a bu:replace?
+            if (this.inUpdateTag) {
+                this.inUpdateTag = false;
+                // Is there already an id?
+                String localId = attrs.getValue("id");
+                if (localId != null) {
+                    // Yes: check it's the same
+                    if (this.updateTagId != null && !localId.equals(this.updateTagId)) {
+                        throw new SAXParseException("Id on bu:replace (" + this.updateTagId + ") and " + raw + " (" +
+                                localId + ") don't match.", this.locator);
+                    }
+                } else {
+                    // No: add it
+                    if (this.updateTagId == null) {
+                        throw new SAXParseException("Neither bu:replace nor " + raw + " have an id attribute.", this.locator);
+                    }
+                    AttributesImpl newAttrs = new AttributesImpl(attrs);
+                    newAttrs.addCDATAAttribute("id", this.updateTagId);
+                    attrs = newAttrs;
+                }
+                this.updateTagId = null;
+            }
+            if (!this.ajaxRequest || this.replaceDepth > 0) {
+                super.startElement(uri, loc, raw, attrs);
+            }
+        }
+    }
+
+    public void characters(char[] buffer, int offset, int len) throws SAXException {
+        if (this.inUpdateTag) {
+            // Check that it's only spaces
+            for (int i = offset; i < len; i++) {
+                if (!Character.isWhitespace(buffer[i])) {
+                    throw new SAXParseException("bu:replace must include a single child element and no text.", this.locator);
+                }
+            }
+        }
+        if (!this.ajaxRequest || this.replaceDepth > 0) {
+            super.characters(buffer, offset, len);
+        }
+    }
+    
+    public void endElement(String uri, String loc, String raw) throws SAXException {
+        if (BU_NSURI.equals(uri) && "replace".equals(loc)) {
+            replaceDepth--;
+            if (this.ajaxRequest && this.replaceDepth == 0) {
+                // Pass this element through
+                super.endElement(uri, loc, raw);
+            }
+        } else {
+            // Passthrough if not in ajax mode or if in a bu:replace
+            if (!this.ajaxRequest || this.replaceDepth > 0) {
+                super.endElement(uri, loc, raw);
+            }
+        }
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+        // Passthrough if not in ajax mode or if in a bu:replace
+        if (!this.ajaxRequest || this.replaceDepth > 0) {
+            super.endPrefixMapping(prefix);
+        }
+    }
+
+    public void endDocument() throws SAXException {
+        if (ajaxRequest) {
+            super.endElement(BU_NSURI, "document", "bu:document");
+            super.endPrefixMapping("bu");
+        }
+        super.endDocument();
+    }
+}

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/BrowserUpdateTransformer.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java Wed Sep 28 03:24:51 2005
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.cocoon.ajax;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.acting.AbstractAction;
+import org.apache.cocoon.components.flow.ContinuationsManager;
+import org.apache.cocoon.components.flow.FlowHelper;
+import org.apache.cocoon.components.flow.WebContinuation;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.sitemap.SitemapParameters;
+
+/**
+ * Get a continuation knowing its identifier, and set it as the current continuation
+ * in the object model, in the same manner as <code>cocoon.sendPageAndWait()</code> does.
+ * This action is useful when an Ajax request is made that needs to access data related
+ * to the continuation that originally displayed the current page.
+ * <p>
+ * The continuation id is either the <code>src</code> attribute in <code>&lt;map:act&gt;</code>
+ * or, if not specified, the <code>continuation-id</code> request parameter.
+ * <p>
+ * This action is successful if the continuation exists and is still valid.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class GetContinuationAction extends AbstractAction implements ThreadSafe, Serviceable, Disposable {
+    ServiceManager manager;
+    ContinuationsManager contManager;
+
+    public void service(ServiceManager manager) throws ServiceException {
+        this.manager = manager;
+        this.contManager = (ContinuationsManager)manager.lookup(ContinuationsManager.ROLE);
+    }
+
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception {
+        String continuationId;
+        if (source == null) {
+            continuationId = ObjectModelHelper.getRequest(objectModel).getParameter("continuation-id");
+        } else {
+            continuationId = source;
+        }
+        
+        // The interpreter id is the sitemap's URI
+        String interpreterId = SitemapParameters.getLocation(parameters).getURI();
+        WebContinuation wk = this.contManager.lookupWebContinuation(continuationId, interpreterId);
+        if (wk == null || wk.disposed()) {
+            return null;
+        } else {
+            FlowHelper.setWebContinuation(objectModel, wk);
+            return Collections.EMPTY_MAP;
+        }
+    }
+
+    public void dispose() {
+        this.manager.release(this.contManager);
+    }
+}

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/GetContinuationAction.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt Wed Sep 28 03:24:51 2005
@@ -0,0 +1,16 @@
+Copyright (c) 2005 Sam Stephenson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-prototype.txt
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt Wed Sep 28 03:24:51 2005
@@ -0,0 +1,20 @@
+Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/LICENSE-scriptaculous.txt
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js Wed Sep 28 03:24:51 2005
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+var Cocoon = {
+  // Dynamic JS loading code copied from scriptaculous.js
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+  },
+  
+  load: function() {
+    var scriptTags = document.getElementsByTagName("script");
+    for(var i=0;i<scriptTags.length;i++) {
+      if(scriptTags[i].src && scriptTags[i].src.match(/cocoon-ajax\.js$/)) {
+        var path = scriptTags[i].src.replace(/cocoon-ajax\.js$/,'');
+        this.require(path + 'prototype.js');
+        this.require(path + 'util.js');
+        this.require(path + 'effects.js');
+        this.require(path + 'dragdrop.js');
+        this.require(path + 'controls.js');
+        this.require(path + 'slider.js');
+        return;
+      }
+    }
+    alert("Could not load Cocoon Ajax libraries");
+  }
+}
+
+Cocoon.load();
\ No newline at end of file

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/cocoon-ajax.js
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js Wed Sep 28 03:24:51 2005
@@ -0,0 +1,703 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+  baseInitialize: function(element, update, options) {
+    this.element     = $(element); 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+
+    if (this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || {};
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+    function(element, update){ 
+      if(!update.style.position || update.style.position=='absolute') {
+        update.style.position = 'absolute';
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+      }
+      new Effect.Appear(update,{duration:0.15});
+    };
+    this.options.onHide = this.options.onHide || 
+    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if (typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) {
+      Position.clone(this.update, this.iefix);
+      this.iefix.style.zIndex = 1;
+      this.update.style.zIndex = 2;
+      Element.show(this.iefix);
+    }
+  },
+
+  hide: function() {
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+      }
+     else 
+      if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) 
+        return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+        
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else this.hide();
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCcount-1;
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+
+    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    var lastTokenPos = this.findLastToken();
+    if (lastTokenPos != -1) {
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value;
+    } else {
+      this.element.value = value;
+    }
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.firstChild);
+
+      if(this.update.firstChild && this.update.firstChild.childNodes) {
+        this.entryCount = 
+          this.update.firstChild.childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+
+      this.index = 0;
+      this.render();
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    if(this.getToken().length>=this.options.minChars) {
+      this.startIndicator();
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  getToken: function() {
+    var tokenPos = this.findLastToken();
+    if (tokenPos != -1)
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+    else
+      var ret = this.element.value;
+
+    return /\n/.test(ret) ? '' : ret;
+  },
+
+  findLastToken: function() {
+    var lastTokenPos = -1;
+
+    for (var i=0; i<this.options.tokens.length; i++) {
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+      if (thisTokenPos > lastTokenPos)
+        lastTokenPos = thisTokenPos;
+    }
+    return lastTokenPos;
+  }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+  initialize: function(element, update, url, options) {
+	  this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || {});
+  }
+});
+
+// AJAX in-place editor
+//
+// The constructor takes three parameters. The first is the element
+// that should support in-place editing. The second is the url to submit
+// the changed value to. The server should respond with the updated
+// value (the server might have post-processed it or validation might
+// have prevented it from changing). The third is a hash of options.
+//
+// Supported options are (all are optional and have sensible defaults):
+// - okText - The text of the submit button that submits the changed value
+//            to the server (default: "ok")
+// - cancelText - The text of the link that cancels editing (default: "cancel")
+// - savingText - The text being displayed as the AJAX engine communicates
+//                with the server (default: "Saving...")
+// - formId - The id given to the <form> element
+//            (default: the id of the element to edit plus '-inplaceeditor')
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $(element);
+
+    this.options = Object.extend({
+      okText: "ok",
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onComplete: function(transport, element) {
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+      },
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      formClassName: 'inplaceeditor-form',
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+      highlightendcolor: "#FFFFFF",
+      externalControl:	null,
+      ajaxOptions: {}
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $(this.options.externalControl);
+    }
+    
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
+    if (!this.originalBackground) {
+      this.originalBackground = "transparent";
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function() {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.form = this.getForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.focus(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+  },
+  getForm: function() {
+    form = document.createElement("form");
+    form.id = this.options.formId;
+    Element.addClassName(form, this.options.formClassName)
+    form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField(form);
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      form.appendChild(br);
+    }
+
+    okButton = document.createElement("input");
+    okButton.type = "submit";
+    okButton.value = this.options.okText;
+    form.appendChild(okButton);
+
+    cancelLink = document.createElement("a");
+    cancelLink.href = "#";
+    cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+    cancelLink.onclick = this.onclickCancel.bind(this);
+    form.appendChild(cancelLink);
+    return form;
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function(form) {
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(this.getText())) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = this.getText();
+      textField.style.backgroundColor = this.options.highlightcolor;
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0)
+        textField.size = size;
+      form.appendChild(textField);
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(this.getText());
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      form.appendChild(textArea);
+      this.editField = textArea;
+    }
+  },
+  getText: function() {
+    if (this.options.loadTextURL) {
+      this.loadExternalText();
+      return this.options.loadingText;
+    } else {
+      return this.element.innerHTML;
+    }
+  },
+  loadExternalText: function() {
+    new Ajax.Request(
+      this.options.loadTextURL,
+      {
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    this.form.value.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+    this.saving = true;
+    new Ajax.Updater(
+      { 
+        success: this.element,
+         // don't update on failure (this could be an option)
+        failure: null
+      },
+      this.url,
+      Object.extend({
+        parameters: this.options.callback(this.form, this.editField.value),
+        onComplete: this.onComplete.bind(this),
+        onFailure: this.onFailure.bind(this)
+      }, this.options.ajaxOptions)
+    );
+    this.onLoading();
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    this.element.style.backgroundColor = this.options.highlightcolor;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    if (this.options.backgroundColor) {
+      this.element.style.backgroundColor = this.oldBackground;
+    }
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+    this.effect = new Effect.Highlight(this.element, {
+      startcolor: this.options.highlightcolor,
+      endcolor: this.options.highlightendcolor,
+      restorecolor: this.originalBackground
+    });
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
\ No newline at end of file

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/controls.js
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js?rev=292158&view=auto
==============================================================================
--- cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js (added)
+++ cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js Wed Sep 28 03:24:51 2005
@@ -0,0 +1,504 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Element.Class part Copyright (c) 2005 by Rick Olson
+// 
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(e) { return e==element });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null  
+    }, arguments[1] || {});
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if((typeof containment == 'object') && 
+        (containment.constructor == Array)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+
+  isContained: function(element, drop) {
+    var parentNode = element.parentNode;
+    return drop._containers.detect(function(c) { return parentNode == c });
+  },
+
+  isAffected: function(pX, pY, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.Class.has_any(element, drop.accept))) &&
+      Position.within(drop.element, pX, pY) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.Class.remove(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(this.last_active) this.deactivate(this.last_active);
+    if(drop.hoverclass)
+      Element.Class.add(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(event, element) {
+    if(!this.drops.length) return;
+    var pX = Event.pointerX(event);
+    var pY = Event.pointerY(event);
+    Position.prepare();
+
+    var i = this.drops.length-1; do {
+      var drop = this.drops[i];
+      if(this.isAffected(pX, pY, element, drop)) {
+        if(drop.onHover)
+           drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+        if(drop.greedy) { 
+          this.activate(drop);
+          return;
+        }
+      }
+    } while (i--);
+    
+    if(this.last_active) this.deactivate(this.last_active);
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
+      if (this.last_active.onDrop) 
+        this.last_active.onDrop(element, this.last_active.element);
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  observers: [],
+  addObserver: function(observer) {
+    this.observers.push(observer);    
+  },
+  removeObserver: function(element) {  // element instead of obsever fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+  },
+  notify: function(eventName, draggable) {  // 'onStart', 'onEnd'
+    this.observers.invoke(eventName, draggable);
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+  initialize: function(element) {
+    var options = Object.extend({
+      handle: false,
+      starteffect: function(element) { 
+        new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); 
+      },
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
+      },
+      endeffect: function(element) { 
+         new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); 
+      },
+      zindex: 1000,
+      revert: false
+    }, arguments[1] || {});
+
+    this.element      = $(element);
+    this.handle       = options.handle ? $(options.handle) : this.element;
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.offsetX      = 0;
+    this.offsetY      = 0;
+    this.originalLeft = this.currentLeft();
+    this.originalTop  = this.currentTop();
+    this.originalX    = this.element.offsetLeft;
+    this.originalY    = this.element.offsetTop;
+    this.originalZ    = parseInt(this.element.style.zIndex || "0");
+
+    this.options      = options;
+
+    this.active       = false;
+    this.dragging     = false;   
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+    this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+  },
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    this.unregisterEvents();
+  },
+  registerEvents: function() {
+    if(this.active) return;
+    Event.observe(document, "mouseup", this.eventMouseUp);
+    Event.observe(document, "mousemove", this.eventMouseMove);
+    Event.observe(document, "keypress", this.eventKeypress);
+  },
+  unregisterEvents: function() {
+    if(!this.active) return;
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    Event.stopObserving(document, "keypress", this.eventKeypress);
+  },
+  currentLeft: function() {
+    return parseInt(this.element.style.left || '0');
+  },
+  currentTop: function() {
+    return parseInt(this.element.style.top || '0')
+  },
+  startDrag: function(event) {
+    if(Event.isLeftClick(event)) {
+      
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if(src.tagName && (
+        src.tagName=='INPUT' ||
+        src.tagName=='SELECT' ||
+        src.tagName=='BUTTON' ||
+        src.tagName=='TEXTAREA')) return;
+      
+      this.registerEvents();
+      this.active = true;
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var offsets = Position.cumulativeOffset(this.element);
+      this.offsetX =  (pointer[0] - offsets[0]);
+      this.offsetY =  (pointer[1] - offsets[1]);
+      Event.stop(event);
+    }
+  },
+  finishDrag: function(event, success) {
+    this.unregisterEvents();
+
+    this.active = false;
+    this.dragging = false;
+
+    if(this.options.ghosting) {
+      Position.relativize(this.element);
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    if(success) Droppables.fire(event, this.element);
+    Draggables.notify('onEnd', this);
+
+    var revert = this.options.revert;
+    if(revert && typeof revert == 'function') revert = revert(this.element);
+
+    if(revert && this.options.reverteffect) {
+      this.options.reverteffect(this.element, 
+      this.currentTop()-this.originalTop,
+      this.currentLeft()-this.originalLeft);
+    } else {
+      this.originalLeft = this.currentLeft();
+      this.originalTop  = this.currentTop();
+    }
+
+    this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+
+
+    Droppables.reset();
+  },
+  keyPress: function(event) {
+    if(this.active) {
+      if(event.keyCode==Event.KEY_ESC) {
+        this.finishDrag(event, false);
+        Event.stop(event);
+      }
+    }
+  },
+  endDrag: function(event) {
+    if(this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.element);
+    offsets[0] -= this.currentLeft();
+    offsets[1] -= this.currentTop();
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = (pointer[0] - offsets[0] - this.offsetX) + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = (pointer[1] - offsets[1] - this.offsetY) + "px";
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  update: function(event) {
+   if(this.active) {
+      if(!this.dragging) {
+        var style = this.element.style;
+        this.dragging = true;
+        if(style.position=="") style.position = "relative";
+        style.zIndex = this.options.zindex;
+
+        if(this.options.ghosting) {
+          this._clone = this.element.cloneNode(true);
+          Position.absolutize(this.element);
+          this.element.parentNode.insertBefore(this._clone, this.element);
+        }
+
+        Draggables.notify('onStart', this);
+        if(this.options.starteffect) this.options.starteffect(this.element);
+      }
+
+      Droppables.show(event, this.element);
+      this.draw(event);
+      if(this.options.change) this.options.change(this);
+
+      // fix AppleWebKit rendering
+      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 
+
+      Event.stop(event);
+   }
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+}
+
+var Sortable = {
+  sortables: new Array(),
+  options: function(element){
+    element = $(element);
+    return this.sortables.detect(function(s) { return s.element == element });
+  },
+  destroy: function(element){
+    element = $(element);
+    this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+    });
+    this.sortables = this.sortables.reject(function(s) { return s.element == element });
+  },
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,      // fixme: unimplemented
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      hoverclass:  null,
+      ghosting:    false,
+      onChange:    function() {},
+      onUpdate:    function() {}
+    }, arguments[1] || {});
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover,
+      greedy:      !options.dropOnEmpty
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // make it so
+
+    // drop on empty handling
+    if(options.dropOnEmpty) {
+      Droppables.add(element,
+        {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
+      options.droppables.push(element);
+    }
+
+    (this.findElements(element, options) || []).each( function(e) {
+      // handles are per-draggable
+      var handle = options.handle ? 
+        Element.Class.childrenWith(e, options.handle)[0] : e;    
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      options.droppables.push(e);      
+    });
+
+    // keep reference
+    this.sortables.push(options);
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    if(!element.hasChildNodes()) return null;
+    var elements = [];
+    $A(element.childNodes).each( function(e) {
+      if(e.tagName && e.tagName==options.tag.toUpperCase() &&
+        (!options.only || (Element.Class.has(e, options.only))))
+          elements.push(e);
+      if(options.tree) {
+        var grandchildren = this.findElements(e, options);
+        if(grandchildren) elements.push(grandchildren);
+      }
+    });
+
+    return (elements.length>0 ? elements.flatten() : null);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+
+  onEmptyHover: function(element, dropon) {
+    if(element.parentNode!=dropon) {
+      dropon.appendChild(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Element.hide(Sortable._marker);
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = $('dropmarker') || document.createElement('DIV');
+      Element.hide(Sortable._marker);
+      Element.Class.add(Sortable._marker, 'dropmarker');
+      Sortable._marker.style.position = 'absolute';
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.style.top  = offsets[1] + 'px';
+    if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+    Sortable._marker.style.left = offsets[0] + 'px';
+    Element.show(Sortable._marker);
+  },
+
+  serialize: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag:  sortableOptions.tag,
+      only: sortableOptions.only,
+      name: element.id
+    }, arguments[1] || {});
+    return $A(element.childNodes).collect( function(item) { 
+      return (encodeURIComponent(options.name) + "[]=" + 
+              encodeURIComponent(item.id.split("_")[1]));
+    }).join("&");
+  }
+} 
\ No newline at end of file

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/ajax/trunk/java/org/apache/cocoon/ajax/resources/js/dragdrop.js
------------------------------------------------------------------------------
    svn:keywords = Id