Commits

Stephen McKamey  committed 3dbde9f

$init/$load callback unit tests and fixes

  • Participants
  • Parent commits 7f61793

Comments (0)

Files changed (8)

 duel-js/target
 duel-maven-plugin/target
 target
+TODO.txt

File duel-compiler/src/main/java/org/duelengine/duel/parsing/CharUtility.java

 			(ch >= '\u0300' && ch <= '\u036F') ||
 			(ch >= '\u203F' && ch <= '\u2040');
 	}
+
+	/**
+	 * Checks for HTML attribute name char
+	 */
+	public static boolean isAttrNameChar(int ch) {
+		// http://www.w3.org/TR/html5/syntax.html#attributes-0
+		switch (ch) {
+			case '\0':
+			case '"':
+			case '\'':
+			case '>':
+			case '/':
+			case '=':
+				return false;
+			default:
+				return !isWhiteSpace(ch) && !isUnsafe(ch);
+		}
+	}
+
+	/**
+	 * Checks for Control chars and permanently undefined Unicode chars
+	 */
+	private static boolean isUnsafe(int ch) {
+		// http://www.w3.org/TR/xml/#char32
+		return
+			(ch >= '\u007F' && ch <= '\u0084') ||
+			(ch >= '\u0086' && ch <= '\u009F') ||
+			(ch >= '\uFDD0' && ch <= '\uFDEF');
+	}
 }

File duel-compiler/src/main/java/org/duelengine/duel/parsing/DuelLexer.java

 		this.setMark(CAPACITY+1);
 		this.buffer.setLength(0);
 
