uritemplate
Advanced tools
Comparing version 0.2.1 to 0.2.2
@@ -1,1 +0,1 @@ | ||
(function(e){"use strict";function t(e){var n,o;if(null===e||void 0===e)return!1;if(r.isArray(e)){for(n=0;e.length>n;n+=1)if(t(e[n]))return!0;return!1}if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return!0;for(o in e)if(e.hasOwnProperty(o)&&t(e[o]))return!0;return!1}var r=function(){function e(e){return"[object Array]"===Object.prototype.toString.apply(e)}function t(e,t,r){var n,o=r;for(n in e)e.hasOwnProperty(n)&&(o=t(o,e[n],n,e));return o}function r(e,t,r){var n,o=r;for(n=0;e.length>n;n+=1)o=t(o,e[n],n,e);return o}function n(n,o,i){return e(n)?r(n,o,i):t(n,o,i)}return{isArray:e,reduce:n}}(),n=function(){function e(e){return e>="a"&&"z">=e||e>="A"&&"Z">=e}function t(e){return e>="0"&&"9">=e}function r(e){return t(e)||e>="a"&&"f">=e||e>="A"&&"F">=e}return{isAlpha:e,isDigit:t,isHexDigit:r}}(),o=function(){function e(e){return unescape(encodeURIComponent(e))}function t(t){var r,n,o="",i=e(t);for(n=0;i.length>n;n+=1)r=i.charCodeAt(n),o+="%"+r.toString(16).toUpperCase();return o}function r(e){if(3>e.length)return!1;for(var t=0;e.length>t;t+=3)if("%"!==e.charAt(t)||!n.isHexDigit(e.charAt(t+1)||!n.isHexDigit(e.charAt(t+2))))return!1;return!0}function o(e,t){var n=e.charAt(t);return"%"!==n?n:(n=e.substr(t,3),r(n)?n:"%")}return{encodeCharacter:t,decodeCharacter:decodeURIComponent,isPctEncoded:r,pctCharAt:o}}(),i=function(){function e(e){return n.isAlpha(e)||n.isDigit(e)||"_"===e||o.isPctEncoded(e)}function t(e){return n.isAlpha(e)||n.isDigit(e)||"-"===e||"."===e||"_"===e||"~"===e}function r(e){return":"===e||"/"===e||"?"===e||"#"===e||"["===e||"]"===e||"@"===e||"!"===e||"$"===e||"&"===e||"("===e||")"===e||"*"===e||"+"===e||","===e||";"===e||"="===e||"'"===e}return{isVarchar:e,isUnreserved:t,isReserved:r}}(),a=function(){function e(e,t){var r,n="",a="";for(("number"==typeof e||"boolean"==typeof e)&&(e=""+e),r=0;e.length>r;r+=a.length)a=o.pctCharAt(e,r),n+=a.length>1?a:i.isUnreserved(a)||t&&i.isReserved(a)?a:o.encodeCharacter(a);return n}function t(t){return e(t,!0)}return{encode:e,encodePassReserved:t}}(),u=function(){function e(e){t[e]={symbol:e,separator:"?"===e?"&":""===e||"+"===e||"#"===e?",":e,named:";"===e||"&"===e||"?"===e,ifEmpty:"&"===e||"?"===e?"=":"",first:"+"===e?"":e,encode:"+"===e||"#"===e?a.encodePassReserved:a.encode,toString:function(){return this.symbol}}}var t={};return e(""),e("+"),e("#"),e("."),e("/"),e(";"),e("?"),e("&"),{valueOf:function(e){if(t[e])return t[e];if("=,!@|".indexOf(e)>=0)throw Error('Illegal use of reserved operator "'+e+'"');return t[""]}}}(),s=function(){function e(e){var t,r="",n="";for(t=0;e.length>t;t+=n.length)n=o.pctCharAt(e,t),r+=n.length>0?n:i.isReserved(n)||i.isUnreserved(n)?n:o.encodeCharacter(n);return r}function t(e){this.literal=t.encodeLiteral(e)}return t.encodeLiteral=e,t.prototype.expand=function(){return this.literal},t.prototype.toString=t.prototype.expand,t}(),f=function(){function e(e){function t(){h={varname:a.substring(d,f),exploded:!1,maxLength:null},d=null}function r(){if(g===f)throw Error("after a ':' you have to specify the length. position = "+f);h.maxLength=parseInt(a.substring(g,f),10),g=null}var a,s,f,l,p=[],h=null,d=null,g=null;for(a=e.substr(1,e.length-2),s=u.valueOf(a.charAt(0)),f=""===s.symbol?0:1,d=f;a.length>f;f+=l.length){if(l=o.pctCharAt(a,f),null!==d){if("."===l){if(d===f)throw Error("a varname MUST NOT start with a dot -- see position "+f);continue}if(i.isVarchar(l))continue;t()}if(null!==g){if(n.isDigit(l))continue;r()}if(":"!==l)if("*"!==l){if(","!==l)throw Error("illegal character '"+l+"' at position "+f);p.push(h),h=null,d=f+1}else{if(null===h)throw Error("explode exploded at position "+f);if(h.exploded)throw Error("explode exploded twice at position "+f);if(h.maxLength)throw Error("an explode (*) MUST NOT follow to a prefix, see position "+f);h.exploded=!0}else{if(null!==h.maxLength)throw Error("only one :maxLength is allowed per varspec at position "+f);g=f+1}}return null!==d&&t(),null!==g&&r(),p.push(h),new c(e,s,p)}function t(t){var r,n,o=[],i=null,a=0;for(r=0;t.length>r;r+=1)if(n=t.charAt(r),null===a){if(null===i)throw Error("reached unreachable code");if("{"===n)throw Error("brace was opened in position "+i+" and cannot be reopened in position "+r);if("}"===n){if(i+1===r)throw Error("empty braces on position "+i);o.push(e(t.substring(i,r+1))),i=null,a=r+1}}else{if("}"===n)throw Error("brace was closed in position "+r+" but never opened");"{"===n&&(r>a&&o.push(new s(t.substring(a,r))),a=null,i=r)}if(null!==i)throw Error("brace was opened on position "+i+", but never closed");return t.length>a&&o.push(new s(t.substr(a))),new l(t,o)}return t}(),c=function(){function e(e){return JSON?JSON.stringify(e):e}function n(e,t,r){this.templateText=e,this.operator=t,this.varspecs=r}return n.prototype.toString=function(){return this.templateText},n.prototype.expand=function(n){function o(e,r,n){return t(r)&&(e.length>0&&(e+=","),l||(e+=d.encode(n)+","),e+=d.encode(r)),e}function i(e,r,n){return t(r)&&(e.length>0&&(e+=d.separator),e+=l?s.encodeLiteral(f.varname):d.encode(n),e+="="+d.encode(r)),e}function a(e,r,n){return t(r)&&(e.length>0&&(e+=d.separator),l||(e+=d.encode(n)+"="),e+=d.encode(r)),e}var u,f,c,l,p="",h=!0,d=this.operator;for(u=0;this.varspecs.length>u;u+=1)if(f=this.varspecs[u],c=n[f.varname],h?(p+=this.operator.first,h=!1):p+=this.operator.separator,l=r.isArray(c),"string"==typeof c||"number"==typeof c||"boolean"==typeof c){if(c=""+c,this.operator.named){if(p+=s.encodeLiteral(f.varname),""===c){p+=this.operator.ifEmpty;continue}p+="="}f.maxLength&&c.length>f.maxLength&&(c=c.substr(0,f.maxLength)),p+=this.operator.encode(c)}else{if(f.maxLength)throw Error("Prefix modifiers are not applicable to variables that have composite values. You tried to expand "+this+" with "+e(c));if(f.exploded)p+=r.reduce(c,d.named?i:a,"");else{if(d.named){if(p+=s.encodeLiteral(f.varname),!t(c)){p+=this.operator.ifEmpty;continue}p+="="}p+=r.reduce(c,o,"")}}return p},n}(),l=function(){function e(e,t){this.templateText=e,this.expressions=t}return e.prototype.toString=function(){return this.templateText},e.prototype.expand=function(e){var t,r="";for(t=0;this.expressions.length>t;t+=1)r+=this.expressions[t].expand(e);return r},e.parse=f,e}();e(l)})(function(e){"use strict";"undefined"!=typeof module?module.exports=e:"undefined"!=typeof define?define([],function(){return e}):"undefined"!=typeof window?window.UriTemplate=e:global.UriTemplate=e}); | ||
(function(e){"use strict";function r(e){var t,o;if(null===e||void 0===e)return!1;if(n.isArray(e)){for(t=0;e.length>t;t+=1)if(r(e[t]))return!0;return!1}if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return!0;for(o in e)if(e.hasOwnProperty(o)&&r(e[o]))return!0;return!1}var n=function(){function e(e){return"[object Array]"===Object.prototype.toString.apply(e)}function r(e,r,n){var t,o=n;for(t in e)e.hasOwnProperty(t)&&(o=r(o,e[t],t,e));return o}function n(e,r,n){var t,o=n;for(t=0;e.length>t;t+=1)o=r(o,e[t],t,e);return o}function t(t,o,i){return e(t)?n(t,o,i):r(t,o,i)}return{isArray:e,reduce:t}}(),t=function(){function e(e){return e>="a"&&"z">=e||e>="A"&&"Z">=e}function r(e){return e>="0"&&"9">=e}function n(e){return r(e)||e>="a"&&"f">=e||e>="A"&&"F">=e}return{isAlpha:e,isDigit:r,isHexDigit:n}}(),o=function(){function e(e){var r,n,t="",o=u.encode(e);for(n=0;o.length>n;n+=1)r=o.charCodeAt(n),t+="%"+r.toString(16).toUpperCase();return t}function r(e,r){return"%"===e[r]&&t.isHexDigit(e[r+1])&&t.isHexDigit(e[r+2])}function n(e,r){return parseInt(e.substr(r,2),16)}function o(e){if(!r(e,0))return!1;var t=n(e,1),o=u.numBytes(t);if(0===o)return!1;for(var i=1;o>i;i+=1)if(!r(e,3*i)||!u.isValidFollowingCharCode(n(e,3*i+1)))return!1;return!0}function i(e,t){var o=e[t];if(!r(e,t))return o;var i=n(e,t+1),a=u.numBytes(i);if(0===a)return o;for(var s=1;a>s;s+=1)if(!r(e,t+3*s)||!u.isValidFollowingCharCode(n(e,t+3*s+1)))return o;return e.substr(t,3*a)}var u={encode:function(e){return unescape(encodeURIComponent(e))},numBytes:function(e){return 127>=e?1:e>=194&&223>=e?2:e>=224&&239>=e?3:e>=240&&244>=e?4:0},isValidFollowingCharCode:function(e){return e>=128&&191>=e}};return{encodeCharacter:e,isPctEncoded:o,pctCharAt:i}}(),i=function(){function e(e){return t.isAlpha(e)||t.isDigit(e)||"_"===e||o.isPctEncoded(e)}function r(e){return t.isAlpha(e)||t.isDigit(e)||"-"===e||"."===e||"_"===e||"~"===e}function n(e){return":"===e||"/"===e||"?"===e||"#"===e||"["===e||"]"===e||"@"===e||"!"===e||"$"===e||"&"===e||"("===e||")"===e||"*"===e||"+"===e||","===e||";"===e||"="===e||"'"===e}return{isVarchar:e,isUnreserved:r,isReserved:n}}(),u=function(){function e(e,r){var n,t="",u="";for(("number"==typeof e||"boolean"==typeof e)&&(e=""+e),n=0;e.length>n;n+=u.length)u=o.pctCharAt(e,n),t+=u.length>1?u:i.isUnreserved(u)||r&&i.isReserved(u)?u:o.encodeCharacter(u);return t}function r(r){return e(r,!0)}return{encode:e,encodePassReserved:r}}(),a=function(){function e(e){r[e]={symbol:e,separator:"?"===e?"&":""===e||"+"===e||"#"===e?",":e,named:";"===e||"&"===e||"?"===e,ifEmpty:"&"===e||"?"===e?"=":"",first:"+"===e?"":e,encode:"+"===e||"#"===e?u.encodePassReserved:u.encode,toString:function(){return this.symbol}}}var r={};return e(""),e("+"),e("#"),e("."),e("/"),e(";"),e("?"),e("&"),{valueOf:function(e){if(r[e])return r[e];if("=,!@|".indexOf(e)>=0)throw Error('Illegal use of reserved operator "'+e+'"');return r[""]}}}(),s=function(){function e(e){var r,n="",t="";for(r=0;e.length>r;r+=t.length)t=o.pctCharAt(e,r),n+=t.length>0?t:i.isReserved(t)||i.isUnreserved(t)?t:o.encodeCharacter(t);return n}function r(e){this.literal=r.encodeLiteral(e)}return r.encodeLiteral=e,r.prototype.expand=function(){return this.literal},r.prototype.toString=r.prototype.expand,r}(),f=function(){function e(e){function r(){p={varname:u.substring(h,f),exploded:!1,maxLength:null},h=null}function n(){if(d===f)throw Error("after a ':' you have to specify the length. position = "+f);p.maxLength=parseInt(u.substring(d,f),10),d=null}var u,s,f,c=[],p=null,h=null,d=null,g="";for(u=e.substr(1,e.length-2),s=a.valueOf(u.charAt(0)),f=""===s.symbol?0:1,h=f;u.length>f;f+=g.length){if(g=o.pctCharAt(u,f),null!==h){if("."===g){if(h===f)throw Error("a varname MUST NOT start with a dot -- see position "+f);continue}if(i.isVarchar(g))continue;r()}if(null!==d){if(t.isDigit(g))continue;n()}if(":"!==g)if("*"!==g){if(","!==g)throw Error("illegal character '"+g+"' at position "+f+' of "'+u+'"');c.push(p),p=null,h=f+1}else{if(null===p)throw Error("explode exploded at position "+f);if(p.exploded)throw Error("explode exploded twice at position "+f);if(p.maxLength)throw Error("an explode (*) MUST NOT follow to a prefix, see position "+f);p.exploded=!0}else{if(null!==p.maxLength)throw Error("only one :maxLength is allowed per varspec at position "+f);d=f+1}}return null!==h&&r(),null!==d&&n(),c.push(p),new l(e,s,c)}function r(r){var n,t,o=[],i=null,u=0;for(n=0;r.length>n;n+=1)if(t=r.charAt(n),null===u){if(null===i)throw Error("reached unreachable code");if("{"===t)throw Error("brace was opened in position "+i+" and cannot be reopened in position "+n);if("}"===t){if(i+1===n)throw Error("empty braces on position "+i);o.push(e(r.substring(i,n+1))),i=null,u=n+1}}else{if("}"===t)throw Error("brace was closed in position "+n+" but never opened");"{"===t&&(n>u&&o.push(new s(r.substring(u,n))),u=null,i=n)}if(null!==i)throw Error("brace was opened on position "+i+", but never closed");return r.length>u&&o.push(new s(r.substr(u))),new c(r,o)}return r}(),l=function(){function e(e){return JSON?JSON.stringify(e):e}function t(e,r,n){this.templateText=e,this.operator=r,this.varspecs=n}return t.prototype.toString=function(){return this.templateText},t.prototype.expand=function(t){function o(e,n,t){return r(n)&&(e.length>0&&(e+=","),c||(e+=d.encode(t)+","),e+=d.encode(n)),e}function i(e,n,t){return r(n)&&(e.length>0&&(e+=d.separator),e+=c?s.encodeLiteral(f.varname):d.encode(t),e+="="+d.encode(n)),e}function u(e,n,t){return r(n)&&(e.length>0&&(e+=d.separator),c||(e+=d.encode(t)+"="),e+=d.encode(n)),e}var a,f,l,c,p="",h=!0,d=this.operator;for(a=0;this.varspecs.length>a;a+=1)if(f=this.varspecs[a],l=t[f.varname],r(l))if(h?(p+=this.operator.first,h=!1):p+=this.operator.separator,c=n.isArray(l),"string"==typeof l||"number"==typeof l||"boolean"==typeof l){if(l=""+l,this.operator.named){if(p+=s.encodeLiteral(f.varname),""===l){p+=this.operator.ifEmpty;continue}p+="="}f.maxLength&&l.length>f.maxLength&&(l=l.substr(0,f.maxLength)),p+=this.operator.encode(l)}else{if(f.maxLength)throw Error("Prefix modifiers are not applicable to variables that have composite values. You tried to expand "+this+" with "+e(l));if(f.exploded)p+=n.reduce(l,d.named?i:u,"");else{if(d.named){if(p+=s.encodeLiteral(f.varname),!r(l)){p+=this.operator.ifEmpty;continue}p+="="}p+=n.reduce(l,o,"")}}return p},t}(),c=function(){function e(e,r){this.templateText=e,this.expressions=r}return e.prototype.toString=function(){return this.templateText},e.prototype.expand=function(e){var r,n="";for(r=0;this.expressions.length>r;r+=1)n+=this.expressions[r].expand(e);return n},e.parse=f,e}();e(c)})(function(e){"use strict";"undefined"!=typeof module?module.exports=e:"undefined"!=typeof define?define([],function(){return e}):"undefined"!=typeof window?window.UriTemplate=e:global.UriTemplate=e}); |
@@ -76,11 +76,37 @@ /*jshint */ | ||
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html | ||
function toUtf8(s) { | ||
return unescape(encodeURIComponent(s)); | ||
} | ||
var utf8 = { | ||
encode: function (chr) { | ||
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html | ||
return unescape(encodeURIComponent(chr)); | ||
}, | ||
numBytes: function (firstCharCode) { | ||
if (firstCharCode <= 0x7F) { | ||
return 1; | ||
} | ||
else if (0xC2 <= firstCharCode && firstCharCode <= 0xDF) { | ||
return 2; | ||
} | ||
else if (0xE0 <= firstCharCode && firstCharCode <= 0xEF) { | ||
return 3; | ||
} | ||
else if (0xF0 <= firstCharCode && firstCharCode <= 0xF4) { | ||
return 4; | ||
} | ||
// no valid first octet | ||
return 0; | ||
}, | ||
isValidFollowingCharCode: function (charCode) { | ||
return 0x80 <= charCode && charCode <= 0xBF; | ||
} | ||
}; | ||
function encode(chr) { | ||
/** | ||
* encodes a character, if needed or not. | ||
* @param chr | ||
* @return pct-encoded character | ||
*/ | ||
function encodeCharacter (chr) { | ||
var | ||
result = '', | ||
octets = toUtf8(chr), | ||
octets = utf8.encode(chr), | ||
octet, | ||
@@ -95,8 +121,26 @@ index; | ||
function isPctEncoded(chr) { | ||
if (chr.length < 3) { | ||
function isPercentDigitDigit (text, start) { | ||
return text[start] === '%' && charHelper.isHexDigit(text[start + 1]) && charHelper.isHexDigit(text[start + 2]); | ||
} | ||
function parseHex2(text, start) { | ||
return parseInt(text.substr(start, 2), 16); | ||
} | ||
/** | ||
* Returns wether or not the given char sequence is a correctly pct-encoded sequence. | ||
* @param chr | ||
* @return {boolean} | ||
*/ | ||
function isPctEncoded (chr) { | ||
if (!isPercentDigitDigit(chr, 0)) { | ||
return false; | ||
} | ||
for (var index = 0; index < chr.length; index += 3) { | ||
if (chr.charAt(index) !== '%' || !charHelper.isHexDigit(chr.charAt(index + 1) || !charHelper.isHexDigit(chr.charAt(index + 2)))) { | ||
var firstCharCode = parseHex2(chr, 1); | ||
var numBytes = utf8.numBytes(firstCharCode); | ||
if (numBytes === 0) { | ||
return false; | ||
} | ||
for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) { | ||
if (!isPercentDigitDigit(chr, 3*byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(chr, 3*byteNumber + 1))) { | ||
return false; | ||
@@ -108,17 +152,28 @@ } | ||
/** | ||
* Reads as much as needed from the text, e.g. '%20' or '%C3%B6'. It does not decode! | ||
* @param text | ||
* @param startIndex | ||
* @return the character or pct-string of the text at startIndex | ||
*/ | ||
function pctCharAt(text, startIndex) { | ||
var chr = text.charAt(startIndex); | ||
if (chr !== '%') { | ||
var chr = text[startIndex]; | ||
if (!isPercentDigitDigit(text, startIndex)) { | ||
return chr; | ||
} | ||
chr = text.substr(startIndex, 3); | ||
if (!isPctEncoded(chr)) { | ||
return '%'; | ||
var utf8CharCode = parseHex2(text, startIndex + 1); | ||
var numBytes = utf8.numBytes(utf8CharCode); | ||
if (numBytes === 0) { | ||
return chr; | ||
} | ||
return chr; | ||
for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) { | ||
if (!isPercentDigitDigit(text, startIndex + 3 * byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(text, startIndex + 3 * byteNumber + 1))) { | ||
return chr; | ||
} | ||
} | ||
return text.substr(startIndex, 3 * numBytes); | ||
} | ||
return { | ||
encodeCharacter: encode, | ||
decodeCharacter: decodeURIComponent, | ||
encodeCharacter: encodeCharacter, | ||
isPctEncoded: isPctEncoded, | ||
@@ -167,5 +222,8 @@ pctCharAt: pctCharAt | ||
/** | ||
* encoding of rfc 6570 | ||
*/ | ||
var encodingHelper = (function () { | ||
function encode(text, passReserved) { | ||
function encode (text, passReserved) { | ||
var | ||
@@ -190,3 +248,3 @@ result = '', | ||
function encodePassReserved(text) { | ||
function encodePassReserved (text) { | ||
return encode(text, true); | ||
@@ -327,3 +385,3 @@ } | ||
index, | ||
chr; | ||
chr = ''; | ||
@@ -354,3 +412,3 @@ function closeVarname() { | ||
if (varnameStart !== null) { | ||
// the spec says: varname = varchar *( ["."] varchar ) | ||
// the spec says: varname = varchar *( ["."] varchar ) | ||
// so a dot is allowed except for the first char | ||
@@ -401,3 +459,3 @@ if (chr === '.') { | ||
} | ||
throw new Error("illegal character '" + chr + "' at position " + index); | ||
throw new Error("illegal character '" + chr + "' at position " + index + ' of "' + text + '"'); | ||
} // for chr | ||
@@ -536,5 +594,5 @@ if (varnameStart !== null) { | ||
value = variables[varspec.varname]; | ||
// if (!isDefined(value)) { | ||
// continue; | ||
// } | ||
if (!isDefined(value)) { | ||
continue; | ||
} | ||
if (isFirstVarspec) { | ||
@@ -541,0 +599,0 @@ result += this.operator.first; |
@@ -27,3 +27,5 @@ /*jshint */ | ||
"assertion_suffix": "" | ||
}; | ||
}, | ||
NODEUNIT_REPORTER = 'default'; | ||
// NODEUNIT_REPORTER = 'minimal'; | ||
@@ -62,3 +64,3 @@ var | ||
var all = new jake.FileList(); | ||
all.include('./*'); | ||
all.include('./Jakefile.js', 'own-testcases.json'); | ||
all.include('src/**'); | ||
@@ -96,3 +98,3 @@ all.include('test/**'); | ||
jake.logger.log('unit testing ...'); | ||
nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, callback); | ||
nodeunit.reporters[NODEUNIT_REPORTER].run(UNIT_TESTS, NODEUNIT_OPTIONS, callback); | ||
}, | ||
@@ -113,3 +115,3 @@ function (callback) { | ||
jake.logger.log('integration tests ...'); | ||
nodeunit.reporters['default'].run(INTEGRATION_TESTS, NODEUNIT_OPTIONS, callback); | ||
nodeunit.reporters[NODEUNIT_REPORTER].run(INTEGRATION_TESTS, NODEUNIT_OPTIONS, callback); | ||
}, | ||
@@ -133,3 +135,3 @@ function (callback) { | ||
jake.logger.log('integration tests with minified version ... '); | ||
nodeunit.reporters['default'].run(INTEGRATION_TESTS, NODEUNIT_OPTIONS, callback); | ||
nodeunit.reporters[NODEUNIT_REPORTER].run(INTEGRATION_TESTS, NODEUNIT_OPTIONS, callback); | ||
}, | ||
@@ -143,4 +145,11 @@ function (callback) { | ||
desc('release'); | ||
task('release', [TARGET_COMPRESSED], function () { | ||
// for short test only | ||
desc('unit tests'); | ||
task('unit', [], function () { | ||
// here we want the default reporter and not the configured one | ||
nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, complete); | ||
}, ASYNC); | ||
desc('build'); | ||
task('build', [TARGET_COMPRESSED], function () { | ||
jake.logger.log('done.'); | ||
@@ -147,0 +156,0 @@ }); |
@@ -38,6 +38,12 @@ { | ||
], | ||
"version": "0.2.1", | ||
"version": "0.2.2", | ||
"readmeFilename": "README.md", | ||
"gitHead": "901b85201a821427dfb4591b56aea3a70d45c67c", | ||
"devDependencies": {}, | ||
"devDependencies": { | ||
"jshint": "*", | ||
"nodeunit": "*", | ||
"jake": "*", | ||
"uglify-js": "*", | ||
"async": "*" | ||
}, | ||
"repository": { | ||
@@ -44,0 +50,0 @@ "type": "git", |
@@ -20,3 +20,3 @@ URI Template JS | ||
and then: | ||
and then in a node application: | ||
@@ -30,2 +30,11 @@ var | ||
or within a html document (see also demo.html): | ||
<script type="text/javascript" src="bin/uritemplate.js"></script> | ||
<script type="text/javascript"> | ||
var template = UriTemplate.parse('{?query*}'); | ||
alert(template.expand({query: {first: 1, second: 2}})); | ||
</script> | ||
If you want to clone the git project, be aware of the submodule uritemplate-test. | ||
@@ -40,3 +49,3 @@ So you have to to: | ||
----- | ||
jake clean release | ||
jake clean build | ||
@@ -57,4 +66,5 @@ Tests | ||
------------- | ||
0.2.0 heavy project refactoring, splitting source files, introducing jshint (preparation of next steps) | ||
0.2.1 fixed a bug in package.json | ||
* 0.2.2 fixed pct encoding bug with multibyte utf8 chars | ||
* 0.2.1 fixed a bug in package.json | ||
* 0.2.0 heavy project refactoring, splitting source files, introducing jshint (preparation of next steps) | ||
@@ -61,0 +71,0 @@ Next Steps |
/*jshint unused: false */ | ||
/*global rfcCharHelper, pctEncoder*/ | ||
/** | ||
* encoding of rfc 6570 | ||
*/ | ||
var encodingHelper = (function () { | ||
"use strict"; | ||
function encode(text, passReserved) { | ||
function encode (text, passReserved) { | ||
var | ||
@@ -26,3 +29,3 @@ result = '', | ||
function encodePassReserved(text) { | ||
function encodePassReserved (text) { | ||
return encode(text, true); | ||
@@ -29,0 +32,0 @@ } |
@@ -15,3 +15,3 @@ /*jshint unused:false */ | ||
index, | ||
chr; | ||
chr = ''; | ||
@@ -42,3 +42,3 @@ function closeVarname() { | ||
if (varnameStart !== null) { | ||
// the spec says: varname = varchar *( ["."] varchar ) | ||
// the spec says: varname = varchar *( ["."] varchar ) | ||
// so a dot is allowed except for the first char | ||
@@ -89,3 +89,3 @@ if (chr === '.') { | ||
} | ||
throw new Error("illegal character '" + chr + "' at position " + index); | ||
throw new Error("illegal character '" + chr + "' at position " + index + ' of "' + text + '"'); | ||
} // for chr | ||
@@ -92,0 +92,0 @@ if (varnameStart !== null) { |
/*jshint unused:false */ | ||
/*global unescape, charHelper*/ | ||
/*global charHelper, unescape*/ | ||
var pctEncoder = (function () { | ||
"use strict"; | ||
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html | ||
function toUtf8(s) { | ||
return unescape(encodeURIComponent(s)); | ||
} | ||
var utf8 = { | ||
encode: function (chr) { | ||
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html | ||
return unescape(encodeURIComponent(chr)); | ||
}, | ||
numBytes: function (firstCharCode) { | ||
if (firstCharCode <= 0x7F) { | ||
return 1; | ||
} | ||
else if (0xC2 <= firstCharCode && firstCharCode <= 0xDF) { | ||
return 2; | ||
} | ||
else if (0xE0 <= firstCharCode && firstCharCode <= 0xEF) { | ||
return 3; | ||
} | ||
else if (0xF0 <= firstCharCode && firstCharCode <= 0xF4) { | ||
return 4; | ||
} | ||
// no valid first octet | ||
return 0; | ||
}, | ||
isValidFollowingCharCode: function (charCode) { | ||
return 0x80 <= charCode && charCode <= 0xBF; | ||
} | ||
}; | ||
function encode(chr) { | ||
/** | ||
* encodes a character, if needed or not. | ||
* @param chr | ||
* @return pct-encoded character | ||
*/ | ||
function encodeCharacter (chr) { | ||
var | ||
result = '', | ||
octets = toUtf8(chr), | ||
octets = utf8.encode(chr), | ||
octet, | ||
@@ -24,8 +50,26 @@ index; | ||
function isPctEncoded(chr) { | ||
if (chr.length < 3) { | ||
function isPercentDigitDigit (text, start) { | ||
return text[start] === '%' && charHelper.isHexDigit(text[start + 1]) && charHelper.isHexDigit(text[start + 2]); | ||
} | ||
function parseHex2(text, start) { | ||
return parseInt(text.substr(start, 2), 16); | ||
} | ||
/** | ||
* Returns wether or not the given char sequence is a correctly pct-encoded sequence. | ||
* @param chr | ||
* @return {boolean} | ||
*/ | ||
function isPctEncoded (chr) { | ||
if (!isPercentDigitDigit(chr, 0)) { | ||
return false; | ||
} | ||
for (var index = 0; index < chr.length; index += 3) { | ||
if (chr.charAt(index) !== '%' || !charHelper.isHexDigit(chr.charAt(index + 1) || !charHelper.isHexDigit(chr.charAt(index + 2)))) { | ||
var firstCharCode = parseHex2(chr, 1); | ||
var numBytes = utf8.numBytes(firstCharCode); | ||
if (numBytes === 0) { | ||
return false; | ||
} | ||
for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) { | ||
if (!isPercentDigitDigit(chr, 3*byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(chr, 3*byteNumber + 1))) { | ||
return false; | ||
@@ -37,17 +81,28 @@ } | ||
/** | ||
* Reads as much as needed from the text, e.g. '%20' or '%C3%B6'. It does not decode! | ||
* @param text | ||
* @param startIndex | ||
* @return the character or pct-string of the text at startIndex | ||
*/ | ||
function pctCharAt(text, startIndex) { | ||
var chr = text.charAt(startIndex); | ||
if (chr !== '%') { | ||
var chr = text[startIndex]; | ||
if (!isPercentDigitDigit(text, startIndex)) { | ||
return chr; | ||
} | ||
chr = text.substr(startIndex, 3); | ||
if (!isPctEncoded(chr)) { | ||
return '%'; | ||
var utf8CharCode = parseHex2(text, startIndex + 1); | ||
var numBytes = utf8.numBytes(utf8CharCode); | ||
if (numBytes === 0) { | ||
return chr; | ||
} | ||
return chr; | ||
for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) { | ||
if (!isPercentDigitDigit(text, startIndex + 3 * byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(text, startIndex + 3 * byteNumber + 1))) { | ||
return chr; | ||
} | ||
} | ||
return text.substr(startIndex, 3 * numBytes); | ||
} | ||
return { | ||
encodeCharacter: encode, | ||
decodeCharacter: decodeURIComponent, | ||
encodeCharacter: encodeCharacter, | ||
isPctEncoded: isPctEncoded, | ||
@@ -54,0 +109,0 @@ pctCharAt: pctCharAt |
@@ -73,5 +73,5 @@ /*jshint unused:false */ | ||
value = variables[varspec.varname]; | ||
// if (!isDefined(value)) { | ||
// continue; | ||
// } | ||
if (!isDefined(value)) { | ||
continue; | ||
} | ||
if (isFirstVarspec) { | ||
@@ -78,0 +78,0 @@ result += this.operator.first; |
@@ -6,5 +6,14 @@ module.exports = (function () { | ||
fs = require('fs'), | ||
path = require('path'), | ||
sandbox = require('nodeunit').utils.sandbox; | ||
// var testCase = require('nodeunit').testCase; | ||
var NOISY = false; | ||
function log(text) { | ||
if (NOISY) { | ||
console.log(text); | ||
} | ||
} | ||
function loadUriTemplate() { | ||
@@ -27,14 +36,11 @@ var context = {module: {}}; | ||
uriTemplate = UriTemplate.parse(template); | ||
// console.log('uritemplate parsed:' + uriTemplate); | ||
} | ||
catch (error) { | ||
// console.log('error', error); | ||
// console.log('expected', expected.toString()); | ||
// if expected === false, the error was expected! | ||
if (expected === false) { | ||
log('ok. expected error found'); | ||
return; | ||
} | ||
console.log('error', error); | ||
console.log('expected', expected.toString()); | ||
test.notEqual('chapter ' + chapterName + ', template ' + template + ' threw error: ' + error); | ||
log('error', error); | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + error); | ||
return; | ||
@@ -54,3 +60,3 @@ } | ||
} | ||
test.notEqual('chapter ' + chapterName + ', template ' + template + ' threw error: ' + JSON.stringify(exception, null, 4)); | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + JSON.stringify(exception, null, 4)); | ||
return; | ||
@@ -65,3 +71,3 @@ } | ||
} | ||
test.notEqual('actual: ' + actual + ', expected: one of ' + JSON.stringify(expected) + 'chapter ' + chapterName + ', template ' + template); | ||
test.fail("actual: '" + actual + "', expected: one of " + JSON.stringify(expected) + ', chapter ' + chapterName + ', template ' + template); | ||
} | ||
@@ -83,2 +89,3 @@ else { | ||
UriTemplate; | ||
log(filename); | ||
UriTemplate = loadUriTemplate(); | ||
@@ -88,2 +95,3 @@ tests = loadTestFile(filename); | ||
if (tests.hasOwnProperty(chapterName)) { | ||
log('-> ' + chapterName); | ||
chapter = tests[chapterName]; | ||
@@ -94,5 +102,5 @@ variables = chapter.variables; | ||
expexted = chapter.testcases[index][1]; | ||
log(' -> ' + template); | ||
assertMatches(test, template, variables, expexted, chapterName, UriTemplate); | ||
} | ||
console.log(chapterName); | ||
} | ||
@@ -103,12 +111,16 @@ } | ||
var SPEC_HOME = 'uritemplate-test'; | ||
return { | ||
'spec examples': function (test) { | ||
runTestFile(test, 'uritemplate-test/spec-examples.json'); | ||
runTestFile(test, path.join(SPEC_HOME, 'spec-examples.json')); | ||
}, | ||
'extended tests': function (test) { | ||
runTestFile(test, 'uritemplate-test/extended-tests.json'); | ||
runTestFile(test, path.join(SPEC_HOME, 'extended-tests.json')); | ||
}, | ||
// 'negative tests': function (test) { | ||
// runTestFile(test, 'uritemplate-test/negative-tests.json'); | ||
// }, | ||
/* negative tests have invalid specs in it -- they were fixed in a later version of the test | ||
'negative tests': function (test) { | ||
runTestFile(test, path.join(SPEC_HOME, 'negative-tests.json')); | ||
}, | ||
*/ | ||
'own tests': function (test) { | ||
@@ -115,0 +127,0 @@ runTestFile(test, 'own-testcases.json'); |
module.exports = (function () { | ||
"use strict"; | ||
var | ||
sandbox = require('nodeunit').utils.sandbox; | ||
var context = {}; | ||
sandbox('src/objectHelper.js', context); | ||
var context = {console: console}; | ||
require('nodeunit').utils.sandbox('src/objectHelper.js', context); | ||
var objectHelper = context.objectHelper; | ||
return { | ||
@@ -15,0 +9,0 @@ 'reduce works with initial value': function (test) { |
110406
32
2482
73
5