Commit f88149e5 authored by fxa's avatar fxa

bugfixes according double encoding (Thanks to [RubenVerborgh]) and prefix modifiers

parent dd7a3854
......@@ -47,6 +47,7 @@
UNIT_TESTS = new jake.FileList('test/unit/test*.js').toArray(),
INTEGRATION_TESTS = [
path.join('test', 'integration', 'simpleTest.js'),
path.join('test', 'integration', 'testExport.js'),
path.join('test', 'integration', 'testRfcSamples.js')
],
......@@ -59,32 +60,36 @@
ASYNC = {async: true};
var TARGET_UNCOMPRESSED_DEPENDENCIES = (function () {
var all = new jake.FileList();
all.include('./Jakefile.js', 'own-testcases.json');
all.include('src/**');
all.include('test/**');
all.exclude(TARGET_COMPRESSED);
var TARGET_UNCOMPRESSED_DEPENDENCIES = all.toArray();
return all.toArray();
}());
function closeTask(err) {
function closeTask (err) {
if (err) {
fail(err);
fail(JSON.stringify(err, null, 4));
}
else {
complete();
}
}
desc('clean');
desc('removes all artifacts');
task('clean', [], function () {
function unlinkWhenExists(filename, callback) {
function unlinkWhenExists (filename, callback) {
fs.unlink(filename, function (err) {
callback(err && err.code !== 'ENOENT' ? err : undefined);
});
}
async.forEach([TMP_UNTESTED_UNCOMPRESSED, TMP_UNTESTED_COMPRESSED, TARGET_UNCOMPRESSED, TARGET_COMPRESSED], unlinkWhenExists, closeTask);
}, ASYNC);
file(TARGET_UNCOMPRESSED, TARGET_UNCOMPRESSED_DEPENDENCIES, function () {
// there is no other way to pass parameters to a testcase -- sorry
global.URI_TEMPLATE_FILE = TMP_UNTESTED_UNCOMPRESSED;
async.series([
function (callback) {
......@@ -93,7 +98,7 @@
},
function (callback) {
jake.logger.log('unit testing ...');
nodeunit.reporters[NODEUNIT_REPORTER].run(UNIT_TESTS, NODEUNIT_OPTIONS, callback);
nodeunit.reporters['minimal'].run(UNIT_TESTS, NODEUNIT_OPTIONS, callback);
},
function (callback) {
jake.logger.log('build concatenated version ...');
......@@ -138,9 +143,8 @@
}, ASYNC);
// for short test only
desc('unit tests');
desc('unit tests (without jshint)');
task('unit', [], function () {
// here we want the default reporter and not the configured one
nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, complete);
}, ASYNC);
......@@ -148,7 +152,6 @@
task('build', [TARGET_COMPRESSED], function () {
jake.logger.log('done.');
});
task('default', ['clean', 'release']);
task('default', ['clean', 'build']);
}());
\ No newline at end of file
......@@ -9,6 +9,8 @@ It exposes a constructor function UriTemplate with the two methods:
* (static) parse(uriTemplateText) returning an instance of UriTemplate
* expand(variables) returning an string
Be aware, that a parsed UriTemplate is frozen, so it is stateless. You can reuse instances of UriTemplates.
Requirements
------------
......@@ -60,6 +62,7 @@ MIT License, see http://mit-license.org/
Release Notes
-------------
* 0.2.4 fixed double encoding according [RubenVerborgh] and some Prefix modifiers bugs
* 0.2.3 fixed bug with empty objects ('{?empty}' with '{empty:{}}' shall expand to '?empty=')
* 0.2.2 fixed pct encoding bug with multibyte utf8 chars
* 0.2.1 fixed a bug in package.json
......@@ -67,7 +70,6 @@ Release Notes
Next Steps
----------
* Implementing unit tests (now only dummy test implemented)
* Updating uritemplate-test (mnot added some new tests and removed some wrong. At the moment I cannot update, because the new tests will not pass)
* Implementing more unit tests (now only a view tests are implemented)
* A new method extract(uri), which tries to extract the variables from a given uri.
This is harder, than you might think
(function(e){"use strict";function n(e){var t,o;if(null===e||void 0===e)return!1;if(r.isArray(e)){for(t=0;e.length>t;t+=1)if(n(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)&&n(e[o]))return!0;return!1}var r=function(){function e(e){return"[object Array]"===Object.prototype.toString.apply(e)}function n(e,n,r){var t,o=r;for(t in e)e.hasOwnProperty(t)&&(o=n(o,e[t],t,e));return o}function r(e,n,r){var t,o=r;for(t=0;e.length>t;t+=1)o=n(o,e[t],t,e);return o}function t(t,o,i){return e(t)?r(t,o,i):n(t,o,i)}return{isArray:e,reduce:t}}(),t=function(){function e(e){return e>="a"&&"z">=e||e>="A"&&"Z">=e}function n(e){return e>="0"&&"9">=e}function r(e){return n(e)||e>="a"&&"f">=e||e>="A"&&"F">=e}return{isAlpha:e,isDigit:n,isHexDigit:r}}(),o=function(){function e(e){var n,r,t="",o=u.encode(e);for(r=0;o.length>r;r+=1)n=o.charCodeAt(r),t+="%"+n.toString(16).toUpperCase();return t}function n(e,n){return"%"===e[n]&&t.isHexDigit(e[n+1])&&t.isHexDigit(e[n+2])}function r(e,n){return parseInt(e.substr(n,2),16)}function o(e){if(!n(e,0))return!1;var t=r(e,1),o=u.numBytes(t);if(0===o)return!1;for(var i=1;o>i;i+=1)if(!n(e,3*i)||!u.isValidFollowingCharCode(r(e,3*i+1)))return!1;return!0}function i(e,t){var o=e[t];if(!n(e,t))return o;var i=r(e,t+1),a=u.numBytes(i);if(0===a)return o;for(var s=1;a>s;s+=1)if(!n(e,t+3*s)||!u.isValidFollowingCharCode(r(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 n(e){return t.isAlpha(e)||t.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:n,isReserved:r}}(),u=function(){function e(e,n){var r,t="",u="";for(("number"==typeof e||"boolean"==typeof e)&&(e=""+e),r=0;e.length>r;r+=u.length)u=o.pctCharAt(e,r),t+=u.length>1?u:i.isUnreserved(u)||n&&i.isReserved(u)?u:o.encodeCharacter(u);return t}function n(n){return e(n,!0)}return{encode:e,encodePassReserved:n}}(),a=function(){function e(e){n[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 n={};return e(""),e("+"),e("#"),e("."),e("/"),e(";"),e("?"),e("&"),{valueOf:function(e){if(n[e])return n[e];if("=,!@|".indexOf(e)>=0)throw Error('Illegal use of reserved operator "'+e+'"');return n[""]}}}(),s=function(){function e(e){var n,r="",t="";for(n=0;e.length>n;n+=t.length)t=o.pctCharAt(e,n),r+=t.length>0?t:i.isReserved(t)||i.isUnreserved(t)?t:o.encodeCharacter(t);return r}function n(e){this.literal=n.encodeLiteral(e)}return n.encodeLiteral=e,n.prototype.expand=function(){return this.literal},n.prototype.toString=n.prototype.expand,n}(),f=function(){function e(e){function n(){p={varname:u.substring(d,f),exploded:!1,maxLength:null},d=null}function r(){if(h===f)throw Error("after a ':' you have to specify the length. position = "+f);p.maxLength=parseInt(u.substring(h,f),10),h=null}var u,s,f,c=[],p=null,d=null,h=null,g="";for(u=e.substr(1,e.length-2),s=a.valueOf(u.charAt(0)),f=""===s.symbol?0:1,d=f;u.length>f;f+=g.length){if(g=o.pctCharAt(u,f),null!==d){if("."===g){if(d===f)throw Error("a varname MUST NOT start with a dot -- see position "+f);continue}if(i.isVarchar(g))continue;n()}if(null!==h){if(t.isDigit(g))continue;r()}if(":"!==g)if("*"!==g){if(","!==g)throw Error("illegal character '"+g+"' at position "+f+' of "'+u+'"');c.push(p),p=null,d=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);h=f+1}}return null!==d&&n(),null!==h&&r(),c.push(p),new l(e,s,c)}function n(n){var r,t,o=[],i=null,u=0;for(r=0;n.length>r;r+=1)if(t=n.charAt(r),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 "+r);if("}"===t){if(i+1===r)throw Error("empty braces on position "+i);o.push(e(n.substring(i,r+1))),i=null,u=r+1}}else{if("}"===t)throw Error("brace was closed in position "+r+" but never opened");"{"===t&&(r>u&&o.push(new s(n.substring(u,r))),u=null,i=r)}if(null!==i)throw Error("brace was opened on position "+i+", but never closed");return n.length>u&&o.push(new s(n.substr(u))),new c(n,o)}return n}(),l=function(){function e(e){return JSON?JSON.stringify(e):e}function t(e,n,r){this.templateText=e,this.operator=n,this.varspecs=r}return t.prototype.toString=function(){return this.templateText},t.prototype.expand=function(t){function o(e,r,t){return n(r)&&(e.length>0&&(e+=","),c||(e+=h.encode(t)+","),e+=h.encode(r)),e}function i(e,r,t){return n(r)&&(e.length>0&&(e+=h.separator),e+=c?s.encodeLiteral(f.varname):h.encode(t),e+="="+h.encode(r)),e}function u(e,r,t){return n(r)&&(e.length>0&&(e+=h.separator),c||(e+=h.encode(t)+"="),e+=h.encode(r)),e}var a,f,l,c,p="",d=!0,h=this.operator;for(a=0;this.varspecs.length>a;a+=1)if(f=this.varspecs[a],l=t[f.varname],n(l))if(d?(p+=h.first,d=!1):p+=h.separator,c=r.isArray(l),"string"==typeof l||"number"==typeof l||"boolean"==typeof l){if(l=""+l,h.named){if(p+=s.encodeLiteral(f.varname),""===l){p+=h.ifEmpty;continue}p+="="}f.maxLength&&l.length>f.maxLength&&(l=l.substr(0,f.maxLength)),p+=h.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+=r.reduce(l,h.named?i:u,"");else{if(h.named){if(p+=s.encodeLiteral(f.varname),!n(l)){p+=h.ifEmpty;continue}p+="="}p+=r.reduce(l,o,"")}}if(d){var g=!1;for(a=0;this.varspecs.length>a;a+=1)if(this.varspecs[a].exploded){g=!0;break}h.named&&!g&&(p+=h.symbol,p+=f.varname+h.ifEmpty)}return p},t}(),c=function(){function e(e,n){this.templateText=e,this.expressions=n}return e.prototype.toString=function(){return this.templateText},e.prototype.expand=function(e){var n,r="";for(n=0;this.expressions.length>n;n+=1)r+=this.expressions[n].expand(e);return r},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});
\ No newline at end of file
(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)}function o(e){if("object"!=typeof e||null===e)return e;Object.freeze(e);var r,n;for(n in e)e.hasOwnProperty(n)&&(r=e[n],"object"==typeof r&&i(r));return e}function i(e){return"function"==typeof Object.freeze?o(e):e}return{isArray:e,reduce:t,deepFreeze:i}}(),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=e.charAt(n),t+=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(r){this.literal=e.encodeLiteral(r)}return e.encodeLiteral=function(e){var r,n="",t="";for(r=0;e.length>r;r+=t.length)t=o.pctCharAt(e,r),n+=t.length>1?t:i.isReserved(t)||i.isUnreserved(t)?t:o.encodeCharacter(t);return n},e.prototype.expand=function(){return this.literal},e.prototype.toString=e.prototype.expand,e}(),f=function(){function e(e){function r(){p={varname:u.substring(d,f),exploded:!1,maxLength:null},d=null}function n(){if(h===f)throw Error("after a ':' you have to specify the length. position = "+f);p.maxLength=parseInt(u.substring(h,f),10),h=null}var u,s,f,c=[],p=null,d=null,h=null,g="";for(u=e.substr(1,e.length-2),s=a.valueOf(u.charAt(0)),f=""===s.symbol?0:1,d=f;u.length>f;f+=g.length){if(g=o.pctCharAt(u,f),null!==d){if("."===g){if(d===f)throw Error("a varname MUST NOT start with a dot -- see position "+f);continue}if(i.isVarchar(g))continue;r()}if(null!==h){if(f===h&&"0"===g)throw Error("A :prefix must not start with digit 0 -- see position "+f);if(t.isDigit(g)){if(f-h>=4)throw Error("A :prefix must max 4 digits -- see position "+f);continue}n()}if(":"!==g)if("*"!==g){if(","!==g)throw Error("illegal character '"+g+"' at position "+f+' of "'+u+'"');c.push(p),p=null,d=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);h=f+1}}return null!==d&&r(),null!==h&&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+=h.encode(t)+","),e+=h.encode(n)),e}function i(e,n,t){return r(n)&&(e.length>0&&(e+=h.separator),e+=c?s.encodeLiteral(f.varname):h.encode(t),e+="="+h.encode(n)),e}function u(e,n,t){return r(n)&&(e.length>0&&(e+=h.separator),c||(e+=h.encode(t)+"="),e+=h.encode(n)),e}var a,f,l,c,p="",d=!0,h=this.operator;for(a=0;this.varspecs.length>a;a+=1)if(f=this.varspecs[a],l=t[f.varname],r(l))if(d?(p+=h.first,d=!1):p+=h.separator,c=n.isArray(l),"string"==typeof l||"number"==typeof l||"boolean"==typeof l){if(l=""+l,h.named){if(p+=s.encodeLiteral(f.varname),""===l){p+=h.ifEmpty;continue}p+="="}null!==f.maxLength&&(l=l.substr(0,f.maxLength)),p+=h.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,h.named?i:u,"");else{if(h.named){if(p+=s.encodeLiteral(f.varname),!r(l)){p+=h.ifEmpty;continue}p+="="}p+=n.reduce(l,o,"")}}if(d){var g=!1;for(a=0;this.varspecs.length>a;a+=1)if(this.varspecs[a].exploded){g=!0;break}h.named&&!g&&(p+=h.symbol,p+=f.varname+h.ifEmpty)}return p},t}(),c=function(){function e(e,r){this.templateText=e,this.expressions=r,n.deepFreeze(this)}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:"function"==typeof define?define([],function(){return e}):"undefined"!=typeof window?window.UriTemplate=e:global.UriTemplate=e});
\ No newline at end of file
......@@ -9,14 +9,13 @@
(function (exportCallback) {
"use strict";
var objectHelper = (function () {
function isArray(value) {
function isArray (value) {
return Object.prototype.toString.apply(value) === '[object Array]';
}
// performs an array.reduce for objects
// TODO handling if initialValue is undefined
function objectReduce(object, callback, initialValue) {
function objectReduce (object, callback, initialValue) {
var
propertyName,
currentValue = initialValue;
......@@ -30,7 +29,7 @@ var objectHelper = (function () {
// performs an array.reduce, if reduce is not present (older browser...)
// TODO handling if initialValue is undefined
function arrayReduce(array, callback, initialValue) {
function arrayReduce (array, callback, initialValue) {
var
index,
currentValue = initialValue;
......@@ -40,13 +39,40 @@ var objectHelper = (function () {
return currentValue;
}
function reduce(arrayOrObject, callback, initialValue) {
function reduce (arrayOrObject, callback, initialValue) {
return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue);
}
function deepFreezeUsingObjectFreeze (object) {
if (typeof object !== "object" || object === null) {
return object;
}
Object.freeze(object);
var property, propertyName;
for (propertyName in object) {
if (object.hasOwnProperty(propertyName)) {
property = object[propertyName];
// be aware, arrays are 'object', too
if (typeof property === "object") {
deepFreeze(property);
}
}
}
return object;
}
function deepFreeze (object) {
if (typeof Object.freeze === 'function') {
return deepFreezeUsingObjectFreeze(object);
}
return object;
}
return {
isArray: isArray,
reduce: reduce
reduce: reduce,
deepFreeze: deepFreeze
};
}());
......@@ -72,7 +98,6 @@ var charHelper = (function () {
}());
var pctEncoder = (function () {
var utf8 = {
encode: function (chr) {
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html
......@@ -117,16 +142,28 @@ var pctEncoder = (function () {
return result;
}
/**
* Returns, whether the given text at start is in the form 'percent hex-digit hex-digit', like '%3F'
* @param text
* @param start
* @return {boolean|*|*}
*/
function isPercentDigitDigit (text, start) {
return text[start] === '%' && charHelper.isHexDigit(text[start + 1]) && charHelper.isHexDigit(text[start + 2]);
}
function parseHex2(text, start) {
/**
* Parses a hex number from start with length 2.
* @param text a string
* @param start the start index of the 2-digit hex number
* @return {Number}
*/
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.
* Returns whether or not the given char sequence is a correctly pct-encoded sequence.
* @param chr
* @return {boolean}
*/
......@@ -185,7 +222,7 @@ var rfcCharHelper = (function () {
* @param chr
* @return (Boolean)
*/
function isVarchar(chr) {
function isVarchar (chr) {
return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '_' || pctEncoder.isPctEncoded(chr);
}
......@@ -194,16 +231,17 @@ var rfcCharHelper = (function () {
* @param chr
* @return {Boolean}
*/
function isUnreserved(chr) {
function isUnreserved (chr) {
return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '-' || chr === '.' || chr === '_' || chr === '~';
}
/**
* Returns if chr is an reserved character according 1.5 of rfc 6570
* or the percent character mentioned in 3.2.1.
* @param chr
* @return {Boolean}
*/
function isReserved(chr) {
function isReserved (chr) {
return chr === ':' || chr === '/' || chr === '?' || chr === '#' || chr === '[' || chr === ']' || chr === '@' || chr === '!' || chr === '$' || chr === '&' || chr === '(' ||
chr === ')' || chr === '*' || chr === '+' || chr === ',' || chr === ';' || chr === '=' || chr === "'";
}
......@@ -230,14 +268,9 @@ var encodingHelper = (function () {
text = text.toString();
}
for (index = 0; index < text.length; index += chr.length) {
chr = pctEncoder.pctCharAt(text, index);
if (chr.length > 1) {
result += chr;
}
else {
chr = text.charAt(index);
result += rfcCharHelper.isUnreserved(chr) || (passReserved && rfcCharHelper.isReserved(chr)) ? chr : pctEncoder.encodeCharacter(chr);
}
}
return result;
}
......@@ -303,7 +336,7 @@ var operators = (function () {
* @param object
* @return {Boolean}
*/
function isDefined(object) {
function isDefined (object) {
var
index,
propertyName;
......@@ -332,30 +365,28 @@ function isDefined(object) {
}
var LiteralExpression = (function () {
function LiteralExpression (literal) {
this.literal = LiteralExpression.encodeLiteral(literal);
}
function encodeLiteral(literal) {
LiteralExpression.encodeLiteral = function (literal) {
var
result = '',
index,
chr = '';
for (index = 0; index < literal.length; index += chr.length) {
chr = pctEncoder.pctCharAt(literal, index);
if (chr.length > 0) {
if (chr.length > 1) {
result += chr;
}
else {
result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
}
// chr = literal.charAt(index);
// result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
}
return result;
}
function LiteralExpression(literal) {
this.literal = LiteralExpression.encodeLiteral(literal);
}
LiteralExpression.encodeLiteral = encodeLiteral;
};
LiteralExpression.prototype.expand = function () {
return this.literal;
......@@ -367,8 +398,7 @@ var LiteralExpression = (function () {
}());
var parse = (function () {
function parseExpression(outerText) {
function parseExpression (outerText) {
var
text,
operator,
......@@ -379,12 +409,12 @@ var parse = (function () {
index,
chr = '';
function closeVarname() {
function closeVarname () {
varspec = {varname: text.substring(varnameStart, index), exploded: false, maxLength: null};
varnameStart = null;
}
function closeMaxLength() {
function closeMaxLength () {
if (maxLengthStart === index) {
throw new Error("after a ':' you have to specify the length. position = " + index);
}
......@@ -392,7 +422,7 @@ var parse = (function () {
maxLengthStart = null;
}
// remove outer {}
// remove outer braces
text = outerText.substr(1, outerText.length - 2);
// determine operator
......@@ -417,7 +447,13 @@ var parse = (function () {
closeVarname();
}
if (maxLengthStart !== null) {
if (index === maxLengthStart && chr === '0') {
throw new Error('A :prefix must not start with digit 0 -- see position ' + index);
}
if (charHelper.isDigit(chr)) {
if (index - maxLengthStart >= 4) {
throw new Error('A :prefix must max 4 digits -- see position ' + index);
}
continue;
}
closeMaxLength();
......@@ -461,7 +497,7 @@ var parse = (function () {
return new VariableExpression(outerText, operator, varspecs);
}
function parseTemplate(uriTemplateText) {
function parseTemplate (uriTemplateText) {
// assert filled string
var
index,
......@@ -515,13 +551,12 @@ var parse = (function () {
}());
var VariableExpression = (function () {
// helper function if JSON is not available
function prettyPrint(value) {
function prettyPrint (value) {
return JSON ? JSON.stringify(value) : value;
}
function VariableExpression(templateText, operator, varspecs) {
function VariableExpression (templateText, operator, varspecs) {
this.templateText = templateText;
this.operator = operator;
this.varspecs = varspecs;
......@@ -531,7 +566,7 @@ var VariableExpression = (function () {
return this.templateText;
};
VariableExpression.prototype.expand = function expandExpression(variables) {
VariableExpression.prototype.expand = function (variables) {
var
result = '',
index,
......@@ -542,7 +577,7 @@ var VariableExpression = (function () {
operator = this.operator;
// callback to be used within array.reduce
function reduceUnexploded(result, currentValue, currentKey) {
function reduceUnexploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) {
if (result.length > 0) {
result += ',';
......@@ -555,7 +590,7 @@ var VariableExpression = (function () {
return result;
}
function reduceNamedExploded(result, currentValue, currentKey) {
function reduceNamedExploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) {
if (result.length > 0) {
result += operator.separator;
......@@ -566,7 +601,7 @@ var VariableExpression = (function () {
return result;
}
function reduceUnnamedExploded(result, currentValue, currentKey) {
function reduceUnnamedExploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) {
if (result.length > 0) {
result += operator.separator;
......@@ -604,7 +639,7 @@ var VariableExpression = (function () {
}
result += '=';
}
if (varspec.maxLength && value.length > varspec.maxLength) {
if (varspec.maxLength !== null) {
value = value.substr(0, varspec.maxLength);
}
result += operator.encode(value);
......@@ -648,14 +683,14 @@ var VariableExpression = (function () {
return result;
};
return VariableExpression;
}());
var UriTemplate = (function () {
function UriTemplate(templateText, expressions) {
function UriTemplate (templateText, expressions) {
this.templateText = templateText;
this.expressions = expressions;
objectHelper.deepFreeze(this);
}
UriTemplate.prototype.toString = function () {
......@@ -663,6 +698,7 @@ var UriTemplate = (function () {
};
UriTemplate.prototype.expand = function (variables) {
// this.expressions.map(function (expression) {return expression.expand(variables);}).join('');
var
index,
result = '';
......@@ -673,7 +709,6 @@ var UriTemplate = (function () {
};
UriTemplate.parse = parse;
return UriTemplate;
}());
......@@ -685,7 +720,7 @@ var UriTemplate = (function () {
if (typeof module !== "undefined") {
module.exports = UriTemplate;
}
else if (typeof define !== "undefined") {
else if (typeof define === "function") {
define([],function() {
return UriTemplate;
});
......
......@@ -5,13 +5,13 @@ module.exports = (function () {
fs = require('fs'),
async = require('async');
function readFileUtf8(filename, callback) {
function readFileUtf8 (filename, callback) {
// if you call readFile with encoding, the result is the file content as string.
// without encoding it would be a stream, which can be converted to a string with its toString() method
fs.readFile(filename, 'utf-8', callback);
}
function concat(inputFileArr, outputfile, callback, options) {
function concat (inputFileArr, outputfile, callback, options) {
async.map(inputFileArr, readFileUtf8, function (err, contents) {
if (err) {
throw new Error('could not read files: ' + err);
......@@ -41,19 +41,19 @@ module.exports = (function () {
});
}
function startsWith(text, beginning) {
function startsWith (text, beginning) {
return text.substr(0, beginning.length) === beginning;
}
function removeFirstLine(text) {
function removeFirstLine (text) {
var indexOfLinebreak = text.indexOf("\n");
if (indexOfLinebreak < 0) {
return "";
return text;
}
return text.substr(indexOfLinebreak + 1);
}
function removeUseStrict(text) {
function removeUseStrict (text) {
var lines = text.split('\n');
var filteredLines = lines.filter(function (line) {
return line.indexOf('"use strict"') < 0;
......@@ -61,7 +61,7 @@ module.exports = (function () {
return filteredLines.join('\n');
}
function removeJshintOptions(source) {
function removeJshintOptions (source) {
if (startsWith(source, '/*jshint')) {
source = removeFirstLine(source);
}
......
......@@ -22,7 +22,7 @@ module.exports = (function () {
undef: true, // forbids use of undefined variables
unused: true, // forbids unused variables
maxcomplexity: 17 // much too high. should be max. 10
maxcomplexity: 19 // much too high. should be max. 10
},
JSHINT_GLOBALS = {
......@@ -39,13 +39,19 @@ module.exports = (function () {
return;
}
if (err) {
throw new Error('could not read file ' + jsFile + ': ' + err);
callback('could not read file ' + jsFile + ': ' + err);
return;
}
numCheckedFiles += 1;
if (!jshint.jshint(content, JSHINT_OPTIONS, JSHINT_GLOBALS)) {
failed = true;
console.log(jshint.errors);
callback(jshint.errors, jsFile);
jshint.errors.push({filename: jsFile});
// jshint errors are awfully formated, when given to jake.fail()
console.log(JSON.stringify(jshint.errors, null, 4));
callback({
filename: jsFile,
reason: jshint.errors[0].reason
});
}
if (numCheckedFiles === jsFiles.length) {
callback();
......
......@@ -8,5 +8,40 @@
"testcases" : [
["http://localhost:8080/api/search{?q}", "http://localhost:8080/api/search?q=val"]
]
},
"Allowed characters": {
"level": 3,
"variables": {
"dot": "dot"
},
"testcases": [
["{?.dot}", false],
["{a,.b}", false]
]
},
"Prefix modifiers": {
"level": 4,
"variables": {
"text": "I like octal numbers",
"emptyObject": {},
"filledObject": {"composite": "true"},
"emptyArr": [],
"filledArr": [1]
},
"testcases": [
["{text:0}", false],
["{text:08}", false],
["{text:8}", "I%20like%20o"],
["{text:9999}", "I%20like%20octal%20numbers"],
["{text:10000}", false],
["{text:1.0}", false],
["{text:1e+2}", false],
["{text:9876543210987654321098765432109876543210}", false],
["{emptyObject:1}", ""],
["{filledObject:1}", false],
["{emptyArr:1}", ""],
["{filledArr:1}", false]
]
}
}
......@@ -12,12 +12,10 @@
"contributors": [],
"dependencies": {},
"main": "bin/uritemplate.js",
"licenses": [
{
"licenses": [{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
}],
"files": [
"src",
"test",
......@@ -27,7 +25,7 @@
"demo.html",
"demo_AMD.html",
"README.md",
"reqire.js",
"require.js",
".gitmodules",
"uritemplate-test/extended-tests.json",
"uritemplate-test/negative-tests.json",
......@@ -35,7 +33,7 @@
"uritemplate-test/spec-examples-by-sections.json",
"uritemplate-test/spec-examples.json"
],
"version": "0.2.3",
"version": "0.2.4",
"readmeFilename": "README.md",
"gitHead": "901b85201a821427dfb4591b56aea3a70d45c67c",
"devDependencies": {
......@@ -48,6 +46,5 @@
"repository": {
"type": "git",
"url": "https://github.com/fxa/uritemplate-js.git"
},
"license": "MIT"
}
}
......@@ -2,25 +2,28 @@
/*global pctEncoder, rfcCharHelper*/
var LiteralExpression = (function () {
"use strict";
function LiteralExpression (literal) {
this.literal = LiteralExpression.encodeLiteral(literal);
}
function encodeLiteral(literal) {
LiteralExpression.encodeLiteral = function (literal) {
var
result = '',
index,
chr = '';
for (index = 0; index < literal.length; index += chr.length) {
chr = literal.charAt(index);
chr = pctEncoder.pctCharAt(literal, index);
if (chr.length > 1) {
result += chr;
}
else {
result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
}
return result;
// chr = literal.charAt(index);
// result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
}
function LiteralExpression(literal) {
this.literal = LiteralExpression.encodeLiteral(literal);
}
LiteralExpression.encodeLiteral = encodeLiteral;
return result;
};
LiteralExpression.prototype.expand = function () {
return this.literal;
......
/*jshint unused:false */
/*global parse*/
/*global parse, objectHelper*/
var UriTemplate = (function () {
"use strict";
function UriTemplate(templateText, expressions) {
function UriTemplate (templateText, expressions) {
this.templateText = templateText;
this.expressions = expressions;
objectHelper.deepFreeze(this);
}
UriTemplate.prototype.toString = function () {
......@@ -12,6 +13,7 @@ var UriTemplate = (function () {
};
UriTemplate.prototype.expand = function (variables) {
// this.expressions.map(function (expression) {return expression.expand(variables);}).join('');
var
index,
result = '';
......@@ -22,6 +24,5 @@ var UriTemplate = (function () {
};
UriTemplate.parse = parse;
return UriTemplate;
}());
......@@ -2,13 +2,12 @@
/*global pctEncoder, rfcCharHelper, isDefined, LiteralExpression, objectHelper*/
var VariableExpression = (function () {
"use strict";
// helper function if JSON is not available
function prettyPrint(value) {
function prettyPrint (value) {
return JSON ? JSON.stringify(value) : value;
}
function VariableExpression(templateText, operator, varspecs) {
function VariableExpression (templateText, operator, varspecs) {
this.templateText = templateText;
this.operator = operator;
this.varspecs = varspecs;
......@@ -18,7 +17,7 @@ var VariableExpression = (function () {
return this.templateText;
};
VariableExpression.prototype.expand = function expandExpression(variables) {
VariableExpression.prototype.expand = function (variables) {
var
result = '',
index,
......@@ -29,7 +28,7 @@ var VariableExpression = (function () {
operator = this.operator;
// callback to be used within array.reduce
function reduceUnexploded(result, currentValue, currentKey) {
function reduceUnexploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) {
if (result.length > 0) {
result += ',';
......@@ -42,7 +41,7 @@ var VariableExpression = (function () {
return result;
}
function reduceNamedExploded(result, currentValue, currentKey) {
function reduceNamedExploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) {
if (result.length > 0) {
result += operator.separator;
......@@ -53,7 +52,7 @@ var VariableExpression = (function () {
return result;
}
function reduceUnnamedExploded(result, currentValue, currentKey) {
function reduceUnnamedExploded (result, currentValue, currentKey) {
if (isDefined(currentValue)) {
if (result.length > 0) {
result += operator.separator;
......@@ -91,7 +90,7 @@ var VariableExpression = (function () {
}
result += '=';
}
if (varspec.maxLength && value.length > varspec.maxLength) {
if (varspec.maxLength !== null) {
value = value.substr(0, varspec.maxLength);
}
result += operator.encode(value);
......@@ -135,6 +134,5 @@ var VariableExpression = (function () {
return result;
};
return VariableExpression;
}());
......@@ -11,7 +11,7 @@
* @param object
* @return {Boolean}
*/
function isDefined(object) {
function isDefined (object) {
"use strict";
var
index,
......
/*jshint unused: false */
var objectHelper = (function () {
"use strict";
function isArray(value) {
function isArray (value) {
return Object.prototype.toString.apply(value) === '[object Array]';
}
// performs an array.reduce for objects
// TODO handling if initialValue is undefined
function objectReduce(object, callback, initialValue) {
function objectReduce (object, callback, initialValue) {
var
propertyName,
currentValue = initialValue;
......@@ -22,7 +21,7 @@ var objectHelper = (function () {
// performs an array.reduce, if reduce is not present (older browser...)
// TODO handling if initialValue is undefined
function arrayReduce(array, callback, initialValue) {
function arrayReduce (array, callback, initialValue) {
var
index,
currentValue = initialValue;
......@@ -32,12 +31,39 @@ var objectHelper = (function () {
return currentValue;
}
function reduce(arrayOrObject, callback, initialValue) {
function reduce (arrayOrObject, callback, initialValue) {
return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue);
}
function deepFreezeUsingObjectFreeze (object) {
if (typeof object !== "object" || object === null) {
return object;
}
Object.freeze(object);
var property, propertyName;
for (propertyName in object) {
if (object.hasOwnProperty(propertyName)) {
property = object[propertyName];
// be aware, arrays are 'object', too
if (typeof property === "object") {
deepFreeze(property);
}
}
}
return object;
}
function deepFreeze (object) {
if (typeof Object.freeze === 'function') {
return deepFreezeUsingObjectFreeze(object);
}
return object;
}
return {
isArray: isArray,
reduce: reduce
reduce: reduce,
deepFreeze: deepFreeze
};
}());
/*jshint unused:false */
/*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression */
/*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression*/
var parse = (function () {
"use strict";
function parseExpression(outerText) {
function parseExpression (outerText) {
var
text,
operator,
......@@ -14,12 +13,12 @@ var parse = (function () {
index,
chr = '';
function closeVarname() {
function closeVarname () {
varspec = {varname: text.substring(varnameStart, index), exploded: false, maxLength: null};
varnameStart = null;
}
function closeMaxLength() {
function closeMaxLength () {
if (maxLengthStart === index) {
throw new Error("after a ':' you have to specify the length. position = " + index);
}
......@@ -27,7 +26,7 @@ var parse = (function () {
maxLengthStart = null;
}
// remove outer {}
// remove outer braces
text = outerText.substr(1, outerText.length - 2);
// determine operator
......@@ -52,7 +51,13 @@ var parse = (function () {
closeVarname();
}
if (maxLengthStart !== null) {
if (index === maxLengthStart && chr === '0') {
throw new Error('A :prefix must not start with digit 0 -- see position ' + index);
}
if (charHelper.isDigit(chr)) {
if (index - maxLengthStart >= 4) {
throw new Error('A :prefix must max 4 digits -- see position ' + index);
}
continue;
}
closeMaxLength();
......@@ -96,7 +101,7 @@ var parse = (function () {
return new VariableExpression(outerText, operator, varspecs);
}
function parseTemplate(uriTemplateText) {
function parseTemplate (uriTemplateText) {
// assert filled string
var
index,
......
......@@ -2,7 +2,6 @@
/*global charHelper, unescape*/
var pctEncoder = (function () {
"use strict";
var utf8 = {
encode: function (chr) {
// see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html
......@@ -47,16 +46,28 @@ var pctEncoder = (function () {
return result;
}
/**
* Returns, whether the given text at start is in the form 'percent hex-digit hex-digit', like '%3F'
* @param text
* @param start
* @return {boolean|*|*}
*/
function isPercentDigitDigit (text, start) {
return text[start] === '%' && charHelper.isHexDigit(text[start + 1]) && charHelper.isHexDigit(text[start + 2]);
}
function parseHex2(text, start) {
/**
* Parses a hex number from start with length 2.
* @param text a string
* @param start the start index of the 2-digit hex number
* @return {Number}
*/
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.
* Returns whether or not the given char sequence is a correctly pct-encoded sequence.
* @param chr
* @return {boolean}
*/
......
......@@ -7,7 +7,7 @@
if (typeof module !== "undefined") {
module.exports = UriTemplate;
}
else if (typeof define !== "undefined") {
else if (typeof define === "function") {
define([],function() {
return UriTemplate;
});
......
......@@ -8,7 +8,7 @@ var rfcCharHelper = (function () {
* @param chr
* @return (Boolean)
*/
function isVarchar(chr) {
function isVarchar (chr) {
return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '_' || pctEncoder.isPctEncoded(chr);
}
......@@ -17,7 +17,7 @@ var rfcCharHelper = (function () {
* @param chr
* @return {Boolean}
*/
function isUnreserved(chr) {
function isUnreserved (chr) {
return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '-' || chr === '.' || chr === '_' || chr === '~';
}
......@@ -27,9 +27,9 @@ var rfcCharHelper = (function () {
* @param chr
* @return {Boolean}
*/
function isReserved(chr) {
function isReserved (chr) {
return chr === ':' || chr === '/' || chr === '?' || chr === '#' || chr === '[' || chr === ']' || chr === '@' || chr === '!' || chr === '$' || chr === '&' || chr === '(' ||
chr === ')' || chr === '*' || chr === '+' || chr === ',' || chr === ';' || chr === '=' || chr === "'" || chr === '%';
chr === ')' || chr === '*' || chr === '+' || chr === ',' || chr === ';' || chr === '=' || chr === "'";
}
return {
......
......@@ -5,9 +5,19 @@ module.exports = (function () {
UriTemplate = require('../../' + global.URI_TEMPLATE_FILE);
return {
'UriTemplate has a parse function': function (test) {
'UriTemplate has a static parse function': function (test) {
test.equal(typeof UriTemplate.parse, 'function');
test.done();
},
'UriTemplate has a expand function': function (test) {
test.equal(typeof UriTemplate.prototype.expand, 'function');
test.done();
},
'UriTemplate instances are frozen': function (test) {
var ut = new UriTemplate('text', []);
test.ok(Object.isFrozen(ut));
test.ok(Object.isFrozen(ut.expressions));
test.done();
}
};
}());
......@@ -5,27 +5,25 @@ 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) {
function log (text) {
if (NOISY) {
console.log(text);
}
}
function loadUriTemplate() {
function loadUriTemplate () {
var context = {module: {}};
sandbox(global.URI_TEMPLATE_FILE, context);
return context.module.exports;
}
function loadTestFile(testFileName) {
function loadTestFile (testFileName) {
return JSON.parse(fs.readFileSync(testFileName));
}
function assertMatches(test, template, variables, expected, chapterName, UriTemplate) {
function assertMatches (test, template, variables, expected, chapterName, UriTemplate) {
var
uriTemplate,
actual,
......@@ -34,13 +32,12 @@ module.exports = (function () {
uriTemplate = UriTemplate.parse(template);
}
catch (error) {
// if expected === false, the error was expected!
if (expected === false) {
log('ok. expected error found');
return;
}
log('error', error);
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + error);
console.log('error', error);
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error, when parsing: ' + error);
return;
}
test.ok(!!uriTemplate, 'uri template could not be parsed');
......@@ -51,11 +48,12 @@ module.exports = (function () {
return;
}
}
catch (exception) {
catch (error) {
if (expected === false) {
return;
}
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + JSON.stringify(exception, null, 4));
console.log('error', error);
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error, when expanding: ' + JSON.stringify(error, null, 4));
return;
}
if (expected.constructor === Array) {
......@@ -74,11 +72,11 @@ module.exports = (function () {
test.fail("actual: '" + actual + "', expected: one of " + JSON.stringify(expected) + ', chapter ' + chapterName + ', template ' + template);
}
else {
test.equal(actual, expected, 'actual: ' + actual + ', expected: ' + expected + ', template: ' + template);
test.equal(actual, expected, 'actual: "' + actual + '", expected: "' + expected + '", template: "' + template + '"');
}
}
function runTestFile(test, filename) {
function runTestFile (test, filename) {
var
tests,
chapterName,
......@@ -107,12 +105,15 @@ module.exports = (function () {
test.done();
}
var SPEC_HOME = 'uritemplate-test';
var SPEC_HOME = '../uritemplate-test';
return {
'spec examples': function (test) {
runTestFile(test, path.join(SPEC_HOME, 'spec-examples.json'));
},
'spec examples by section': function (test) {
runTestFile(test, path.join(SPEC_HOME, 'spec-examples-by-section.json'));
},
'extended tests': function (test) {
runTestFile(test, path.join(SPEC_HOME, 'extended-tests.json'));
},
......@@ -121,6 +122,10 @@ module.exports = (function () {
},
'own tests': function (test) {
runTestFile(test, 'own-testcases.json');
},
'testfile': function (test) {
test.ok(global.URI_TEMPLATE_FILE.substr(0, 3), 'tmp');
test.done();
}
};
}());
module.exports = (function () {
"use strict";
var
sandbox = require('nodeunit').utils.sandbox,
context = {};
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
sandbox('src/rfcCharHelper.js', context);
sandbox('src/encodingHelper.js', context);
sandbox('src/LiteralExpression.js', context);
var LiteralExpression = context.LiteralExpression;
return {
'LiteralExpression.encodeLiteral works as expected': function (test) {
test.equal(typeof LiteralExpression.encodeLiteral, 'function');
test.equal(LiteralExpression.encodeLiteral('a b'), 'a%20b');
test.equal(LiteralExpression.encodeLiteral('a%20b'), 'a%20b');
test.done();
}
};
}());
\ No newline at end of file
module.exports = (function () {
"use strict";
var context = {console: console};
var context = {};
require('nodeunit').utils.sandbox('src/objectHelper.js', context);
var objectHelper = context.objectHelper;
......@@ -29,6 +29,33 @@ module.exports = (function () {
}, -1);
test.equal(result, 17);
test.done();
},
'deepFreeze': {
'deepFreeze freezes an object': function (test) {
var object = {};
objectHelper.deepFreeze(object);
test.ok(Object.isFrozen(object));
test.done();
},
'deepFreeze freezes children': function (test) {
var object = {child: {}};
objectHelper.deepFreeze(object);
test.ok(Object.isFrozen(object));
test.ok(Object.isFrozen(object.child));
test.done();
},
'deepFreeze works with arrays': function (test) {
var object = {child: []};
objectHelper.deepFreeze(object);
test.ok(Object.isFrozen(object));
test.ok(Object.isFrozen(object.child));
test.done();
},
'deepFreeze works with null': function (test) {
objectHelper.deepFreeze(null);
test.done();
}
}
};
}());
......@@ -3,7 +3,7 @@ module.exports = (function () {
var
sandbox = require('nodeunit').utils.sandbox,
context = {console: console};
context = {};
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
var charHelper = context.charHelper;
......@@ -62,24 +62,7 @@ module.exports = (function () {
test.equal(pctEncoder.encodeCharacter('!'), '%21');
test.done();
}
},
'this and that': {
"ouml is correct encoded": function (test) {
test.equal(pctEncoder.encodeCharacter('ö'), '%C3%B6');
test.done();
},
'ouml is pctEncoded': function (test) {
test.ok(pctEncoder.isPctEncoded('%C3%B6'));
test.done();
},
'ouml is extracted from text': function (test) {
test.equal(pctEncoder.pctCharAt('zw%C3%B6lf', 2), '%C3%B6');
test.done();
}
}
};
}());
module.exports = (function () {
"use strict";
var
sandbox = require('nodeunit').utils.sandbox,
context = {};
sandbox('src/objectHelper.js', context);
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
sandbox('src/rfcCharHelper.js', context);
sandbox('src/encodingHelper.js', context);
sandbox('src/operators.js', context);
sandbox('src/isDefined.js', context);
sandbox('src/LiteralExpression.js', context);
sandbox('src/parse.js', context);
sandbox('src/VariableExpression.js', context);
sandbox('src/UriTemplate_.js', context);
var
operators = context.operators,
VariableExpression = context.VariableExpression,
UriTemplate = context.UriTemplate;
return {
'UriTemplate has a static parse function': function (test) {
test.equal(typeof UriTemplate.parse, 'function');
test.done();
},
'UriTemplate has a expand function': function (test) {
test.equal(typeof UriTemplate.prototype.expand, 'function');
test.done();
},
'UriTemplate instances are frozen': function (test) {
var ut = new UriTemplate('text', []);
test.ok(Object.isFrozen(ut));
test.ok(Object.isFrozen(ut.expressions));
test.done();
}
};
}());
......@@ -3,7 +3,7 @@ module.exports = (function () {
var
sandbox = require('nodeunit').utils.sandbox,
context = {console: console};
context = {};
sandbox('src/objectHelper.js', context);
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
......@@ -11,6 +11,7 @@ module.exports = (function () {
sandbox('src/encodingHelper.js', context);
sandbox('src/operators.js', context);
sandbox('src/isDefined.js', context);
sandbox('src/LiteralExpression.js', context);
sandbox('src/VariableExpression.js', context);
var operators = context.operators;
......@@ -39,6 +40,27 @@ module.exports = (function () {
]);
test.equal(ve.expand({empty: {}}), '');
test.done();
},
"double encode if ?": function (test) {
var ve = new VariableExpression("{?uri", operators.valueOf('?'), [
{varname: 'uri', exploded: false, maxLength: null}
]);
test.equal(ve.expand({uri: 'http://example.org/?ref=http%3A%2F%2Fother.org%2F'}), '?uri=http%3A%2F%2Fexample.org%2F%3Fref%3Dhttp%253A%252F%252Fother.org%252F');
test.done();
},
"not double encode if +": function (test) {
var ve = new VariableExpression("{+uri", operators.valueOf('+'), [
{varname: 'uri', exploded: false, maxLength: null}
]);
test.equal(ve.expand({uri: 'http://example.org/?ref=http%3A%2F%2Fother.org%2F'}), 'http://example.org/?ref=http%253A%252F%252Fother.org%252F');
test.done();
},
'maxLength is used': function (test) {
var ve = new VariableExpression("{one:1}", operators.valueOf(''), [
{varname: 'one', exploded: false, maxLength: 1}
]);
test.equal(ve.expand({one: 'two'}), 't');
test.done();
}
};
}());
\ No newline at end of file
Subproject commit b317372c613f1aab82f1e497146b2859144b9308
Subproject commit b6d42631481d87abc6bfb950d007212991ae25ff
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment