Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/HtmlProducerAjaxInfo.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/HtmlProducerAjaxInfo.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/HtmlProducerAjaxInfo.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/HtmlProducerAjaxInfo.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,85 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.magma.website.CompoundType;
+import org.apache.magma.website.Head;
+import org.apache.magma.website.HtmlProducer;
+import org.apache.magma.website.Producer;
+import org.apache.magma.website.autoajax.data.LocalElement;
+
+public abstract class HtmlProducerAjaxInfo extends CollectedAjaxInfo {
+
+       protected HtmlProducer producer;
+       protected List<LocalElement> children = null;
+       
+       public HtmlProducerAjaxInfo(CollectedAjaxInfo parent, HtmlProducer 
producer) {
+               super(parent);
+               this.producer = producer;
+       }
+
+       public String getLocalurl() {
+               return producer.getCompletePath();
+       }
+       
+       protected String produceJson() {
+               return "\"localurl\":\"" + this.getLocalurl() + "\", ";
+       }
+       
+       public String getId() {
+               return Integer.toHexString(getLocalurl().hashCode() + 
getClass().getName().hashCode()) + "/" + 
Integer.toHexString(getAdditionalHash());
+       }
+       
+       @Override
+       public List<LocalElement> getChildren() {
+               if (children == null) {
+                       children = new ArrayList<LocalElement>();
+                       for (CompoundType type : CompoundType.values()) {
+                               List<HtmlProducer> on = 
((List<HtmlProducer>)(List)producer.findCompoundedOn(type));
+                               if (on.size() > 0) {
+                                       children.add(new 
CompoundTypeAjaxInfo(this,type, on));
+                               }
+                               /*
+                               // TODO why should InternalHtmlProducer list 
compounded elements as children!? 
+                               // Check instanceof needed to avoid endless 
internals having other internals as children
+                               if (type == CompoundType.BEFORE && !(this 
instanceof InternalHtmlProducerAjaxInfo)) {
+                                       children.add(new 
InternalHtmlProducerAjaxInfo(this, producer));
+                               }
+                               */
+                               if (type == CompoundType.BEFORE) {
+                                       children.add(new 
InternalHtmlProducerAjaxInfo(this, producer));
+                               }
+                       }
+               }
+               return children;
+       }
+
+       @Override
+       public long getTimestamp() {
+               long expires = producer.getExpires();
+               if (expires == Producer.EXPIRES_UNSET) {
+                       expires = Long.MAX_VALUE;
+               }
+               return System.currentTimeMillis() - expires;
+       }
+       
+       @Override
+       protected boolean matches(Object key) {
+               if (!(key instanceof HtmlProducer)) return false;
+               return ((HtmlProducer)key) == this.producer;
+       }
+
+       @Override
+       public void renderHead(Head head) {
+               this.producer.head(head);
+       }
+
+       public HtmlProducer getProducer() {
+               return producer;
+       }
+       
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/InternalHtmlProducerAjaxInfo.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/InternalHtmlProducerAjaxInfo.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/InternalHtmlProducerAjaxInfo.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/InternalHtmlProducerAjaxInfo.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,67 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.magma.website.CompoundType;
+import org.apache.magma.website.HtmlProducer;
+import org.apache.magma.website.Producer;
+import org.apache.magma.website.autoajax.data.LocalElement;
+
+public class InternalHtmlProducerAjaxInfo extends HtmlProducerAjaxInfo {
+
+       
+       public InternalHtmlProducerAjaxInfo(HtmlProducerAjaxInfo parent, 
HtmlProducer producer) {
+               super(parent,producer);
+       }
+       public long getRefresh() {
+               return producer.getForceRefreshAfter();
+       }
+       public boolean isDeferred() {
+               return producer.isDeferrable();
+       }
+       
+       protected String getType() {
+               return "int";
+       }
+       
+       protected String produceJson() {
+               return super.produceJson() +
+                       (getRefresh() > 0 ? "\"refresh\":\"" + getRefresh() + 
"\"," : "") +
+                       (isDeferred() ? "\"deferred\": \"true\"," : "");
+       }
+       
+       @Override
+       protected int getAdditionalHash() {
+               String addkey = producer.toKey();
+               return super.getAdditionalHash() + (addkey == null ? 0 : 
addkey.hashCode());
+       }
+       
+       @Override
+       public void renderBody(OutputStream out) throws IOException {
+               producer.produceWithoutCompound(out);
+       }
+       
+       @Override
+       public List<LocalElement> getChildren() {
+               if (children == null) {
+                       List<HtmlProducer> compounds = new 
ArrayList<HtmlProducer>();
+                       for (CompoundType type : CompoundType.values()) {
+                               List<HtmlProducer> on = 
((List<HtmlProducer>)(List)producer.findCompoundedOn(type));
+                               if (on.size() > 0) {
+                                       compounds.addAll(on);
+                               }
+                       }
+                       List<HtmlProducer> pc = 
((List<HtmlProducer>)(List)producer.getChildren());
+                       pc = new ArrayList<HtmlProducer>(pc);
+                       pc.removeAll(compounds);
+                       children = new ArrayList<LocalElement>();
+                       for (HtmlProducer htmlProducer : pc) {
+                               children.add(new 
OrphanHtmlProducerAjaxInfo(this, htmlProducer));
+                       }
+               }
+               return children;
+       }
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/LocalInvalidator.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/LocalInvalidator.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/LocalInvalidator.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/LocalInvalidator.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,20 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import org.apache.magma.basics.startup.Cycle;
+import org.apache.magma.website.autoajax.LocalInvalidations;
+import org.apache.magma.website.autoajax.data.Invalidator;
+import org.apache.magma.website.autoajax.data.LocalElement;
+import org.apache.magma.website.autoajax.data.StateElement;
+
+public class LocalInvalidator implements Invalidator {
+
+       private LocalInvalidations invs = Cycle.get().getLocalInvalidations();
+       
+       public boolean isInvalid(StateElement remote, LocalElement local) {
+               if (local instanceof HtmlProducerAjaxInfo) {
+                       return 
invs.isInvalidated(((HtmlProducerAjaxInfo)local).getLocalurl());
+               }
+               return false;
+       }
+
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/OrphanHtmlProducerAjaxInfo.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/OrphanHtmlProducerAjaxInfo.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/OrphanHtmlProducerAjaxInfo.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/OrphanHtmlProducerAjaxInfo.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,24 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.magma.website.HtmlProducer;
+
+public class OrphanHtmlProducerAjaxInfo extends HtmlProducerAjaxInfo {
+
+       public OrphanHtmlProducerAjaxInfo(InternalHtmlProducerAjaxInfo parent, 
HtmlProducer producer) {
+               super(parent, producer);
+       }
+
+       @Override
+       public void renderBody(OutputStream out) throws IOException {
+               producer.produce(out);
+       }
+       
+       @Override
+       protected String getType() {
+               return "orph";
+       }
+
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateAjaxInfo.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateAjaxInfo.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateAjaxInfo.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateAjaxInfo.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,92 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.magma.website.Head;
+import org.apache.magma.website.HtmlProducer;
+import org.apache.magma.website.autoajax.data.LocalElement;
+import org.apache.magma.website.templating.Template;
+import org.apache.magma.website.templating.TemplateData;
+import org.apache.magma.website.utils.URLRewritingStream;
+
+public class TemplateAjaxInfo extends HandleAjaxInfo {
+
+       protected Template template = null;
+       protected List<LocalElement> children = null;
+       
+       public TemplateAjaxInfo(Template template) {
+               super(null);
+               this.setHandle(template.getClass().getName());
+               this.template = template;
+       }
+       
+       protected String getType() {
+               return "tpl";
+       }
+
+       @Override
+       public List<LocalElement> getChildren() {
+               if (children == null) {
+                       children = new ArrayList<LocalElement>();
+                       Map<String, List<HtmlProducer>> parsedZones = 
template.getParsedZones();
+                       List<String> names = 
template.getTemplateData().getZoneNames();
+                       for (String name: names) {
+                               if (name.equals("__head_content")) continue;
+                               List<HtmlProducer> zoneds = 
parsedZones.get(name);
+                               if (zoneds == null) zoneds = 
Collections.emptyList();
+                               children.add(new TemplateZoneAjaxInfo(this, 
name, zoneds));
+                       }
+               }               
+               return children;
+       }
+
+       @Override
+       public long getTimestamp() {
+               return 0;
+       }
+       
+       @Override
+       protected boolean matches(Object key) {
+               if (!(key instanceof Template)) return false;
+               return ((Template)key) == this.template;
+       }
+
+       @Override
+       public void renderBody(OutputStream out) throws IOException {
+               // Se internal aspect that avoid sending "body" and "head"
+               template.produce((URLRewritingStream)out);
+       }
+       
+       public static aspect RenameBodyAndHead {
+               
+               String around() : 
+                       execution(String TemplateData.getRaw(..))
+                       && cflow(execution(* TemplateAjaxInfo.renderBody(..))) {
+                       String ret = proceed();
+                       ret = ret.replaceAll("<body", "<div type='body' ");
+                       ret = ret.replaceAll("</body", "</div");
+                       ret = ret.replaceAll("<head", "<div type='head' ");
+                       ret = ret.replaceAll("</head", "</div");
+                       return ret;
+               }
+               
+       }
+       
+
+       @Override
+       public void renderHead(Head head) {
+       }
+
+       public Template getTemplate() {
+               return template;
+       }
+       
+       
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateZoneAjaxInfo.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateZoneAjaxInfo.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateZoneAjaxInfo.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/TemplateZoneAjaxInfo.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,63 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.magma.website.Head;
+import org.apache.magma.website.HtmlProducer;
+import org.apache.magma.website.autoajax.data.LocalElement;
+import org.apache.magma.website.utils.URLRewritingStream;
+
+public class TemplateZoneAjaxInfo extends HandleAjaxInfo {
+
+       private List<HtmlProducer> prods;
+       private List<LocalElement> children = null;
+
+       public TemplateZoneAjaxInfo(TemplateAjaxInfo parent, String key, 
List<HtmlProducer> value) {
+               super(parent);
+               setHandle(key);
+               this.prods = value;
+       }
+
+       @Override
+       protected String getType() {
+               return "tplzone";
+       }
+       
+       @Override
+       public List<LocalElement> getChildren() {
+               if (children == null) {
+                       children = new ArrayList<LocalElement>();
+                       for (HtmlProducer prod : prods) {
+                               if (prod != null) children.add(new 
ZonedHtmlProducerAjaxInfo(this,prod));
+                       }
+               }
+               return children;
+       }
+
+       @Override
+       public long getTimestamp() {
+               return 0;
+       }
+
+       @Override
+       protected boolean matches(Object key) {
+               if (!(key instanceof String)) return false;
+               return getHandle().equals(key);
+       }
+
+       @Override
+       public void renderBody(OutputStream out) throws IOException {
+               ((TemplateAjaxInfo)parent).template.sendZone(this.getHandle(), 
(URLRewritingStream)out);
+       }
+
+       @Override
+       public void renderHead(Head head) {
+               for (HtmlProducer prod : prods) {
+                       if (prod != null) prod.head(head);
+               }               
+       }
+
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/ZonedHtmlProducerAjaxInfo.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/ZonedHtmlProducerAjaxInfo.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/ZonedHtmlProducerAjaxInfo.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/java/org/apache/magma/website/autoajax/webdata/ZonedHtmlProducerAjaxInfo.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,28 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.magma.website.Head;
+import org.apache.magma.website.HtmlProducer;
+import org.apache.magma.website.utils.URLRewritingStream;
+
+public class ZonedHtmlProducerAjaxInfo extends HtmlProducerAjaxInfo {
+
+       public ZonedHtmlProducerAjaxInfo(TemplateZoneAjaxInfo parent, 
HtmlProducer prod) {
+               super(parent, prod);
+       }
+
+       protected String getType() {
+               return "zoneprod";
+       }
+
+       @Override
+       public void renderBody(OutputStream out) throws IOException {
+               TemplateZoneAjaxInfo myp = ((TemplateZoneAjaxInfo)parent);
+               
((TemplateAjaxInfo)myp.getParent()).getTemplate().sendProducer(myp.getHandle(), 
producer, (URLRewritingStream)out);
+               // TODO Auto-generated method stub
+               
+       }
+
+}

Added: 
labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/autoajax.js
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/autoajax.js?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/autoajax.js
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/autoajax.js
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,734 @@
+if (window.magma == undefined) {
+       window.magma = function() {};
+}
+
+magma.autoajax = new function() {
+       
+       /**
+        * Current state
+        */
+       this.state = new Object();
+       this.state.elements = new Object();
+       
+       this.scriptsLoaded = new Object();
+       
+       /**
+        * Server current timestamp, this avoid
+        * timezone/computer clock offsets.
+        */
+       this.serverts = null;
+       
+       /**
+        * Relative part of the server url to the java context.
+        */
+       this.serverurl = "";
+       
+       
+       /**
+        * Initialize the state
+        */
+       this.initState = function(acstate) {
+               this.state.elements = acstate;
+               this.recalcState();
+       }
+       
+       /**
+        * Updates one or more entries of the state.
+        * This method can be called multiple times with minimum
+        * overhead, but recalcState must be called when
+        * one or more updates have been performed to have
+        * other methods work correctly. 
+        */
+       this.updateState = function(statepart) {
+               for (var id in statepart) {
+                       this.state.elements[id] = statepart[id];
+                       var ele = document.getElementById(id);
+                       if (ele) {
+                               this.state.elements[id].domElement=ele;
+                       }
+               }
+       }
+       
+       /**
+        * After modifying the state with updateState
+        * or initState, this method must be called
+        * to have the state perform self-check and 
+        * inspect the dom and reset binding with dom 
+        * elements properly.
+        * 
+        */
+       this.recalcState = function() {
+               this._stateDom();
+       }
+       
+       /**
+        * Finds and connects dom elements and
+        * hierarchy on the state. State elements
+        * for which a corresponding element is not
+        * found will be remove from the state.
+        */
+       this._stateDom = function() {
+               // Find dom elements
+               for (var id in this.state.elements) {
+                       var stategrp = this.state.elements[id];
+                       stategrp.stateid = id;
+                       if (!stategrp.timestamp) {
+                               stategrp.timestamp = this.serverts;
+                       }
+                       if (stategrp.type == 'tpl') {
+                               this.state.root = stategrp;
+                               stategrp.domElement=document.body;
+                               stategrp.children=new Array();
+                       } else {
+                               var ele = document.getElementById(id);
+                               if (!ele) {
+                                       delete(this.state.elements[id]);
+                               } else {
+                                       stategrp.domElement=ele;
+                                       stategrp.children=new Array();
+                                       stategrp.parent=null;
+                               }
+                       }
+               }
+               // Find parents and children
+               for (var id in this.state.elements) {
+                       var stategrp = this.state.elements[id];
+                       if (stategrp.type == 'tpl') continue;
+                       var ele = stategrp.domElement;
+                       ele = ele.parentNode;
+                       while (ele) {
+                               if (this.state.elements[ele.id]) {
+                                       
stategrp.parent=this.state.elements[ele.id];
+                                       
this.state.elements[ele.id].children.push(stategrp);
+                                       break;
+                               };
+                               ele = ele.parentNode;
+                       }
+                       if (stategrp.parent == null) {
+                               stategrp.parent = this.state.root;
+                               this.state.root.children.push(stategrp);
+                       }
+               }               
+       }
+       
+       this.findStatesByUrl = function(url) {
+               var ret = new Array();
+               for (id in this.state.elements) {
+                       var grp = this.state.elements[id];
+                       if (grp.localurl && grp.localurl.match("^"+url) == url) 
{
+                               ret.push(grp);
+                       }
+               }
+               return ret;
+       }
+       
+       /**
+        * Dumps an XML representation of the style
+        * suitable for server side processing.
+        */
+       this.getState = function() {
+               return this._elementState(this.state.root,0);
+       }
+       
+       /**
+        * Recursively dump XML representation of a state element
+        */
+       this._elementState = function(stategrp, level) {
+               var ret = "<l" + level + " ";
+               ret += "id='" + stategrp.stateid + "' ";
+               ret += "ty='" + stategrp.type + "' ";
+               if (stategrp.handle) {
+                       ret += "ha='" + stategrp.handle + "' ";
+               }
+               if (stategrp.localurl) {
+                       ret += "ur='" + stategrp.localurl + "' ";
+               }
+               if (stategrp.timestamp) {
+                       ret += "ti='" + stategrp.timestamp + "' ";
+               }
+               ret += '>';
+               for (var i = 0; i < stategrp.children.length; i++) {
+                       ret += this._elementState(stategrp.children[i], level + 
1);
+               }
+               ret += '</l' + level + '>';
+               return ret;
+       }
+       
+       /**
+        * Performs an in place renew of a piece.
+        * 
+        * This method batches if more than one call is made
+        * in the same thread span.
+        */
+       this.renew = function(stateId) {
+               var me = this;
+               var stategrp = this.state.elements[stateId];
+               var jqele = jQuery(stategrp.domElement);
+               var event = jQuery.Event("renewing");
+               event.state = stategrp;
+               jqele.trigger(event);
+               var url = stategrp.localurl;
+               var real = this.serverurl + url + ".ajax";
+               jQuery.ajax({
+                       url: real,
+                       type: "GET",
+                       dataType: "html",
+                       complete: function(res, status){
+                               if ( status == "success" || status == 
"notmodified" ) {
+                                       var payload = $(res.responseText);
+                                       jqele.empty();
+                                       
jqele.append(payload.filter('.AjaxWrapper').removeClass('AjaxWrapper'));
+                                       
me.parseMagmaHead(payload.filter('.AjaxHead'), payload, function() {
+                                               var event = 
jQuery.Event("renewed");
+                                               event.state = stategrp;
+                                               jqele.trigger(event);
+                                       });
+                               } else {
+                                       var event = jQuery.Event("notrenewed");
+                                       event.state = stategrp;
+                                       jqele.trigger(event);
+                               }                               
+                       }
+               });             
+                               
+       }
+       
+       /**
+        * Performs a remove of an element present
+        * in the state.
+        */
+       this.performRemove = function(stateId) {
+               var stategrp = this.state.elements[stateId];
+               var jqele = jQuery(stategrp.domElement);
+               var event = jQuery.Event("removing");
+               event.state = stategrp;
+               jqele.trigger(event);
+               if (!event.isDefaultPrevented()) {
+                       jQuery(magma.autoajax).trigger(event);
+               }
+               if (!event.isDefaultPrevented()) {
+                       var parent = jqele.parent(); 
+                       jqele.remove();
+                       event = jQuery.Event("removed");
+                       event.state = stategrp;
+                       parent.trigger(event);
+                       jQuery(magma.autoajax).trigger(event);
+               }
+       }
+       
+       this.performAdd = function(position, relative, what) {
+               var stategrp = this.state.elements[relative];
+               var jqele = jQuery(stategrp.domElement);
+               var payload = $(what).children();
+               var addtarget = null;
+               if (position == 'inside') {
+                       jqele.append(payload);
+                       addtarget = jqele;
+               } else if (position == 'after') {
+                       jqele.after(payload);
+               } else if (position == 'before') {
+                       jqele.before(payload);
+               }
+               var event = jQuery.Event("added");
+               payload.trigger(event);
+       }
+
+       this.performRenew = function(which, what) {
+               var stategrp = this.state.elements[which];
+               var jqele = jQuery(stategrp.domElement);
+               var payload = $(what).children();
+               jqele.after(payload);
+               jqele.remove();
+               var event = jQuery.Event("renewed");
+               event.state = stategrp;
+               payload.trigger(event);
+       }
+       
+       
+       this.performNewPage = function(ele) {
+               var event = jQuery.Event("newpaging");
+               $(document).trigger(event);
+               var jqe = jQuery(ele);
+               var body = jQuery('body');
+               var head = jQuery('head');
+               body.empty().append(jqe.find('div[type="body"]').children());
+               head.empty().append(jqe.find('div[type="head"]').children());
+               var event = jQuery.Event("newpaged");
+               $(document).trigger(event);
+       }
+
+       this.performMove = function(id1, relation, id2) {
+               var stategrp1 = this.state.elements[id1];
+               var jqele1 = jQuery(stategrp1.domElement);
+               
+               var stategrp2 = this.state.elements[id2];
+               var jqele2 = jQuery(stategrp2.domElement);
+               
+               var event = jQuery.Event("moving");
+               event.state = stategrp1;
+               jqele1.trigger(event);
+               jqele1.detach();
+               if (relation == 'after') {
+                       jqele2.after(jqele1);
+               } else if (relation == 'before') {
+                       jqele2.before(jqele1);
+               }               
+               var event = jQuery.Event("moved");
+               event.state = stategrp1;
+               jqele1.trigger(event);
+       }
+               
+       /**
+        * Performs an Ajax Browser Update. It will
+        * contact the server, asking for the specified
+        * magma-relative url, and send the current state.
+        * Server will then provide change requests needed
+        * to update the page from its current state to its
+        * desired appearance.
+        */
+       this.ajaxBu = function(rel, otherparams) {
+               var me = this;
+               // TODO send the request, parse the resulting
+               // head and directives.
+               var real = this.serverurl + rel + ".ajaxbu";
+               var state = this.getState();
+               if (otherparams) {
+                       otherparams[otherparams.length] = 
{name:'state',value:state};
+               } else {
+                       otherparams = {state: state};
+               }
+               jQuery.ajax({
+                       url: real,
+                       type: "POST",
+                       dataType: "html",
+                       data: otherparams,
+                       complete: function(res, status){
+                               if ( status == "success" || status == 
"notmodified" ) {
+                                       var payload = $(res.responseText);
+                                       var actions = payload.children();
+                                       var newpages = 
actions.filter(".newpage");
+                                       if (newpages.length > 0) {
+                                               newpages.each(function() {
+                                                       me.performNewPage(this);
+                                                       var statemod = 
$(this).attr('state');
+                                                       if (statemod != '' && 
typeof statemod != 'undefined') {
+                                                               statemod = 
jQuery.parseJSON('{' + statemod + '}');
+                                                               
me.updateState(statemod);
+                                                       }
+                                                       me.recalcState();
+                                               });
+                                       } else {
+                                               
me.parseMagmaHead(payload.find('.ajaxhead'),payload, function() {
+                                                       actions.each(function() 
{
+                                                               try { 
+                                                                       var 
classes = this.className;
+                                                                       var 
acts = classes.split(' ');
+                                                                       if 
(acts[0] == 'add') {
+                                                                               
me.performAdd(acts[1], acts[2], this);
+                                                                       } else 
if (acts[0] == 'move') {
+                                                                               
me.performMove(acts[1], acts[2], acts[3]);                                      
                
+                                                                       } else 
if (acts[0] == 'renew') {
+                                                                               
me.performRenew(acts[1], this);
+                                                                       }
+                                                                       var 
statemod = $(this).attr('state');
+                                                                       if 
(statemod != '' && typeof statemod != 'undefined') {
+                                                                               
statemod = jQuery.parseJSON('{' + statemod + '}');
+                                                                               
me.updateState(statemod);
+                                                                       }
+                                                               } catch (e) {
+                                                                       
console.log(e);
+                                                               }
+                                                       });
+                                                       
actions.filter(".remove").each(function() {
+                                                               try {
+                                                                       var 
classes = this.className;
+                                                                       var 
acts = classes.split(' ');
+                                                                       
me.performRemove(acts[1]);
+                                                               } catch (e) {
+                                                                       
console.log(e);
+                                                               }
+                                                       });
+                                                       me.recalcState();
+                                               });
+                                       }
+                                       //alert("Success");
+                               }
+                       }
+               });             
+       }
+       
+       /**
+        * Adds dynamically elements to the document head based on the
+        * ajax response.
+        * 
+        * These means : adding missing .css files, loading missing .js
+        * files, executing inline script snippets.
+        * 
+        * @param recvhead jQuery object of the div (or any thing else)
+        * containing head elements from ajax request.
+        */
+       this.parseMagmaHead = function(recvhead, fullrecv, callback) {
+               var me = this;
+               var ajaxhead = recvhead.children();
+               var head = $('head')[0];
+               var scripts = new Array();
+               for (var i = 0; i < ajaxhead.length; i++) {
+                       var tag = ajaxhead[i].tagName;
+                       if (tag == 'LINK') {
+                               var check = tag;
+                               var url = ajaxhead[i].href;
+                               url = url.substring(url.lastIndexOf('/'));
+                               check += "[href$='" + url + "']";
+                               if ($(check, head).length > 0) continue;
+                               $(head).prepend(ajaxhead[i]);
+                       } else {
+                               head.appendChild(ajaxhead[i]);
+                       }
+               }
+               function checkScript() {
+                       var url = this.src;
+                       if (url != '') {
+                               url = url.substring(url.lastIndexOf('/'));
+                               var check = "script[src$='" + url + "']";
+                               if ($(check, head).length > 0) return;
+                               if (me.scriptsLoaded[url]) return;
+                       }
+                       scripts.push(this);
+               };
+               $(fullrecv).filter('script').each(checkScript);
+               $('script',fullrecv).each(checkScript);
+
+               if (scripts.length > 0) {
+                       this.lateLoad(scripts, 0, callback);
+               } else {
+                       callback.call(me);
+               }
+       }
+       /**
+        * Chain progressive loading of script elements
+        * (both inline script and external javascripts)
+        * 
+        * A function is needed cause some loading might be async.
+        */
+       this.lateLoad = function(scripts, index, callback) {
+               var me = this;
+               var ele = scripts[index];
+               if (ele.src && ele.src != '') {
+                       var src = ele.src;
+                       var inturl = src.substring(src.lastIndexOf('/'));
+                       me.scriptsLoaded[inturl] = true;
+                       // Hack for google maps, that require an explicit
+                       // async parameter when loaded after page load
+                       if (src.indexOf("google.com/maps") != -1) {
+                               src += "&async=2";
+                       }
+                       if (scripts.length - 1> index) {
+                               jQuery.getScript(src, function() { 
me.lateLoad(scripts, index + 1, callback) });
+                       } else {
+                               jQuery.getScript(src, function() { 
callback.call(me) });
+                       }
+               } else {
+                       var js = ele.text;
+                       jQuery.globalEval(js);
+                       if (scripts.length - 1> index) {
+                               me.lateLoad(scripts, index + 1, callback);
+                       } else {
+                               callback.call(me);
+                       }
+               }
+       }
+       
+
+       
+       /**
+        * Loads the page given in the hash. The hash
+        * could arrive from many places, a bookmarked
+        * page, the browser back, or hooking on links.
+        * 
+        * Will call the ajaxBu method.
+        */
+       this.loadHash = function(hash) {
+               if (hash == '/' && magma.autoajax.skippingHash) return;
+               if (hash == '/' && magma.autoajax.serverurl.match('/$')) {
+                       hash = '';
+               }
+               magma.autoajax.ajaxBu(hash,magma.autoajax.hashParams);
+       }
+       
+       /**
+        * Navigates to the given url, if possible (if
+        * it is a server local url and hash navigation is
+        * enabled) changing the hash of the page and updating 
+        * it using ajax. Otherwise, will redirect the browser
+        * to the given url. 
+        */
+       this.hashNavigate = function(href, params) {
+               this.hashParams = params;
+               var orighref = href;
+               // String protocol and host, as long as they are equal
+               if (href.indexOf(window.location.protocol) == 0) {
+                       href = href.substring(window.location.protocol.length);
+               }
+               if (href.indexOf("//") == 0) href=href.substring(2);
+               if (href.indexOf(window.location.host) == 0) {
+                       href = href.substring(window.location.host.length);
+               }
+               if (href.indexOf(this.serverurl) != 0) {
+                       document.location.href = orighref; 
+               }
+               var subhref = href.substring(this.serverurl.length);
+               if (subhref == '') subhref = '/';
+               jQuery.historyLoad(subhref);
+       }
+       
+       /**
+        * Enables hash navigation. If a jQuery element or selector
+        * is passed, only anchors in that element are activated
+        * for hash navigation.
+        * 
+        * If an hash is already present in the url, it is immediately
+        * parsed.
+        * 
+        * @param ele The root element (or selector) on which to activate hash 
navigation
+        * @param exclude A list of selectors on which NOT to handle navigation 
+        */
+       this.hashNavigation = function(ele, exclude) {
+               var me = this;
+               if (typeof ele === 'undefined') {
+                       ele = jQuery('body');
+               } else {
+                       ele = jQuery(ele);
+               }
+               if (exclude) {
+                       for (var i = 0; i < exclude.length; i++) {
+                               me.addHashExclude(exclude[i]);
+                       }
+               }
+               me.skippingHash = true;
+               jQuery.historyInit(me.loadHash);
+               me.skippingHash = false;
+               // All liniks must go thru hash navigation
+               jQuery('a', ele).live('click', function(event) {
+                       if (me.matchesHashExclude(this)) return true;
+                       try {
+                               me.hashNavigate(event.currentTarget.href);
+                       } catch (e) {
+                               alert(e);
+                       }
+                       return false;
+               });
+               // Clicks on button are catched before raw submit,
+               // cause otherwise there is no cross browser information 
+               // on which button caused the submit
+               jQuery('form input:submit', ele).live('click', function(event) {
+                       if (me.matchesHashExclude(this)) return true;
+                       try {
+                               var button = $(event.currentTarget);
+                               var form = button.closest('form');
+                               var post = form.serializeArray();
+                               post[post.length] = {name: button.attr('name'), 
value: button.attr('value') };                          
+                               me.hashNavigate(form.attr('action'), post);
+                       } catch (e) {
+                               alert(e);
+                       }
+                       return false;
+               });
+               // default form submits
+               jQuery('form', ele).live('submit', function(event) {
+                       if (me.matchesHashExclude(this)) return true;
+                       try {
+                               me.hashNavigate(event.currentTarget.action, 
$(event.currentTarget).serializeArray());
+                       } catch (e) {
+                               alert(e);
+                       }
+                       return false;
+               });
+       }
+       
+       this.addHashExclude = function(exclude) {
+               var me = this;
+               if (!me.hashExclude) me.hashExclude = new Array();
+               me.hashExclude.push(exclude);
+       }
+       
+       this.matchesHashExclude = function (ele) {
+               if (!this.hashExclude) return false;
+               var jqe = $(ele);
+               for (var i = 0; i < this.hashExclude.length; i++) {
+                       if (jqe.is(this.hashExclude[i])) return true;
+               }
+               return false;
+       }
+       
+}
+
+
+
+
+/*
+ * jQuery history plugin
+ * 
+ * sample page: http://www.mikage.to/jquery/jquery_history.html
+ *
+ * Copyright (c) 2006-2009 Taku Sano (Mikage Sawatari)
+ * Licensed under the MIT License:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *
+ * Modified by Lincoln Cooper to add Safari support and only call the callback 
once during initialization
+ * for msie when no initial hash supplied.
+ */
+
+
+jQuery.extend({
+       historyCurrentHash: undefined,
+       historyCallback: undefined,
+       historyIframeSrc: undefined,
+       historyNeedIframe: jQuery.browser.msie && (jQuery.browser.version < 8 
|| document.documentMode < 8),
+       
+       historyInit: function(callback, src){
+               jQuery.historyCallback = callback;
+               if (src) jQuery.historyIframeSrc = src;
+               var current_hash = location.hash.replace(/\?.*$/, '');
+               
+               jQuery.historyCurrentHash = current_hash;
+               if (jQuery.historyNeedIframe) {
+                       // To stop the callback firing twice during 
initilization if no hash present
+                       if (jQuery.historyCurrentHash == '') {
+                               jQuery.historyCurrentHash = '#';
+                       }
+               
+                       // add hidden iframe for IE
+                       jQuery("body").prepend('<iframe id="jQuery_history" 
style="display: none;"'+
+                               ' src="javascript:false;"></iframe>'
+                       );
+                       var ihistory = jQuery("#jQuery_history")[0];
+                       var iframe = ihistory.contentWindow.document;
+                       iframe.open();
+                       iframe.close();
+                       iframe.location.hash = current_hash;
+               }
+               else if (jQuery.browser.safari) {
+                       // etablish back/forward stacks
+                       jQuery.historyBackStack = [];
+                       jQuery.historyBackStack.length = history.length;
+                       jQuery.historyForwardStack = [];
+                       jQuery.lastHistoryLength = history.length;
+                       
+                       jQuery.isFirst = true;
+               }
+               if(current_hash)
+                       jQuery.historyCallback(current_hash.replace(/^#/, ''));
+               setInterval(jQuery.historyCheck, 100);
+       },
+       
+       historyAddHistory: function(hash) {
+               // This makes the looping function do something
+               jQuery.historyBackStack.push(hash);
+               
+               jQuery.historyForwardStack.length = 0; // clear forwardStack 
(true click occured)
+               this.isFirst = true;
+       },
+       
+       historyCheck: function(){
+               if (jQuery.historyNeedIframe) {
+                       // On IE, check for location.hash of iframe
+                       var ihistory = jQuery("#jQuery_history")[0];
+                       var iframe = ihistory.contentDocument || 
ihistory.contentWindow.document;
+                       var current_hash = 
iframe.location.hash.replace(/\?.*$/, '');
+                       if(current_hash != jQuery.historyCurrentHash) {
+                       
+                               location.hash = current_hash;
+                               jQuery.historyCurrentHash = current_hash;
+                               
jQuery.historyCallback(current_hash.replace(/^#/, ''));
+                               
+                       }
+               } else if (jQuery.browser.safari) {
+                       if(jQuery.lastHistoryLength == history.length && 
jQuery.historyBackStack.length > jQuery.lastHistoryLength) {
+                               jQuery.historyBackStack.shift();
+                       }
+                       if (!jQuery.dontCheck) {
+                               var historyDelta = history.length - 
jQuery.historyBackStack.length;
+                               jQuery.lastHistoryLength = history.length;
+                               
+                               if (historyDelta) { // back or forward button 
has been pushed
+                                       jQuery.isFirst = false;
+                                       if (historyDelta < 0) { // back button 
has been pushed
+                                               // move items to forward stack
+                                               for (var i = 0; i < 
Math.abs(historyDelta); i++) 
jQuery.historyForwardStack.unshift(jQuery.historyBackStack.pop());
+                                       } else { // forward button has been 
pushed
+                                               // move items to back stack
+                                               for (var i = 0; i < 
historyDelta; i++) 
jQuery.historyBackStack.push(jQuery.historyForwardStack.shift());
+                                       }
+                                       var cachedHash = 
jQuery.historyBackStack[jQuery.historyBackStack.length - 1];
+                                       if (cachedHash != undefined) {
+                                               jQuery.historyCurrentHash = 
location.hash.replace(/\?.*$/, '');
+                                               
jQuery.historyCallback(cachedHash);
+                                       }
+                               } else if 
(jQuery.historyBackStack[jQuery.historyBackStack.length - 1] == undefined && 
!jQuery.isFirst) {
+                                       // back button has been pushed to 
beginning and URL already pointed to hash (e.g. a bookmark)
+                                       // document.URL doesn't change in Safari
+                                       if (location.hash) {
+                                               var current_hash = 
location.hash;
+                                               
jQuery.historyCallback(location.hash.replace(/^#/, ''));
+                                       } else {
+                                               var current_hash = '';
+                                               jQuery.historyCallback('');
+                                       }
+                                       jQuery.isFirst = true;
+                               }
+                       }
+               } else {
+                       // otherwise, check for location.hash
+                       var current_hash = location.hash.replace(/\?.*$/, '');
+                       if(current_hash != jQuery.historyCurrentHash) {
+                               jQuery.historyCurrentHash = current_hash;
+                               
jQuery.historyCallback(current_hash.replace(/^#/, ''));
+                       }
+               }
+       },
+       historyLoad: function(hash){
+               var newhash;
+               hash = decodeURIComponent(hash.replace(/\?.*$/, ''));
+               
+               if (jQuery.browser.safari) {
+                       newhash = hash;
+               }
+               else {
+                       newhash = '#' + hash;
+                       location.hash = newhash;
+               }
+               jQuery.historyCurrentHash = newhash;
+               
+               if (jQuery.historyNeedIframe) {
+                       var ihistory = jQuery("#jQuery_history")[0];
+                       var iframe = ihistory.contentWindow.document;
+                       iframe.open();
+                       iframe.close();
+                       iframe.location.hash = newhash;
+                       jQuery.lastHistoryLength = history.length;
+                       jQuery.historyCallback(hash);
+               }
+               else if (jQuery.browser.safari) {
+                       jQuery.dontCheck = true;
+                       // Manually keep track of the history values for Safari
+                       this.historyAddHistory(hash);
+                       
+                       // Wait a while before allowing checking so that Safari 
has time to update the "history" object
+                       // correctly (otherwise the check loop would detect a 
false change in hash).
+                       var fn = function() {jQuery.dontCheck = false;};
+                       window.setTimeout(fn, 200);
+                       jQuery.historyCallback(hash);
+                       // N.B. "location.hash=" must be the last line of code 
for Safari as execution stops afterwards.
+                       //      By explicitly using the "location.hash" command 
(instead of using a variable set to "location.hash") the
+                       //      URL in the browser and the "history" object are 
both updated correctly.
+                       location.hash = newhash;
+               }
+               else {
+                 jQuery.historyCallback(hash);
+               }
+       }
+});
+
+
+
+

Added: 
labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/custom.js
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/custom.js?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/custom.js
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/main/resources/org/apache/magma/website/autoajax/custom.js
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,12 @@
+jQuery(document).bind('added', function(event) {
+       $(event.target).hide().fadeIn(200);
+});
+jQuery(document).bind('renewing', function(event) {
+       $(event.target).css({opacity: 0.5});
+});
+jQuery(document).bind('renewed', function(event) {
+       $(event.target).css({opacity: 1});
+});
+jQuery(document).bind('notrenewed', function(event) {
+       $(event.target).css({opacity: 1, backgroundColor: 'gray'});
+});
\ No newline at end of file

Added: 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockLocalElement.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockLocalElement.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockLocalElement.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockLocalElement.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,55 @@
+package org.apache.magma.website.autoajax.data;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.magma.website.Head;
+
+public class MockLocalElement extends LocalElement {
+
+       private String id;
+       private long ts;
+       private List<LocalElement> children = new ArrayList<LocalElement>();
+       
+       public MockLocalElement(MockLocalElement parent, String id, long ts) {
+               this.id = id;
+               this.ts = ts;
+               if (parent != null) parent.addChild(this);
+       }
+       
+       public MockLocalElement(MockLocalElement parent, String id) {
+               this(parent, id, 1000);
+       }
+
+       public void addChild(MockLocalElement ele) {
+               this.children.add(ele);
+       }
+
+       @Override
+       public List<LocalElement> getChildren() {
+               return this.children;
+       }
+
+       @Override
+       public long getTimestamp() {
+               return ts;
+       }
+
+       @Override
+       public String getId() {
+               return id;
+       }
+
+       @Override
+       public void renderBody(OutputStream out) throws IOException {
+               // TODO Auto-generated method stub
+       }
+
+       @Override
+       public void renderHead(Head head) {
+               // TODO Auto-generated method stub
+       }
+
+}

Added: 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockStateElement.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockStateElement.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockStateElement.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/MockStateElement.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,21 @@
+package org.apache.magma.website.autoajax.data;
+
+public class MockStateElement extends StateElement {
+
+       public MockStateElement(MockStateElement parent, String id) {
+               this(parent, id, 1200);
+       }
+       
+       public MockStateElement(MockStateElement parent, String id, long 
timestamp) {
+               this.id = id;
+               this.timestamp = timestamp;
+               if (parent != null) {
+                       parent.addChild(this);
+               }
+       }
+
+       private void addChild(MockStateElement ele) {
+               this.children.add(ele);
+       }
+       
+}

Added: 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestDiffer.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestDiffer.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestDiffer.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestDiffer.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,224 @@
+package org.apache.magma.website.autoajax.data;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.junit.Test;
+
+
+public class TestDiffer {
+
+       private MockStateElement mockState() {
+               MockStateElement root = new MockStateElement(null, "tpl1");
+               MockStateElement zoneLeft = new MockStateElement(root, 
"zoneLeft");
+               MockStateElement menu1 = new MockStateElement(zoneLeft, 
"menu1");
+               MockStateElement menu2 = new MockStateElement(zoneLeft, 
"menu2");
+               MockStateElement subpart = new MockStateElement(menu2, 
"subpart");
+               MockStateElement menu3 = new MockStateElement(zoneLeft, 
"menu3");
+               MockStateElement zoneMain = new MockStateElement(root, 
"zoneMain");
+               MockStateElement acmain = new MockStateElement(zoneMain, 
"acmain");
+               return root;
+       }
+       
+       @Test
+       public void nodiffs() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeft");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1");
+               MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2");
+               MockLocalElement subpart = new MockLocalElement(menu2, 
"subpart");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMain");
+               MockLocalElement acmain = new MockLocalElement(zoneMain, 
"acmain");
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+               
+               assertEquals(0, changes.size());
+       }
+       
+       @Test
+       public void removals() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeft");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1");
+               //MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2");
+               //MockLocalElement subpart = new MockLocalElement(menu2, 
"subpart");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMain");
+               //MockLocalElement acmain = new MockLocalElement(zoneMain, 
"acmain");
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+               
+               assertEquals(2, changes.size());
+               assertEquals("Remove menu2", changes.get(0).toString());
+               assertEquals("Remove acmain", changes.get(1).toString());
+       }
+       
+       @Test
+       public void additions() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeft");
+               MockLocalElement menu0 = new MockLocalElement(zoneLeft, 
"menu0");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1");
+               MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2");
+               MockLocalElement subpart = new MockLocalElement(menu2, 
"subpart");
+               MockLocalElement menu2b = new MockLocalElement(zoneLeft, 
"menu2b");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMain");
+               MockLocalElement acmain2 = new MockLocalElement(zoneMain, 
"acmain2");
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+               
+               assertEquals(4, changes.size());
+               int ci = 0;
+               assertEquals("Add menu0 before menu1", 
changes.get(ci++).toString());
+               assertEquals("Add menu2b after menu2", 
changes.get(ci++).toString());
+               assertEquals("Remove acmain", changes.get(ci++).toString());
+               assertEquals("Add acmain2 inside zoneMain", 
changes.get(ci++).toString());
+       }
+       
+       @Test
+       public void renewal() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeft");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1");
+               MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2");
+               MockLocalElement subpart = new MockLocalElement(menu2, 
"subpart");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMain");
+               MockLocalElement acmain = new MockLocalElement(zoneMain, 
"acmain", 1500);
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+
+               assertEquals(1, changes.size());
+               int ci = 0;
+               assertEquals("Renew acmain", changes.get(ci++).toString());
+               
+       }
+       
+       @Test
+       public void fullchange() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1b");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeftb");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1b");
+               MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2b");
+               MockLocalElement subpart = new MockLocalElement(menu2, 
"subpartb");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3b");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMainb");
+               MockLocalElement acmain = new MockLocalElement(zoneMain, 
"acmainb");
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+
+               assertEquals(2, changes.size());
+               int ci = 0;
+               assertEquals("Remove tpl1", changes.get(ci++).toString());
+               assertEquals("Add tpl1b inside  none", 
changes.get(ci++).toString());
+               
+       }
+
+       @Test
+       public void fullinternalchange() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeftb");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1b");
+               MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2b");
+               MockLocalElement subpart = new MockLocalElement(menu2, 
"subpartb");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3b");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMainb");
+               MockLocalElement acmain = new MockLocalElement(zoneMain, 
"acmainb");
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+
+               assertEquals(4, changes.size());
+               int ci = 0;
+               assertEquals("Remove zoneLeft", changes.get(ci++).toString());
+               assertEquals("Add zoneLeftb before zoneMain", 
changes.get(ci++).toString());
+               assertEquals("Remove zoneMain", changes.get(ci++).toString());
+               assertEquals("Add zoneMainb after zoneLeftb", 
changes.get(ci++).toString());
+               
+       }
+
+       @Test
+       public void richinternalchange() throws Exception {
+               MockLocalElement root = new MockLocalElement(null, "tpl1");
+               MockLocalElement zoneLeft = new MockLocalElement(root, 
"zoneLeft");
+               MockLocalElement menu1 = new MockLocalElement(zoneLeft, 
"menu1b");
+               MockLocalElement menu2 = new MockLocalElement(zoneLeft, 
"menu2b");
+               MockLocalElement subpart = new MockLocalElement(menu2, 
"subpart");
+               MockLocalElement menu3 = new MockLocalElement(zoneLeft, 
"menu3b");
+               MockLocalElement zoneMain = new MockLocalElement(root, 
"zoneMain");
+               MockLocalElement acmain = new MockLocalElement(zoneMain, 
"acmainb");
+               
+               Differ df = new Differ();
+               df.setLocalRoot(root);
+               df.setRemoteRoot(mockState());
+               
+               df.compute();
+               List<Change> changes = df.changes;
+               for (Change change : changes) {
+                       System.out.println(change);
+               }
+
+               assertEquals(8, changes.size());
+               int ci = 0;
+               assertEquals("Remove menu1", changes.get(ci++).toString());
+               assertEquals("Add menu1b before menu2", 
changes.get(ci++).toString());
+               assertEquals("Remove menu2", changes.get(ci++).toString());
+               assertEquals("Add menu2b after menu1b", 
changes.get(ci++).toString());
+               assertEquals("Remove menu3", changes.get(ci++).toString());
+               assertEquals("Add menu3b after menu2b", 
changes.get(ci++).toString());
+               assertEquals("Remove acmain", changes.get(ci++).toString());
+               assertEquals("Add acmainb inside zoneMain", 
changes.get(ci++).toString());
+               
+       }
+       
+}

