Index: lib/OpenLayers/Format/WPSExecute.js
===================================================================
--- lib/OpenLayers/Format/WPSExecute.js	(revision 12158)
+++ lib/OpenLayers/Format/WPSExecute.js	(working copy)
@@ -203,9 +203,13 @@
                         schema: complexData.schema
                     } 
                 });
-                node.appendChild(
-                    this.getXMLDoc().createCDATASection(complexData.value)
-                );
+                if (complexData.node) {
+                    node.appendChild(complexData.node);
+                } else {
+                    node.appendChild(
+                        this.getXMLDoc().createCDATASection(complexData.value)
+                    );
+                }
                 return node;
             },
             "Reference": function(reference) {
Index: examples/wps.js
===================================================================
--- examples/wps.js	(revision 0)
+++ examples/wps.js	(revision 0)
@@ -0,0 +1,329 @@
+OpenLayers.ProxyHost = "proxy.cgi?url=";
+var capabilities, // the capabilities, read by Format.WPSCapabilities::read
+    process; // the process description from Format.WPSDescribeProcess::read
+
+var wpsUrl = "http://deegree3-testing.deegree.org/deegree-wps-demo/services?";
+
+// get some capabilities
+getCapabilities();
+
+// create the UI
+var layer = new OpenLayers.Layer.Vector("Scratchpad", {
+    isBaseLayer: true
+});
+var toolbar = new OpenLayers.Control.EditingToolbar(layer);
+toolbar.addControls([new OpenLayers.Control.ModifyFeature(layer, {
+    title: "Select feature"
+})]);
+var map = new OpenLayers.Map('map', {
+    controls: [
+        toolbar,
+        new OpenLayers.Control.ZoomPanel(),
+        new OpenLayers.Control.PanPanel()
+    ],
+    layers: [layer]
+});
+map.zoomToMaxExtent();
+
+// add behavior to html elements
+document.getElementById("processes").onchange = describeProcess;
+
+// using OpenLayers.Format.WPSCapabilities to read the capabilities
+function getCapabilities() {
+    OpenLayers.Request.GET({
+        url: wpsUrl,
+        params: {
+            "SERVICE": "WPS",
+            "REQUEST": "GetCapabilities"
+        },
+        success: function(response){
+            capabilities = new OpenLayers.Format.WPSCapabilities().read(
+                response.responseText
+            );
+            var dropdown = document.getElementById("processes");
+            var offerings = capabilities.processOfferings, option;
+            // populate the dropdown
+            for (var p in offerings) {
+                option = document.createElement("option");
+                option.innerHTML = offerings[p].identifier;
+                option.value = p;
+                dropdown.appendChild(option);
+            }
+        }
+    });
+}
+
+// using OpenLayers.Format.WPSDescribeProcess to get information about a
+// process
+function describeProcess() {
+    var selection = this.options[this.selectedIndex].value;
+    OpenLayers.Request.GET({
+        url: wpsUrl,
+        params: {
+            "SERVICE": "WPS",
+            "REQUEST": "DescribeProcess",
+            "VERSION": capabilities.version,
+            "IDENTIFIER": selection
+        },
+        success: function(response) {
+            process = new OpenLayers.Format.WPSDescribeProcess().read(
+                response.responseText
+            ).processDescriptions[selection];
+            buildForm();
+        }
+    });
+}
+
+// dynamically create a form from the process description
+function buildForm() {
+    document.getElementById("abstract").innerHTML = process["abstract"];
+    document.getElementById("input").innerHTML = "<h3>Input:</h3>";
+    document.getElementById("output").innerHTML = "";
+
+    var inputs = process.dataInputs, supported = true;
+    var input;
+    for (var i=0,ii=inputs.length; i<ii; ++i) {
+        input = inputs[i];
+        if (input.complexData) {
+            var formats = input.complexData.supported.formats;
+            if (formats["application/wkt"]) {
+                addWKTInput(input);
+            } else if (formats["text/xml; subtype=wfs-collection/1.0"]) {
+                addWFSCollectionInput(input);
+            } else if (formats["text/xml"]) {
+                addGML3Input(input);
+            } else  {
+                supported = false;
+            }
+        } else if (input.literalData) {
+            addLiteralInput(input);
+        } else {
+            supported = false;
+        }
+    }
+    
+    if (supported) {
+        var executeButton = document.createElement("button");
+        executeButton.innerHTML = "Execute";
+        document.getElementById("input").appendChild(executeButton);
+        executeButton.onclick = execute;
+    } else {
+        document.getElementById("input").innerHTML = '<span class="notsupported">' +
+            "Sorry, the WPS builder does not support the selected process." +
+            "</span>";
+    }
+}
+
+// helper function to dynamically create a textarea for geometry (WKT) data
+// input
+function addWKTInput(input, previousSibling) {
+    var name = input.identifier;
+    var container = document.getElementById("input");
+    var label = document.createElement("label");
+    label["for"] = name;
+    label.title = input["abstract"];
+    label.innerHTML = name + " (select feature, then click field):";
+    previousSibling && previousSibling.nextSibling ?
+        container.insertBefore(label, previousSibling.nextSibling) :
+        container.appendChild(label);
+    var field = document.createElement("textarea");
+    field.onclick = function () {
+        if (layer.selectedFeatures.length) {
+            this.innerHTML = new OpenLayers.Format.WKT().write(
+                layer.selectedFeatures[0]
+            );
+        }
+        createCopy(input, this, addWKTInput);
+    };
+    field.onblur = function() {
+        input.data = field.value ? {
+            complexData: {
+                mimeType: "application/wkt",
+                value: this.value
+            }
+        } : undefined;
+    };
+    field.title = input["abstract"];
+    field.id = name;
+    previousSibling && previousSibling.nextSibling ?
+        container.insertBefore(field, previousSibling.nextSibling.nextSibling) :
+        container.appendChild(field);
+}
+
+// helper function to dynamically create a textarea for geometry (GML) data
+// input
+function addGML3Input(input, previousSibling) {
+    var name = input.identifier;
+    var container = document.getElementById("input");
+    var label = document.createElement("label");
+    label["for"] = name;
+    label.title = input["abstract"];
+    label.innerHTML = name + " (select feature, then click field):";
+    previousSibling && previousSibling.nextSibling ?
+        container.insertBefore(label, previousSibling.nextSibling) :
+        container.appendChild(label);
+    var field = document.createElement("textarea");
+    field.onclick = function () {
+        if (layer.selectedFeatures.length) {
+            var format = new OpenLayers.Format.GML.v3({curve: true, surface: true});
+            var geom = OpenLayers.Format.GML.v3.prototype.writers.feature["_geometry"].apply(format, [layer.selectedFeatures[0].geometry]);
+            this.node = geom.firstChild;
+            this.innerHTML = OpenLayers.Format.XML.prototype.write.apply(this, [this.node]);
+        }
+        createCopy(input, this, addGML3Input);
+    };
+    field.onblur = function() {
+        input.data = field.value ? {
+            complexData: {
+                mimeType: "text/xml",
+                node: this.node
+            }
+        } : undefined;
+    };
+    field.title = input["abstract"];
+    field.id = name;
+    previousSibling && previousSibling.nextSibling ?
+        container.insertBefore(field, previousSibling.nextSibling.nextSibling) :
+        container.appendChild(field);
+}
+
+// helper function to dynamically create a WFS collection reference input
+function addWFSCollectionInput(input) {
+    var name = input.identifier;
+    var field = document.createElement("input");
+    field.title = input["abstract"];
+    field.value = name + " (layer on demo server)";
+    addValueHandlers(field, function() {
+        input.reference = field.value ? {
+            mimeType: "text/xml; subtype=wfs-collection/1.0",
+            href: wpsUrl,
+            method: "POST",
+            body: {
+                wfs: {
+                    version: "1.0.0",
+                    outputFormat: "GML2",
+                    featureType: field.value
+                }
+            }
+        } : undefined;
+    });
+    document.getElementById("input").appendChild(field);
+}
+
+// helper function to create a literal input textfield or dropdown
+function addLiteralInput(input, previousSibling) {
+    var name = input.identifier;
+    var container = document.getElementById("input");
+    var anyValue = input.literalData.anyValue;
+    // anyValue means textfield, otherwise we create a dropdown
+    var field = document.createElement(anyValue ? "input" : "select");
+    field.id = name;
+    field.title = input["abstract"];
+    previousSibling && previousSibling.nextSibling ?
+        container.insertBefore(field, previousSibling.nextSibling) :
+        container.appendChild(field);
+    if (anyValue) {
+        var dataType = input.literalData.dataType;
+        field.value = name + (dataType ? " (" + dataType + ")" : "");
+        addValueHandlers(field, function() {
+            input.data = field.value ? {
+                literalData: {
+                    value: field.value
+                }
+            } : undefined;
+        });
+    } else {
+        var option;
+        option = document.createElement("option");
+        option.innerHTML = name;
+        field.appendChild(option);
+        for (var v in input.literalData.allowedValues) {
+            option = document.createElement("option");
+            option.value = v;
+            option.innerHTML = v;
+            field.appendChild(option);
+        }
+        field.onchange = function() {
+            createCopy(input, field, addLiteralInput);
+            input.data = this.selectedIndex ? {
+                literalData: {
+                    value: this.options[this.selectedIndex].value
+                }
+            } : undefined;
+        };
+    }
+}
+
+// if maxOccurs is > 1, this will add a copy of the field
+function createCopy(input, field, fn) {
+    if (input.maxOccurs && input.maxOccurs > 1 && !field.userSelected) {
+        // add another copy of the field - we don't check maxOccurs
+        field.userSelected = true;
+        var newInput = OpenLayers.Util.extend({}, input);
+        // we recognize copies by the occurrence property
+        newInput.occurrence = (input.occurrence || 0) + 1;
+        process.dataInputs.push(newInput);
+        fn(newInput, field);
+    }
+}
+
+// helper function for adding events to form fields
+function addValueHandlers(field, onblur) {
+    field.onclick = function() {
+        if (!this.initialValue) {
+            this.initialValue = this.value;
+            this.value = "";
+        }
+    };
+    field.onblur = function() {
+        if (!this.value) {
+            this.value = this.initialValue;
+            delete this.initialValue;
+        }
+        onblur.apply(this, arguments);
+    };
+}
+
+// execute the process
+function execute() {
+    var output = process.processOutputs[0];
+    var input;
+    // remove occurrences that the user has not filled out
+    for (var i=process.dataInputs.length-1; i>=0; --i) {
+        input = process.dataInputs[i];
+        if (input.occurrence && !input.data && !input.reference) {
+            OpenLayers.Util.removeItem(process.dataInputs, input);
+        }
+    }
+    process.responseForm = {
+        rawDataOutput: {
+            identifier: output.identifier
+        }
+    };
+    if (output.complexOutput && output.complexOutput.supported.formats["application/wkt"]) {
+        process.responseForm.rawDataOutput.mimeType = "application/wkt";
+    }
+    OpenLayers.Request.POST({
+        url: wpsUrl,
+        data: new OpenLayers.Format.WPSExecute().write(process),
+        success: showOutput
+    });
+}
+
+// add the process's output to the page
+function showOutput(response) {
+    var result = document.getElementById("output");
+    result.innerHTML = "<h3>Output:</h3>";
+    var features;
+    var contentType = response.getResponseHeader("Content-Type");
+    if (contentType == "application/wkt") {
+        features = new OpenLayers.Format.WKT().read(response.responseText);
+    } else if (contentType == "text/xml; subtype=wfs-collection/1.0") {
+        features = new OpenLayers.Format.WFST.v1_0_0().read(response.responseText);
+    }
+    if (features && (features instanceof OpenLayers.Feature.Vector || features.length)) {
+        layer.addFeatures(features);
+        result.innerHTML += "The result should also be visible on the map.";
+    }
+    result.innerHTML += "<textarea>" + response.responseText + "</textarea>";
+}
Index: examples/proxy.cgi
===================================================================
--- examples/proxy.cgi	(revision 12158)
+++ examples/proxy.cgi	(working copy)
@@ -21,7 +21,8 @@
                 'sigma.openplans.org', 'demo.opengeo.org',
                 'www.openstreetmap.org', 'sample.azavea.com',
                 'v2.suite.opengeo.org', 'v-swe.uni-muenster.de:8080', 
-                'vmap0.tiles.osgeo.org', 'www.openrouteservice.org']
+                'vmap0.tiles.osgeo.org', 'www.openrouteservice.org',
+                'deegree3-testing.deegree.org']
 
 method = os.environ["REQUEST_METHOD"]
 