-		if (CharUtility.isNameStartChar(this.ch)) {
+		// consume attribute name
+		if (CharUtility.isAttrNameChar(this.ch)) {
 			this.buffer.append((char)this.ch);
 
-			// consume tag name
-			while (CharUtility.isNameChar(this.nextChar()) && this.buffer.length() < CAPACITY) {
+			while (CharUtility.isAttrNameChar(this.nextChar()) && this.buffer.length() < CAPACITY) {
 				this.buffer.append((char)this.ch);
 			}
 		}
 
 		if (this.buffer.length() == 0) {
-			// not a valid tag name
+			// not a valid attribute name
 			this.resetMark();
 			return false;
 		}

File duel-compiler/src/test/java/org/duelengine/duel/parsing/DuelLexerTests.java

 	}
 
 	@Test
+	public void attrCallbackTest() {
+
+		String input = "<div $init=\"alert('$init');return false;\"></div>";
+
+		Object[] expected = {
+				DuelToken.elemBegin("div"),
+				DuelToken.attrName("$init"),
+				DuelToken.attrValue("alert('$init');return false;"),
+				DuelToken.elemEnd("div")
+			};
+
+		Object[] actual = new DuelLexer(input).toList().toArray();
+
+		assertArrayEquals(expected, actual);
+	}
+
+	@Test
+	public void attrPrefixCallbackTest() {
+
+		String input = "<div duel:oninit=\"alert('$init');return false;\"></div>";
+
+		Object[] expected = {
+				DuelToken.elemBegin("div"),
+				DuelToken.attrName("duel:oninit"),
+				DuelToken.attrValue("alert('$init');return false;"),
+				DuelToken.elemEnd("div")
+			};
+
+		Object[] actual = new DuelLexer(input).toList().toArray();
+
+		assertArrayEquals(expected, actual);
+	}
+
+	@Test
 	public void codeSimpleTest() {
 
 		String input = "<% code block %>";

File duel-js/src/main/javascript/dom.js

 							addHandler(elem, ATTRDUP[name], value);
 						}
 
-					} else if (type === VAL) {
+					} else if (type === VAL && name.charAt(0) !== '$') {
 						elem.setAttribute(name, value);
 	
 						// also set duplicated attributes
 	 * @private
 	 * @param {Node} elem The element
 	 */
-	function onInit(elem) {
+	function callbacks(elem) {
 		if (!elem) {
 			return;
 		}
 						trimWhitespace(child);
 
 						// trigger callbacks
-						onInit(child);
+						callbacks(child);
 
 						// unwrap HTML root, to simplify insertion
 						return child;
 		trimWhitespace(elem);
 
 		// trigger callbacks
-		onInit(elem);
+		callbacks(elem);
 
 		// eliminate wrapper for single nodes
 		if (elem.nodeType === 11 && elem.childNodes.length === 1) {

File duel-js/src/test/javascript/domTests.js

 	same(toHTML(actual), toHTML(expected), '');
 });
 
+test('$init function callback', function() {
+
+	var callbackOninitResult;
+	
+	function callback() {
+		callbackOninitResult = toHTML(this);
+	}
+
+	var view = duel(
+		['div', { '$init' : function() { return callback; } },
+			['p',
+			 	'Lorem ipsum dolor sit amet']
+		]);
+
+	// initialize results
+	callbackOninitResult = null;
+
+	var actual = view().toDOM();
+
+	ok(!!callbackOninitResult, "$init was called");
+
+	var expected = document.createElement('div');
+
+	var temp = document.createElement('p');
+	temp.appendChild(document.createTextNode('Lorem ipsum dolor sit amet'));
+
+	expected.appendChild(temp);
+
+	same(callbackOninitResult, toHTML(expected), '');
+});
+
+test('$init string callback', function() {
+
+	var view = duel(
+		['div', { '$init' : 'document.callbackOninitResult = toHTML(this);' },
+			['p',
+			 	'Lorem ipsum dolor sit amet']
+		]);
+
+	// initialize results
+	document.callbackOninitResult = null;
+
+	var actual = view().toDOM();
+
+	ok(!!document.callbackOninitResult, "$init was called");
+
+	var expected = document.createElement('div');
+
+	var temp = document.createElement('p');
+	temp.appendChild(document.createTextNode('Lorem ipsum dolor sit amet'));
+
+	expected.appendChild(temp);
+
+	same(document.callbackOninitResult, toHTML(expected), '');
+});
+
+asyncTest('$load function callback', function() {
+
+	var callbackOnloadResult;
+
+	function callback() {
+		callbackOnloadResult = toHTML(this);
+	}
+
+	var view = duel(
+		['div', { '$load' : function() { return callback; } },
+			['p',
+			 	'Lorem ipsum dolor sit amet']
+		]);
+
+	// initialize results
+	callbackOnloadResult = null;
+
+	var actual = view().toDOM();
+
+	setTimeout(function() {
+		start();
+
+		ok(callbackOnloadResult, "$load was called");
+
+		var expected = document.createElement('div');
+	
+		var temp = document.createElement('p');
+		temp.appendChild(document.createTextNode('Lorem ipsum dolor sit amet'));
+	
+		expected.appendChild(temp);
+
+		same(callbackOnloadResult, toHTML(expected), '');
+	}, 100);
+});
+
+asyncTest('$load string callback', function() {
+
+	var view = duel(
+		['div', { '$load' : 'document.callbackOnloadResult = toHTML(this);' },
+			['p',
+			 	'Lorem ipsum dolor sit amet']
+		]);
+
+	// initialize results
+	document.callbackOnloadResult = null;
+
+	var actual = view().toDOM();
+
+	setTimeout(function() {
+		start();
+
+		ok(document.callbackOnloadResult, "$load was called");
+
+		var expected = document.createElement('div');
+	
+		var temp = document.createElement('p');
+		temp.appendChild(document.createTextNode('Lorem ipsum dolor sit amet'));
+	
+		expected.appendChild(temp);
+	
+		same(document.callbackOnloadResult, toHTML(expected), '');
+	}, 100);
+});
+
 }catch(ex){alert(ex);}

File duel-js/target/duel.js

 							addHandler(elem, ATTRDUP[name], value);
 						}
 
