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]
