Author: sylvain
Date: Fri May 6 15:01:55 2005
New Revision: 168669
URL: http://svn.apache.org/viewcvs?rev=168669&view=rev
Log:
Ajax: fix a bug with boolean inputs, add some visual sugar by highlighting
updated areas
Modified:
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml
cocoon/blocks/core/forms/trunk/samples/welcome.xml
Modified:
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
URL:
http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js?rev=168669&r1=168668&r2=168669&view=diff
==============================================================================
---
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
(original)
+++
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/flow/javascript/Form.js
Fri May 6 15:01:55 2005
@@ -114,11 +114,7 @@
this.locale = java.util.Locale.getDefault();
viewdata["locale"] = this.locale;
- // Keep the first continuation that will be created as the result of this
function
- var result = null;
-
var finished = false;
- this.isValid = false;
var comingBack = false;
var bookmark = cocoon.createWebContinuation(ttl);
@@ -126,10 +122,11 @@
if (comingBack) {
// We come back to the bookmark: process the form
- if (cocoon.request.getParameter("cocoon-ajax-continue") != null) {
- //TODO(SW): when exiting AJAX rountrips, better send a new
continuation
- //to ensure we really come back from a "100-continue" response
(see below)
- //and this isn't a forged request
+ if (finished && cocoon.request.getParameter("cocoon-ajax-continue") !=
null) {
+ // A request with this parameter is sent by the client upon
receiving the indication
+ // that Ajax interaction on the form is finished (see below).
+ // We also check "finished" to ensure we won't exit showForm()
because of some
+ // faulty or hacked request. It's set to false, this will simply
redisplay the form.
return bookmark;
}
@@ -155,7 +152,7 @@
// Ask the client to load the page
cocoon.response.setHeader("X-Cocoon-Ajax", "continue");
cocoon.response.setHeader("Content-Length", "0");
- cocoon.sendStatus(200); // Continue
+ cocoon.sendStatus(200);
FOM_Cocoon.suicide();
}
Modified:
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
URL:
http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js?rev=168669&r1=168668&r2=168669&view=diff
==============================================================================
---
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
(original)
+++
cocoon/blocks/core/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
Fri May 6 15:01:55 2005
@@ -94,10 +94,15 @@
// Iterate on all form controls
for (var i = 0; i < form.elements.length; i++) {
input = form.elements[i];
- if (input.type != "submit" && input.type != "image") {
+ if (input.type == "submit" || input.type == "image") {
// Skip buttons
- result += "&" + encodeURIComponent(input.name) + "=" +
encodeURIComponent(input.value);
+ continue;
}
+ if ((input.type == "checkbox" || input.type == "radio") &&
!input.checked) {
+ // Skip unchecked checkboxes and radio buttons
+ continue;
+ }
+ result += "&" + encodeURIComponent(input.name) + "=" +
encodeURIComponent(input.value);
}
return result;
}
@@ -131,10 +136,20 @@
return;
}
- BrowserUpdate.processResponse(doc);
- }
+ BrowserUpdate.processResponse(doc);
+ }
} else {
- alert("request failed : " + request.status);
+/* var str = "";
+ for(prop in request) {
+ str += prop
+ str += " = "
+ str += request[prop];
+ str += '\n';
+ }
+ alert(str);
+ alert(request.getAllResponseHeaders());
+*/
+ alert("request failed - status: " + request.status);
}
}
@@ -146,7 +161,7 @@
ATTRIBUTE_NODE : 2,
TEXT_NODE : 3,
CDATA_SECTION_NODE : 4,
- ENTITY_REFERENCE_NODE : 5,
+ ENTITY_REFERENCE_NODE : 5,
ENTITY_NODE : 6,
PROCESSING_INSTRUCTION_NODE : 7,
COMMENT_NODE : 8,
@@ -269,6 +284,140 @@
oldElement.parentNode.replaceChild(newElement, oldElement);
// Ensure the new node has the correct id
newElement.setAttribute("id", id);
+
+ if (BrowserUpdate.highlight) {
+ BrowserUpdate.highlight(newElement);
+ }
}
+}
+
+//-------------------------------------------------------------------------------------------------
+// Fader used to highlight page areas that have been updated
+//-------------------------------------------------------------------------------------------------
+
+/**
+ * Create a fader that will progressively change an element's background color
from
+ * a given color back to its original color.
+ *
+ * @param elt the element to fade
+ * @param color the starting color (default yellow)
+ * @param duration the fade duration in msecs (default 1000)
+ * @param fps the animation frames per seconds (default 25)
+ */
+function Fader(elt, color, duration, fps) {
+ // Set default values
+ if (!color) color = "#FFFF80"; // yellow
+ if (!duration) duration = 1000; // 1 sec
+ if (!fps) fps = 25; // 25 frames/sec
+
+ this.element = elt;
+ this.fromColor = Fader.colorToRgb(color);
+ this.toColor = Fader.colorToRgb(Fader.getBgColor(this.element));
+
+ this.maxFrames = Math.round(fps * duration / 1000.0);
+ this.delay = duration / this.maxFrames;
+}
+
+/**
+ * Creates a default fader for a given element. This function can be used to
set BrowserUpdate.highlight
+ */
+Fader.fade = function(elt) {
+ new Fader(elt).start();
+}
+
+Fader.prototype.start = function() {
+ this.frame = 0;
+ this._changeColor();
+}
+
+Fader.prototype._changeColor = function() {
+ if (this.frame < this.maxFrames) {
+ // Schedule the next iteration right now to keep a more accurate timing
+ var fader = this;
+ setTimeout(function() {fader._changeColor();}, this.delay);
+ }
+ var newColor = new Array(3);
+ for (var channel = 0; channel < 3; channel++) {
+ newColor[channel] = Math.floor(
+ this.fromColor[channel] * ((this.maxFrames - this.frame) /
this.maxFrames) +
+ this.toColor[channel] * (this.frame/this.maxFrames)
+ );
+ }
+
+ this.frame++;
+ var color = Fader.rgbToColor(newColor[0], newColor[1], newColor[2]);
+ this.element.style.backgroundColor = color;
+}
+
+/** Converts a "#RRGGBB" color as an array of 3 ints */
+Fader.colorToRgb = function(hex) {
+ return [
+ parseInt(hex.substr(1,2),16),
+ parseInt(hex.substr(3,2),16),
+ parseInt(hex.substr(5,2),16) ];
+}
+
+/** Converts rgb values to a "#RRGGBB" color */
+Fader.rgbToColor = function(r, g, b) {
+ r = r.toString(16); if (r.length == 1) r = '0' + r;
+ g = g.toString(16); if (g.length == 1) g = '0' + g;
+ b = b.toString(16); if (b.length == 1) b = '0' + b;
+ return "#" + r + g + b;
+}
+
+/** Get the background color of an element */
+Fader.getBgColor = function(elt) {
+ while(elt) {
+ var c;
+ if (window.getComputedStyle) c =
window.getComputedStyle(elt,null).getPropertyValue("background-color");
+ if (elt.currentStyle) c = elt.currentStyle.backgroundColor;
+ if ((c != "" && c != "transparent") || elt.tagName == "BODY") { break;
}
+ elt = elt.parentNode;
+ }
+ if (c == undefined || c == "" || c == "transparent" || c == "white") c =
"#FFFFFF";
+
+ var rgb =
c.match(/rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/);
+ if (rgb) return
this.rgbToColor(parseInt(rgb[1]),parseInt(rgb[2]),parseInt(rgb[3]));
+ return c;
+}
+
+BrowserUpdate.highlight = Fader.fade;
+
+//-------------------------------------------------------------------------------------------------
+// Blinker used to highlight page areas that have been updated
+//-------------------------------------------------------------------------------------------------
+
+function Blinker(elt, color, hltDelay, normalDelay, blinks) {
+ this.element = elt;
+ if (!color) color = "#FFFF80"; // yellow
+ if (!hltDelay) hltDelay = 100;
+ if (!normalDelay) normalDelay = 100;
+ if (!blinks) blinks = 2;
+
+ this.hltColor = color;
+ this.hltDelay = hltDelay;
+ this.normalDelay = normalDelay;
+ this.normalColor = Fader.getBgColor(elt);
+ this.maxBlinks = blinks * 2;
+ this.blink = 0;
+}
+
+Blinker.prototype.start = function() {
+ this.blink = 0;
+ this._doBlink();
+}
+
+Blinker.blink = function(elt) {
+ new Blinker(elt).start();
+}
+
+Blinker.prototype._doBlink = function() {
+ var hlt = (this.blink % 2 == 0);
+ this.element.style.backgroundColor = hlt ? this.hltColor :
this.normalColor;;
+ if (this.blink <= this.maxBlinks) {
+ var blinker = this;
+ setTimeout(function() {blinker._doBlink();}, hlt ? this.hltDelay :
this.normalDelay);
+ }
+ this.blink++;
}
Modified: cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml
URL:
http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml?rev=168669&r1=168668&r2=168669&view=diff
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml
(original)
+++ cocoon/blocks/core/forms/trunk/samples/forms/carselector_template.xml Fri
May 6 15:01:55 2005
@@ -18,11 +18,11 @@
<!-- Import the macros that define CForms template elements -->
<jx:import
uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
<title>Car selector</title>
- <para>This example illustrates how you can programmatically update the
- content of a selection list.</para>
- <para>
- This sample illustrates event-handling in Cocoon Forms and how selection
lists can be changed
- programmatically.
+ <para>This example illustrates:
+ <ul>
+ <li>how you can programmatically update the content of a selection
list,</li>
+ <li>the AJAX features that allow partial page reloads and the
associated visual effects.</li>
+ </ul>
</para>
<para>
Event-handlers are defined in the form definition to update the selection
lists and set
@@ -34,6 +34,30 @@
See "carselector_form.xml" and "carselector_template.xml" to see how this
is done.
</para>
<content>
+ <script language="JavaScript">
+ var currentEffect = "fade";
+ function setEffect(effect) {
+ if (effect == "none") {
+ BrowserUpdate.highlight = null;
+ } else if (effect == "fade") {
+ BrowserUpdate.highlight = Fader.fade;
+ } else if (effect == "blink") {
+ BrowserUpdate.highlight = Blinker.blink;
+ }
+
+ document.getElementById(currentEffect).style.fontWeight = "";
+ document.getElementById(effect).style.fontWeight = "bold";
+ currentEffect = effect;
+ return false;
+ }
+ </script>
+ <para>
+ Page update effect:
+ <a id="none" href="#" onclick="setEffect('none')">None</a> -
+ <a style="font-weight: bold" id="fade" href="#"
onclick="setEffect('fade')">Fade</a> -
+ <a id="blink" href="#" onclick="setEffect('blink')">Blink</a>
+ </para>
+
<ft:form-template action="carselector" method="POST" ajax="true">
<ft:continuation-id/>
<fi:group>
Modified: cocoon/blocks/core/forms/trunk/samples/welcome.xml
URL:
http://svn.apache.org/viewcvs/cocoon/blocks/core/forms/trunk/samples/welcome.xml?rev=168669&r1=168668&r2=168669&view=diff
==============================================================================
--- cocoon/blocks/core/forms/trunk/samples/welcome.xml (original)
+++ cocoon/blocks/core/forms/trunk/samples/welcome.xml Fri May 6 15:01:55 2005
@@ -30,14 +30,18 @@
<sample name="Cocoon Forms Documentation"
href="http://cocoon.apache.org/2.1/userdocs/forms/">
Documentation is available on the Cocoon site.
</sample>
+ <note>
+ Samples with the (Ajax) mark use the new transparent Ajax (Asynchronous
Javascript And XML) framework
+ to reduce client/server roundtrips and perform partial page updates.
+ </note>
</group>
<group name="Basic Samples">
<sample name="Various (Actions)" href="form1">This sample shows validation,
event handling and various Cocoon Forms features.</sample>
<sample name="Various (Flowscript)" href="form1.flow">The same sample as
above using Flowscript.</sample>
<sample name="Registration" href="registration">A simple registration
form.</sample>
- <sample name="Car selector" href="carselector">Illustrates programmatically
changing selectionlists.</sample>
- <sample name="XHR Car selector" href="xhr_carselector">Same sample, using
XmlHttpRequest to reduce client/server roundtrips.</sample>
+ <sample name="Car selector" href="carselector">(Ajax) Illustrates
programmatically changing selectionlists.</sample>
+ <sample name="XHR Car selector" href="xhr_carselector">Same sample, with the
historical first use of XmlHttpRequest to reduce client/server
roundtrips.</sample>
<sample name="Country selector" href="countryselector">Illustrates
programmatically changing flow-jxpath selectionlists.</sample>
<sample name="Upload" href="upload">Shows an upload widget used with
Flowscript</sample>
<sample name="Form Model GUI" href="form_model_gui.flow">Illustrates the use
of Class, New, Struct, and Union.</sample>
@@ -88,14 +92,14 @@
widgets.
</note>
<sample name="Dynamic repeater template" href="do-dynaRepeater.flow">
- Shows a simple repeater, which isn't displayed at all if empty, and whose
row action depend on
+ (Ajax) Shows a simple repeater, which isn't displayed at all if empty,
and whose row action depend on
the row number.
</sample>
<sample name="Datasource chooser" href="do-datasourceChooser.flow">
- A datasource chooser, illustrating the fd:union widget.
+ (Ajax) A datasource chooser, illustrating the fd:union widget.
</sample>
<sample name="Task tree" href="do-taskTree.flow">
- A project work breakdown into a hierarchy of tasks, showing the use of
the fd:class and fd:new
+ (Ajax) A project work breakdown into a hierarchy of tasks, showing the
use of the fd:class and fd:new
widgets.
</sample>
</group>