-					} else if (type === VAL) {
+					} else if (type === VAL && name.charAt(0) !== '$') {
 						elem.setAttribute(name, value);
 	
 						// also set duplicated attributes
 	 * @private
 	 * @param {Node} elem The element
 	 */
-	function onInit(elem) {
+	function callbacks(elem) {
 		if (!elem) {
 			return;
 		}
 						trimWhitespace(child);
 
 						// trigger callbacks
-						onInit(child);
+						callbacks(child);
 
 						// unwrap HTML root, to simplify insertion
 						return child;
 		trimWhitespace(elem);
 
 		// trigger callbacks
-		onInit(elem);
+		callbacks(elem);
 
 		// eliminate wrapper for single nodes
 		if (elem.nodeType === 11 && elem.childNodes.length === 1) {

File duel-js/target/duel.min.js

 c);f=b[d];if(j(f)===3){for(var g in f)if(f.hasOwnProperty(g)){a.append(" ",g);var i=f[g];j(i)!==0&&a.append('="',N(i),'"')}d++}h&&a.append(" /");a.append(">")}for(;d<e;d++)f=b[d],x(f)?D(a,f):a.append(M(f));c&&!h&&a.append("</",c,">")}}function E(a){try{var b=new n;D(b,a);return b.toString()}catch(c){return"["+c+"]"}}function r(a){if(a){if(a.charAt(0)==="!")return l.createComment(a==="!"?"":a.substr(1)+" ")}else{if(l.createDocumentFragment)return l.createDocumentFragment();a=""}return a.toLowerCase()===
 "style"&&l.createStyleSheet?l.createStyleSheet():l.createElement(a)}function v(a,b){if(b){var c=(a.tagName||"").toLowerCase();if(a.nodeType===8)b.nodeType===3&&(a.nodeValue+=b.nodeValue);else if(c==="table"&&a.tBodies)if(b.tagName)if((c=b.tagName.toLowerCase())&&c!=="tbody"&&c!=="thead"){var e=a.tBodies.length>0?a.tBodies[a.tBodies.length-1]:null;e||(e=r(c==="th"?"thead":"tbody"),a.appendChild(e));e.appendChild(b)}else a.canHaveChildren!==false&&a.appendChild(b);else{if(b.nodeType===11)for(;b.firstChild;)v(a,
 b.removeChild(b.firstChild))}else if(c==="style"&&l.createStyleSheet)a.cssText=b;else if(a.canHaveChildren!==false)a.appendChild(b);else if(c==="object"&&b.tagName&&b.tagName.toLowerCase()==="param"){try{a.appendChild(b)}catch(d){}try{if(a.object)a.object[b.name]=b.value}catch(f){}}}}function F(a,b,c){p(c)?a.addEventListener?a.addEventListener(b.substr(0,2)==="on"?b.substr(2):b,c,false):a[b]=c:typeof c==="string"&&(a[b]=new Function("event",c))}function G(a,b){if(b.name&&l.attachEvent&&!a.parentNode)try{var c=
-r("<"+a.tagName+' name="'+b.name+'">');a.tagName===c.tagName&&(a=c)}catch(e){}for(var d in b)if(b.hasOwnProperty(d)){var c=b[d],f=j(c);if(d)f===0&&(c="",f=4),d=P[d.toLowerCase()]||d,d==="style"?typeof a.style.cssText!=="undefined"?a.style.cssText=c:a.style=c:d==="class"?a.className=c:d.substr(0,2)==="on"?(F(a,d,c),u[d]&&F(a,u[d],c)):f===4?(a.setAttribute(d,c),u[d]&&a.setAttribute(u[d],c)):(a[d]=c,u[d]&&(a[u[d]]=c))}return a}function H(a){return!!a&&a.nodeType===3&&(!a.nodeValue||!/\S/.exec(a.nodeValue))}
-function I(a,b){if(a&&a.nodeType===3&&b.exec(a.nodeValue))a.nodeValue=a.nodeValue.replace(b,"")}function z(a){if(a){for(;H(a.firstChild);)a.removeChild(a.firstChild);for(I(a.firstChild,Q);H(a.lastChild);)a.removeChild(a.lastChild);I(a.lastChild,R)}}function J(a,b){var c=a[b];if(c){try{delete a[b]}catch(e){a[b]=void 0}if(!p(c))try{c=new Function(""+c)}catch(d){c=null}}return c}function K(a){if(a){var b=J(a,"$init");b&&b.call(a);(b=J(a,"$load"))?setTimeout(function(){b.call(a);b=a=null},0):b=a=null}}
-function A(a,b){for(var c=1,e=b.length;c<e;c++){var d=b[c];switch(j(d)){case 2:var f=d[0],d=A(r(f),d);if(f==="html")return z(d),K(d),d;v(a,d);break;case 4:d!==""&&v(a,l.createTextNode(""+d));break;case 3:a.nodeType===1&&(a=G(a,d));break;case 5:var f=v,h=a;var g=d,d=r("div");d.innerHTML=""+g;z(d);if(d.childNodes.length===1)d=d.firstChild;else{for(g=r("");d.firstChild;)g.appendChild(d.firstChild);d=g}f(h,d)}}z(a);K(a);if(a.nodeType===11&&a.childNodes.length===1)a=a.firstChild;return a}w.prototype.toString=
-function(){return this.value};var x=Array.isArray||function(a){return a instanceof Array};n.FAST=!B.ScriptEngineMajorVersion;n.prototype.append=function(a,b,c){n.FAST?(this.value+=a,b!==null&&b!==void 0&&(this.value+=b,c!==null&&c!==void 0&&(this.value+=c))):this.value.push.apply(this.value,arguments)};n.prototype.clear=function(){this.value=n.FAST?"":[]};n.prototype.toString=function(){return n.FAST?this.value:this.value.join("")};var m;m=function(a,b,c,e,d,f){switch(j(a)){case 1:return y(a(b,c,
-e,d));case 2:var h=a[0]||"";switch(h){case "$for":a:{var g=a[1]||{},h=[""],i;if(g.hasOwnProperty("count")){i=g.count;p(i)&&(i=i(b,c,e,d));g.hasOwnProperty("data")?(g=g.data,p(g)&&(g=g(b,c,e,d))):g=b;for(b=0;b<i;b++)s(h,t(a,g,b,i,null,f))}else{if(g.hasOwnProperty("in")){var k=g["in"];p(k)&&(k=k(b,c,e,d));if(j(k)===3){g=[];for(i in k)k.hasOwnProperty(i)&&g.push(i);for(b=0,i=g.length;b<i;b++)s(h,t(a,k[g[b]],b,i,g[b],f));a=h;break a}g=k}else g=g.each,p(g)&&(g=g(b,c,e,d));b=j(g);if(b===2)for(b=0,i=g.length;b<
+r("<"+a.tagName+' name="'+b.name+'">');a.tagName===c.tagName&&(a=c)}catch(e){}for(var d in b)if(b.hasOwnProperty(d)){var c=b[d],f=j(c);if(d)f===0&&(c="",f=4),d=P[d.toLowerCase()]||d,d==="style"?typeof a.style.cssText!=="undefined"?a.style.cssText=c:a.style=c:d==="class"?a.className=c:d.substr(0,2)==="on"?(F(a,d,c),u[d]&&F(a,u[d],c)):f===4&&d.charAt(0)!=="$"?(a.setAttribute(d,c),u[d]&&a.setAttribute(u[d],c)):(a[d]=c,u[d]&&(a[u[d]]=c))}return a}function H(a){return!!a&&a.nodeType===3&&(!a.nodeValue||
+!/\S/.exec(a.nodeValue))}function I(a,b){if(a&&a.nodeType===3&&b.exec(a.nodeValue))a.nodeValue=a.nodeValue.replace(b,"")}function z(a){if(a){for(;H(a.firstChild);)a.removeChild(a.firstChild);for(I(a.firstChild,Q);H(a.lastChild);)a.removeChild(a.lastChild);I(a.lastChild,R)}}function J(a,b){var c=a[b];if(c){try{delete a[b]}catch(e){a[b]=void 0}if(!p(c))try{c=new Function(""+c)}catch(d){c=null}}return c}function K(a){if(a){var b=J(a,"$init");b&&b.call(a);(b=J(a,"$load"))?setTimeout(function(){b.call(a);
+b=a=null},0):b=a=null}}function A(a,b){for(var c=1,e=b.length;c<e;c++){var d=b[c];switch(j(d)){case 2:var f=d[0],d=A(r(f),d);if(f==="html")return z(d),K(d),d;v(a,d);break;case 4:d!==""&&v(a,l.createTextNode(""+d));break;case 3:a.nodeType===1&&(a=G(a,d));break;case 5:var f=v,h=a;var g=d,d=r("div");d.innerHTML=""+g;z(d);if(d.childNodes.length===1)d=d.firstChild;else{for(g=r("");d.firstChild;)g.appendChild(d.firstChild);d=g}f(h,d)}}z(a);K(a);if(a.nodeType===11&&a.childNodes.length===1)a=a.firstChild;
+return a}w.prototype.toString=function(){return this.value};var x=Array.isArray||function(a){return a instanceof Array};n.FAST=!B.ScriptEngineMajorVersion;n.prototype.append=function(a,b,c){n.FAST?(this.value+=a,b!==null&&b!==void 0&&(this.value+=b,c!==null&&c!==void 0&&(this.value+=c))):this.value.push.apply(this.value,arguments)};n.prototype.clear=function(){this.value=n.FAST?"":[]};n.prototype.toString=function(){return n.FAST?this.value:this.value.join("")};var m;m=function(a,b,c,e,d,f){switch(j(a)){case 1:return y(a(b,
+c,e,d));case 2:var h=a[0]||"";switch(h){case "$for":a:{var g=a[1]||{},h=[""],i;if(g.hasOwnProperty("count")){i=g.count;p(i)&&(i=i(b,c,e,d));g.hasOwnProperty("data")?(g=g.data,p(g)&&(g=g(b,c,e,d))):g=b;for(b=0;b<i;b++)s(h,t(a,g,b,i,null,f))}else{if(g.hasOwnProperty("in")){var k=g["in"];p(k)&&(k=k(b,c,e,d));if(j(k)===3){g=[];for(i in k)k.hasOwnProperty(i)&&g.push(i);for(b=0,i=g.length;b<i;b++)s(h,t(a,k[g[b]],b,i,g[b],f));a=h;break a}g=k}else g=g.each,p(g)&&(g=g(b,c,e,d));b=j(g);if(b===2)for(b=0,i=g.length;b<
 i;b++)s(h,t(a,g[b],b,i,null,f));else b!==0&&(h=t(a,g,0,1,null,f))}a=h}return a;case "$xor":return C(a,b,c,e,d,f);case "$if":return C(["$xor",a],b,c,e,d,f);case "$call":f=a[1]||{};if(f.view){h=m(f.view,b,c,e,d);g=f.hasOwnProperty("data")?m(f.data,b,c,e,d):b;i=f.hasOwnProperty("index")?m(f.index,b,c,e,d):c;k=f.hasOwnProperty("count")?m(f.count,b,c,e,d):e;b=f.hasOwnProperty("key")?m(f.key,b,c,e,d):d;c={};for(e=a.length-1;e>=2;e--)d=a[e],f=d[1]||{},f.hasOwnProperty("name")&&(c[f.name]=d);a=h&&p(h.getView)?
 m(h.getView(),g,i,k,b,c):null}else a=null;return a;case "$part":return h=(a[1]||{}).name||"",h=f&&f.hasOwnProperty(h)?f[h]:a,t(h,b,c,e,d)}h=[h];g=1;for(i=a.length;g<i;g++)s(h,m(a[g],b,c,e,d,f));return h;case 3:f={};for(h in a)a.hasOwnProperty(h)&&(f[h]=m(a[h],b,c,e,d));return f}return a};var o=B.duel=function(a){return p(a)&&p(a.getView)?a:L(a)};o.raw=o.raw=function(a){return new w(a)};var O={area:true,base:true,basefont:true,br:true,col:true,frame:true,hr:true,img:true,input:true,isindex:true,keygen:true,
 link:true,meta:true,param:true,source:true,wbr:true};q.prototype.toString=function(){return E(this.value)};o.write=o.write=function(a,b,c,e,d){a=o(a).getView();a=m(a,b,c,e,d);l.write(E(a))};var P={rowspan:"rowSpan",colspan:"colSpan",cellpadding:"cellPadding",cellspacing:"cellSpacing",tabindex:"tabIndex",accesskey:"accessKey",hidefocus:"hideFocus",usemap:"useMap",maxlength:"maxLength",readonly:"readOnly",contenteditable:"contentEditable"},u={enctype:"encoding",onscroll:"DOMMouseScroll"},Q=/^[\r\n]+/,