Index: examples/wps.html
===================================================================
--- examples/wps.html	(revision 0)
+++ examples/wps.html	(revision 0)
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <title>OpenLayers WPS Builder Example</title>
+    <link rel="stylesheet" href="../theme/default/style.css" type="text/css">
+    <link rel="stylesheet" href="style.css" type="text/css">
+    <style type="text/css">
+        .olControlEditingToolbar .olControlModifyFeatureItemInactive {
+            background-image: url(../theme/default/img/draw_point_off.png);
+        }
+        .olControlEditingToolbar .olControlModifyFeatureItemActive {
+            background-image: url(../theme/default/img/draw_point_on.png);
+        }
+        textarea {
+            display: block;
+            width: 45em;
+            height: 3em;
+        }
+        label {
+            display: block;
+        }
+        .notsupported {
+            color: red;
+        }
+        button {
+            display: block;
+            margin-top: 10px;
+        }
+    </style>
+  </head>
+  <body>
+    <h1 id="title">WPS Builder Example</h1>
+
+    <div id="tags">
+        wps, process
+    </div>
+
+    <div id="shortdesc">Using WPS formats to interact with WPS</div>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        <p>This example shows WPS in action by using the WPSCapabilities,
+        WPSDescribeProcess and WPSExecute formats. See
+        <a target="_blank" href="bing-tiles.js">wps.js</a> for the
+        source code.</p>
+    </div>
+    <div>
+        <ol>
+            <li>Select a process from the list below. The list is populated
+            with the result of a WPS GetCapabilities request, parsed using
+            <code>OpenLayers.Format.WPSCapabilities::read</code>.</li>
+            <li>Fill out the Input form. Hover over fields to get a description.
+            To use a geometry from the map as input, select the geometry on the
+            map (using the pen symbol on the left of the toolbar) and just
+            click the field. The form is generated from the object returned by
+            <code>OpenLayers.Format.WPSDescribeProcess::read</code></li>
+            <li>Click "Execute" and examine the result in the result text area.
+            If the result can be parsed as features, it will be displayed on
+            the map as well. The process data is sent to the server with the
+            serialized XML from <code>OpenLayers.Format.WPSExecute::write</code>,
+            which can use a modified
+            <code>OpenLayers.Format.WPSDescribeProcess</code> result object as
+            input.</li>
+        </ol>
+        <select id="processes"><option>Select a process</option></select>
+        <p id="abstract"></p>
+        <div id="input"></div>
+        <div id="output"></div>
+    </div>
+    <script src="../lib/OpenLayers.js"></script>
+    <script src="wps.js"></script>
+  </body>
+</html>