Added: 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestListDiff.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestListDiff.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestListDiff.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/data/TestListDiff.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,136 @@
+package org.apache.magma.website.autoajax.data;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+
+public class TestListDiff {
+
+       class ParaString {
+               private String val;
+               public ParaString(String val) { this.val = val; }
+               public boolean equals(Object oth) {
+                       if (oth instanceof String) {
+                               return val.equals(oth);
+                       } else if (oth instanceof ParaString) {
+                               return ((ParaString)oth).val.equals(val);
+                       } else if (oth instanceof OtherString) {
+                               return ((OtherString)oth).val.equals(val);
+                       } else return false;
+               }
+               @Override
+               public String toString() {
+                       return this.val;
+               }
+               public int hashCode() {
+                       return val.hashCode();
+               };
+       }
+       
+       class OtherString {
+               private String val;
+               public OtherString(String val) { this.val = val; }
+               public boolean equals(Object oth) {
+                       if (oth instanceof String) {
+                               return val.equals(oth);
+                       } else if (oth instanceof ParaString) {
+                               return ((ParaString)oth).val.equals(val);
+                       } else if (oth instanceof OtherString) {
+                               return ((OtherString)oth).val.equals(val);
+                       } else return false;
+               }
+               @Override
+               public String toString() {
+                       return this.val;
+               }
+               public int hashCode() {
+                       return val.hashCode();
+               };
+       }
+
+       private List<ParaString> createParaList(String... elements) {
+               List<ParaString> ret = new ArrayList<ParaString>();
+               for (String ele : elements) {
+                       ret.add(new ParaString(ele));
+               }
+               return ret;
+       }
+       private List<OtherString> createOtherList(String... elements) {
+               List<OtherString> ret = new ArrayList<OtherString>();
+               for (String ele : elements) {
+                       ret.add(new OtherString(ele));
+               }
+               return ret;
+       }
+       
+       @Test
+       public void differentTypes() throws Exception {
+               final List<String> events = new ArrayList<String>();
+               
+               final List<OtherString> present = 
createOtherList("a","b","c","d","e","f","g");
+               final List<ParaString> modified = 
createParaList("0","a","c","b","d","f");
+               
+               
+               ListDiff<OtherString, ParaString> df = new 
ListDiff<OtherString, ParaString>(present, modified) {
+                       @Override
+                       protected void removed(OtherString element) {
+                               events.add("Removed " + element);
+                       }
+                       @Override
+                       protected void addedAfter(ParaString element, 
ParaString reference) {
+                               events.add("Added " + element + " after " + 
reference);
+                       }
+                       @Override
+                       protected void addedBefore(ParaString element, 
ParaString reference) {
+                               events.add("Added " + element + " before " + 
reference);
+                       }
+                       @Override
+                       protected void matched(OtherString elea, ParaString 
eleb) {
+                               events.add("Still the same " + elea + " == " + 
eleb);
+                       }
+                       @Override
+                       protected void movedAfter(OtherString element, 
OtherString reference) {
+                               events.add("Moved " + element + " after " + 
reference);
+                       }
+                       @Override
+                       protected void movedBefore(OtherString element, 
OtherString reference) {
+                               events.add("Moved " + element + " before " + 
reference);
+                       }
+                       @Override
+                       protected void added(ParaString element) {
+                               events.add("Added " + element + " without any 
reference");
+                       }
+                       @Override
+                       protected void addedAfterPresent(ParaString element, 
OtherString reference) {
+                               events.add("Added " + element + " after present 
" + reference);
+                               
+                       }
+                       @Override
+                       protected void addedBeforePresent(ParaString element, 
OtherString reference) {
+                               events.add("Added " + element + " before 
present " + reference);                                
+                       }
+               };
+               
+               df.traverseSequences();
+
+               for (String evt : events) {
+                       System.out.println(evt);
+               }
+               
+               assertEquals(8, events.size());
+               int ei = 0;
+               assertEquals("Added 0 before a", events.get(ei++));
+               assertEquals("Still the same a == a", events.get(ei++));
+               assertEquals("Still the same c == c", events.get(ei++));
+               assertEquals("Moved b after c", events.get(ei++));
+               assertEquals("Still the same d == d", events.get(ei++));
+               assertEquals("Removed e", events.get(ei++));
+               assertEquals("Still the same f == f", events.get(ei++));
+               assertEquals("Removed g", events.get(ei++));
+       }
+}

Added: 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/webdata/TestBrowserStateElement.java
URL: 
http://svn.apache.org/viewvc/labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/webdata/TestBrowserStateElement.java?rev=964302&view=auto
==============================================================================
--- 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/webdata/TestBrowserStateElement.java
 (added)
+++ 
labs/magma/trunk/website-autoajax/src/test/java/org/apache/magma/website/autoajax/webdata/TestBrowserStateElement.java
 Thu Jul 15 04:57:08 2010
@@ -0,0 +1,21 @@
+package org.apache.magma.website.autoajax.webdata;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+
+public class TestBrowserStateElement {
+
+       @Test
+       public void parsing() throws Exception {
+               String samplexml = "<l0 id='8836eb75' ty='tpl' 
ha='site.web.template.BaseTemplate' ti='127d335bb29' ><l1 id='dfb01631' 
ty='zone' ha='Banner' ti='127d335bb29' ></l1><l1 id='5061a07e' ty='zone' 
ha='HomeLink' ti='127d335bb29' ></l1><l1 id='69aa3464' ty='zone' ha='Menu' 
ti='127d335bb29' ><l2 id='c8462a5a' ty='zoneprod' ur='/site/site/menu' 
ti='127d335bb29' ><l3 id='3fcc2add' ty='int' ur='/site/site/menu' 
ti='127d335bb29' ></l3></l2></l1><l1 id='3b77a2e0' ty='zone' ha='Languages' 
ti='127d335bb29' ><l2 id='508a2087' ty='zoneprod' ur='/site/locales/showFlags' 
ti='127d335bb29' ><l3 id='c810210a' ty='int' ur='/site/locales/showFlags' 
ti='127d335bb29' ></l3></l2></l1><l1 id='69aa24be' ty='zone' ha='Main' 
ti='127d335bb29' ><l2 id='6600e275' ty='zoneprod' 
ur='/site/content/showLast!homeit' ti='127d335bb29' ><l3 id='dd86e2f8' ty='int' 
ur='/site/content/showLast!homeit' ti='127d335bb29' ></l3></l2></l1><l1 
id='6e3a7ce1' ty='zone' ha='Right' ti='127d335bb29' ><l2 id='4440a5ef' 
ty='zonepr
 od' ur='/site/tech/ultimiArrivi' ti='127d335bb29' ><l3 id='bbc6a672' ty='int' 
ur='/site/tech/ultimiArrivi' ti='127d335bb29' ></l3></l2><l2 id='9075fcb6' 
ty='zoneprod' ur='/site/content/lastBox!5!news' ti='127d335bb29' ><l3 
id='7fbfd39' ty='int' ur='/site/content/lastBox!5!news' ti='127d335bb29' 
></l3></l2></l1><l1 id='e7494ca0' ty='zone' ha='Footer' ti='127d335bb29' ><l2 
id='fa58cf72' ty='zoneprod' ur='/site/site/ultimoAggiornamento' 
ti='127d335bb29' ><l3 id='71decff5' ty='int' 
ur='/site/site/ultimoAggiornamento' ti='127d335bb29' ></l3></l2></l1><l1 
id='f79d854e' ty='zone' ha='ExtraContent' ti='127d335bb29' ></l1></l0>";
+               BrowserStateElement root = new BrowserStateElement(samplexml);
+               assertEquals("8836eb75", root.getId());
+               assertEquals("site.web.template.BaseTemplate", root.getRefId());
+               assertEquals("tpl", root.getType());
+               assertEquals(1270558866217l, root.getTimestamp());
+               assertEquals(8, root.getChildren().size());
+       }
+       
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to