uritemplate
Advanced tools
Comparing version 0.2.4 to 0.3.1
@@ -1,1 +0,1 @@ | ||
(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}); | ||
(function(e){"use strict";function n(e){var t;if(null===e||void 0===e)return!1;if(r.isArray(e))return e.length>0;if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return!0;for(t in e)if(e.hasOwnProperty(t)&&n(e[t]))return!0;return!1}var t=function(){function e(e){this.options=e}return e.prototype.toString=function(){return JSON&&JSON.stringify?JSON.stringify(this.options):this.options},e}(),r=function(){function e(e){return"[object Array]"===Object.prototype.toString.apply(e)}function n(e,n){var t,r="",i=!0;for(t=0;e.length>t;t+=1)i?i=!1:r+=n,r+=e[t];return r}function t(e,n){for(var t=[],r=0;e.length>r;r+=1)t.push(n(e[r]));return t}function r(e,n){for(var t=[],r=0;e.length>r;r+=1)n(e[r])&&t.push(e[r]);return t}function i(e){if("object"!=typeof e||null===e)return e;Object.freeze(e);var n,t;for(t in e)e.hasOwnProperty(t)&&(n=e[t],"object"==typeof n&&o(n));return e}function o(e){return"function"==typeof Object.freeze?i(e):e}return{isArray:e,join:n,map:t,filter:r,deepFreeze:o}}(),i=function(){function e(e){return e>="a"&&"z">=e||e>="A"&&"Z">=e}function n(e){return e>="0"&&"9">=e}function t(e){return n(e)||e>="a"&&"f">=e||e>="A"&&"F">=e}return{isAlpha:e,isDigit:n,isHexDigit:t}}(),o=function(){function e(e){var n,t,r="",i=s.encode(e);for(t=0;i.length>t;t+=1)n=i.charCodeAt(t),r+="%"+n.toString(16).toUpperCase();return r}function n(e,n){return"%"===e[n]&&i.isHexDigit(e[n+1])&&i.isHexDigit(e[n+2])}function t(e,n){return parseInt(e.substr(n,2),16)}function r(e){if(!n(e,0))return!1;var r=t(e,1),i=s.numBytes(r);if(0===i)return!1;for(var o=1;i>o;o+=1)if(!n(e,3*o)||!s.isValidFollowingCharCode(t(e,3*o+1)))return!1;return!0}function o(e,r){var i=e[r];if(!n(e,r))return i;var o=t(e,r+1),a=s.numBytes(o);if(0===a)return i;for(var u=1;a>u;u+=1)if(!n(e,r+3*u)||!s.isValidFollowingCharCode(t(e,r+3*u+1)))return i;return e.substr(r,3*a)}var s={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:r,pctCharAt:o}}(),s=function(){function e(e){return i.isAlpha(e)||i.isDigit(e)||"_"===e||o.isPctEncoded(e)}function n(e){return i.isAlpha(e)||i.isDigit(e)||"-"===e||"."===e||"_"===e||"~"===e}function t(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:t}}(),a=function(){function e(e,n){var t,r="",i="";for(("number"==typeof e||"boolean"==typeof e)&&(e=""+e),t=0;e.length>t;t+=i.length)i=e.charAt(t),r+=s.isUnreserved(i)||n&&s.isReserved(i)?i:o.encodeCharacter(i);return r}function n(n){return e(n,!0)}function t(e,n){var t=o.pctCharAt(e,n);return t.length>1?t:s.isReserved(t)||s.isUnreserved(t)?t:o.encodeCharacter(t)}function r(e){var n,t="",r="";for(n=0;e.length>n;n+=r.length)r=o.pctCharAt(e,n),t+=r.length>1?r:s.isReserved(r)||s.isUnreserved(r)?r:o.encodeCharacter(r);return t}return{encode:e,encodePassReserved:n,encodeLiteral:r,encodeLiteralCharacter:t}}(),u=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?a.encodePassReserved:a.encode,toString:function(){return this.symbol}}}var n={};return e(""),e("+"),e("#"),e("."),e("/"),e(";"),e("?"),e("&"),{valueOf:function(e){return n[e]?n[e]:"=,!@|".indexOf(e)>=0?null:n[""]}}}(),f=function(){function e(e){this.literal=a.encodeLiteral(e)}return e.prototype.expand=function(){return this.literal},e.prototype.toString=e.prototype.expand,e}(),p=function(){function e(e){function n(){var n=e.substring(h,f);if(0===n.length)throw new t({expressionText:e,message:"a varname must be specified",position:f});c={varname:n,exploded:!1,maxLength:null},h=null}function r(){if(d===f)throw new t({expressionText:e,message:"after a ':' you have to specify the length",position:f});c.maxLength=parseInt(e.substring(d,f),10),d=null}var a,f,p=[],c=null,h=null,d=null,m="";for(a=function(n){var r=u.valueOf(n);if(null===r)throw new t({expressionText:e,message:"illegal use of reserved operator",position:f,operator:n});return r}(e.charAt(0)),f=a.symbol.length,h=f;e.length>f;f+=m.length){if(m=o.pctCharAt(e,f),null!==h){if("."===m){if(h===f)throw new t({expressionText:e,message:"a varname MUST NOT start with a dot",position:f});continue}if(s.isVarchar(m))continue;n()}if(null!==d){if(f===d&&"0"===m)throw new t({expressionText:e,message:"A :prefix must not start with digit 0",position:f});if(i.isDigit(m)){if(f-d>=4)throw new t({expressionText:e,message:"A :prefix must have max 4 digits",position:f});continue}r()}if(":"!==m)if("*"!==m){if(","!==m)throw new t({expressionText:e,message:"illegal character",character:m,position:f});p.push(c),c=null,h=f+1}else{if(null===c)throw new t({expressionText:e,message:"exploded without varspec",position:f});if(c.exploded)throw new t({expressionText:e,message:"exploded twice",position:f});if(c.maxLength)throw new t({expressionText:e,message:"an explode (*) MUST NOT follow to a prefix",position:f});c.exploded=!0}else{if(null!==c.maxLength)throw new t({expressionText:e,message:"only one :maxLength is allowed per varspec",position:f});if(c.exploded)throw new t({expressionText:e,message:"an exploeded varspec MUST NOT be varspeced",position:f});d=f+1}}return null!==h&&n(),null!==d&&r(),p.push(c),new l(e,a,p)}function n(n){var r,i,o=[],s=null,a=0;for(r=0;n.length>r;r+=1)if(i=n.charAt(r),null===a){if(null===s)throw Error("reached unreachable code");if("{"===i)throw new t({templateText:n,message:"brace already opened",position:r});if("}"===i){if(s+1===r)throw new t({templateText:n,message:"empty braces",position:s});try{o.push(e(n.substring(s+1,r)))}catch(u){if(u.prototype===t.prototype)throw new t({templateText:n,message:u.options.message,position:s+u.options.position,details:u.options});throw u}s=null,a=r+1}}else{if("}"===i)throw new t({templateText:n,message:"unopened brace closed",position:r});"{"===i&&(r>a&&o.push(new f(n.substring(a,r))),a=null,s=r)}if(null!==s)throw new t({templateText:n,message:"unclosed brace",position:s});return n.length>a&&o.push(new f(n.substr(a))),new c(n,o)}return n}(),l=function(){function e(e){return JSON?JSON.stringify(e):e}function t(e){if(!n(e))return!0;if(""===e)return!0;if(r.isArray(e))return 0===e.length;for(var t in e)if(e.hasOwnProperty(t))return!1;return!0}function i(e){var n,t=[];for(n in e)e.hasOwnProperty(n)&&t.push({name:n,value:e[n]});return t}function o(e,n,t){this.templateText=e,this.operator=n,this.varspecs=t}function s(e,n,t){var r="";if(t=""+t,n.named){if(r+=a.encodeLiteral(e.varname),""===t)return r+=n.ifEmpty;r+="="}return null!==e.maxLength&&(t=t.substr(0,e.maxLength)),r+=n.encode(t)}function u(e){return n(e.value)}function f(e,o,s){var f=[],p="";if(o.named){if(p+=a.encodeLiteral(e.varname),t(s))return p+=o.ifEmpty;p+="="}return r.isArray(s)?(f=s,f=r.filter(f,n),f=r.map(f,o.encode),p+=r.join(f,",")):(f=i(s),f=r.filter(f,u),f=r.map(f,function(e){return o.encode(e.name)+","+o.encode(e.value)}),p+=r.join(f,",")),p}function p(e,o,s){var f=r.isArray(s),p=[];return f?(p=s,p=r.filter(p,n),p=r.map(p,function(n){var r=a.encodeLiteral(e.varname);return r+=t(n)?o.ifEmpty:"="+o.encode(n)})):(p=i(s),p=r.filter(p,u),p=r.map(p,function(e){var n=a.encodeLiteral(e.name);return n+=t(e.value)?o.ifEmpty:"="+o.encode(e.value)})),r.join(p,o.separator)}function l(e,t){var o=[],s="";return r.isArray(t)?(o=t,o=r.filter(o,n),o=r.map(o,e.encode),s+=r.join(o,e.separator)):(o=i(t),o=r.filter(o,function(e){return n(e.value)}),o=r.map(o,function(n){return e.encode(n.name)+"="+e.encode(n.value)}),s+=r.join(o,e.separator)),s}return o.prototype.toString=function(){return this.templateText},o.prototype.expand=function(i){var o,a,u,c,h=[],d=!1,m=this.operator;for(o=0;this.varspecs.length>o;o+=1)if(a=this.varspecs[o],u=i[a.varname],null!==u&&void 0!==u)if(a.exploded&&(d=!0),c=r.isArray(u),"string"==typeof u||"number"==typeof u||"boolean"==typeof u)h.push(s(a,m,u));else{if(a.maxLength&&n(u))throw Error("Prefix modifiers are not applicable to variables that have composite values. You tried to expand "+this+" with "+e(u));a.exploded?n(u)&&(m.named?h.push(p(a,m,u)):h.push(l(m,u))):(m.named||!t(u))&&h.push(f(a,m,u))}return 0===h.length?"":m.first+r.join(h,m.separator)},o}(),c=function(){function e(e,n){this.templateText=e,this.expressions=n,r.deepFreeze(this)}return e.prototype.toString=function(){return this.templateText},e.prototype.expand=function(e){var n,t="";for(n=0;this.expressions.length>n;n+=1)t+=this.expressions[n].expand(e);return t},e.parse=p,e.UriTemplateError=t,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}); |
@@ -11,2 +11,21 @@ /*jshint */ | ||
"use strict"; | ||
var UriTemplateError = (function () { | ||
function UriTemplateError (options) { | ||
this.options = options; | ||
} | ||
UriTemplateError.prototype.toString = function () { | ||
if (JSON && JSON.stringify) { | ||
return JSON.stringify(this.options); | ||
} | ||
else { | ||
return this.options; | ||
} | ||
}; | ||
return UriTemplateError; | ||
}()); | ||
var objectHelper = (function () { | ||
@@ -17,30 +36,39 @@ function isArray (value) { | ||
// performs an array.reduce for objects | ||
// TODO handling if initialValue is undefined | ||
function objectReduce (object, callback, initialValue) { | ||
function join (arr, separator) { | ||
var | ||
propertyName, | ||
currentValue = initialValue; | ||
for (propertyName in object) { | ||
if (object.hasOwnProperty(propertyName)) { | ||
currentValue = callback(currentValue, object[propertyName], propertyName, object); | ||
result = '', | ||
first = true, | ||
index; | ||
for (index = 0; index < arr.length; index += 1) { | ||
if (first) { | ||
first = false; | ||
} | ||
else { | ||
result += separator; | ||
} | ||
result += arr[index]; | ||
} | ||
return currentValue; | ||
return result; | ||
} | ||
// performs an array.reduce, if reduce is not present (older browser...) | ||
// TODO handling if initialValue is undefined | ||
function arrayReduce (array, callback, initialValue) { | ||
function map (arr, mapper) { | ||
var | ||
index, | ||
currentValue = initialValue; | ||
for (index = 0; index < array.length; index += 1) { | ||
currentValue = callback(currentValue, array[index], index, array); | ||
result = [], | ||
index = 0; | ||
for (; index < arr.length; index += 1) { | ||
result.push(mapper(arr[index])); | ||
} | ||
return currentValue; | ||
return result; | ||
} | ||
function reduce (arrayOrObject, callback, initialValue) { | ||
return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue); | ||
function filter (arr, predicate) { | ||
var | ||
result = [], | ||
index = 0; | ||
for (; index < arr.length; index += 1) { | ||
if (predicate(arr[index])) { | ||
result.push(arr[index]); | ||
} | ||
} | ||
return result; | ||
} | ||
@@ -76,3 +104,5 @@ | ||
isArray: isArray, | ||
reduce: reduce, | ||
join: join, | ||
map: map, | ||
filter: filter, | ||
deepFreeze: deepFreeze | ||
@@ -84,11 +114,11 @@ }; | ||
function isAlpha(chr) { | ||
function isAlpha (chr) { | ||
return (chr >= 'a' && chr <= 'z') || ((chr >= 'A' && chr <= 'Z')); | ||
} | ||
function isDigit(chr) { | ||
function isDigit (chr) { | ||
return chr >= '0' && chr <= '9'; | ||
} | ||
function isHexDigit(chr) { | ||
function isHexDigit (chr) { | ||
return isDigit(chr) || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F'); | ||
@@ -285,5 +315,34 @@ } | ||
function encodeLiteralCharacter (literal, index) { | ||
var chr = pctEncoder.pctCharAt(literal, index); | ||
if (chr.length > 1) { | ||
return chr; | ||
} | ||
else { | ||
return rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr); | ||
} | ||
} | ||
function encodeLiteral (literal) { | ||
var | ||
result = '', | ||
index, | ||
chr = ''; | ||
for (index = 0; index < literal.length; index += chr.length) { | ||
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; | ||
} | ||
return { | ||
encode: encode, | ||
encodePassReserved: encodePassReserved | ||
encodePassReserved: encodePassReserved, | ||
encodeLiteral: encodeLiteral, | ||
encodeLiteralCharacter: encodeLiteralCharacter | ||
}; | ||
@@ -300,3 +359,3 @@ | ||
function create(symbol) { | ||
function create (symbol) { | ||
bySymbol[symbol] = { | ||
@@ -323,11 +382,13 @@ symbol: symbol, | ||
create('&'); | ||
return {valueOf: function (chr) { | ||
if (bySymbol[chr]) { | ||
return bySymbol[chr]; | ||
return { | ||
valueOf: function (chr) { | ||
if (bySymbol[chr]) { | ||
return bySymbol[chr]; | ||
} | ||
if ("=,!@|".indexOf(chr) >= 0) { | ||
return null; | ||
} | ||
return bySymbol['']; | ||
} | ||
if ("=,!@|".indexOf(chr) >= 0) { | ||
throw new Error('Illegal use of reserved operator "' + chr + '"'); | ||
} | ||
return bySymbol['']; | ||
}}; | ||
}; | ||
}()); | ||
@@ -341,4 +402,4 @@ | ||
* * the empty string is defined | ||
* * an array ("list") is defined, if it contains at least one defined element | ||
* * an object ("map") is defined, if it contains at least one defined property | ||
* * an array ("list") is defined, if it is not empty (even if all elements are not defined) | ||
* * an object ("map") is defined, if it contains at least one property with defined value | ||
* @param object | ||
@@ -349,3 +410,2 @@ * @return {Boolean} | ||
var | ||
index, | ||
propertyName; | ||
@@ -356,8 +416,4 @@ if (object === null || object === undefined) { | ||
if (objectHelper.isArray(object)) { | ||
for (index = 0; index < object.length; index += 1) { | ||
if (isDefined(object[index])) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
// Section 2.3: A variable defined as a list value is considered undefined if the list contains zero members | ||
return object.length > 0; | ||
} | ||
@@ -379,24 +435,5 @@ if (typeof object === "string" || typeof object === "number" || typeof object === "boolean") { | ||
function LiteralExpression (literal) { | ||
this.literal = LiteralExpression.encodeLiteral(literal); | ||
this.literal = encodingHelper.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 > 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; | ||
}; | ||
LiteralExpression.prototype.expand = function () { | ||
@@ -412,5 +449,5 @@ return this.literal; | ||
var parse = (function () { | ||
function parseExpression (outerText) { | ||
function parseExpression (expressionText) { | ||
var | ||
text, | ||
operator, | ||
@@ -425,3 +462,7 @@ varspecs = [], | ||
function closeVarname () { | ||
varspec = {varname: text.substring(varnameStart, index), exploded: false, maxLength: null}; | ||
var varname = expressionText.substring(varnameStart, index); | ||
if (varname.length === 0) { | ||
throw new UriTemplateError({expressionText: expressionText, message: "a varname must be specified", position: index}); | ||
} | ||
varspec = {varname: varname, exploded: false, maxLength: null}; | ||
varnameStart = null; | ||
@@ -432,18 +473,22 @@ } | ||
if (maxLengthStart === index) { | ||
throw new Error("after a ':' you have to specify the length. position = " + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "after a ':' you have to specify the length", position: index}); | ||
} | ||
varspec.maxLength = parseInt(text.substring(maxLengthStart, index), 10); | ||
varspec.maxLength = parseInt(expressionText.substring(maxLengthStart, index), 10); | ||
maxLengthStart = null; | ||
} | ||
// remove outer braces | ||
text = outerText.substr(1, outerText.length - 2); | ||
operator = (function (operatorText) { | ||
var op = operators.valueOf(operatorText); | ||
if (op === null) { | ||
throw new UriTemplateError({expressionText: expressionText, message: "illegal use of reserved operator", position: index, operator: operatorText}); | ||
} | ||
return op; | ||
}(expressionText.charAt(0))); | ||
index = operator.symbol.length; | ||
// determine operator | ||
operator = operators.valueOf(text.charAt(0)); | ||
index = (operator.symbol === '') ? 0 : 1; | ||
varnameStart = index; | ||
for (; index < text.length; index += chr.length) { | ||
chr = pctEncoder.pctCharAt(text, index); | ||
for (; index < expressionText.length; index += chr.length) { | ||
chr = pctEncoder.pctCharAt(expressionText, index); | ||
if (varnameStart !== null) { | ||
@@ -454,3 +499,3 @@ // the spec says: varname = varchar *( ["."] varchar ) | ||
if (varnameStart === index) { | ||
throw new Error('a varname MUST NOT start with a dot -- see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "a varname MUST NOT start with a dot", position: index}); | ||
} | ||
@@ -466,7 +511,7 @@ continue; | ||
if (index === maxLengthStart && chr === '0') { | ||
throw new Error('A :prefix must not start with digit 0 -- see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must not start with digit 0", position: index}); | ||
} | ||
if (charHelper.isDigit(chr)) { | ||
if (index - maxLengthStart >= 4) { | ||
throw new Error('A :prefix must max 4 digits -- see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must have max 4 digits", position: index}); | ||
} | ||
@@ -479,4 +524,7 @@ continue; | ||
if (varspec.maxLength !== null) { | ||
throw new Error('only one :maxLength is allowed per varspec at position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "only one :maxLength is allowed per varspec", position: index}); | ||
} | ||
if (varspec.exploded) { | ||
throw new UriTemplateError({expressionText: expressionText, message: "an exploeded varspec MUST NOT be varspeced", position: index}); | ||
} | ||
maxLengthStart = index + 1; | ||
@@ -487,9 +535,9 @@ continue; | ||
if (varspec === null) { | ||
throw new Error('explode exploded at position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "exploded without varspec", position: index}); | ||
} | ||
if (varspec.exploded) { | ||
throw new Error('explode exploded twice at position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "exploded twice", position: index}); | ||
} | ||
if (varspec.maxLength) { | ||
throw new Error('an explode (*) MUST NOT follow to a prefix, see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "an explode (*) MUST NOT follow to a prefix", position: index}); | ||
} | ||
@@ -506,3 +554,3 @@ varspec.exploded = true; | ||
} | ||
throw new Error("illegal character '" + chr + "' at position " + index + ' of "' + text + '"'); | ||
throw new UriTemplateError({expressionText: expressionText, message: "illegal character", character: chr, position: index}); | ||
} // for chr | ||
@@ -516,6 +564,6 @@ if (varnameStart !== null) { | ||
varspecs.push(varspec); | ||
return new VariableExpression(outerText, operator, varspecs); | ||
return new VariableExpression(expressionText, operator, varspecs); | ||
} | ||
function parseTemplate (uriTemplateText) { | ||
function parse (uriTemplateText) { | ||
// assert filled string | ||
@@ -532,3 +580,3 @@ var | ||
if (chr === '}') { | ||
throw new Error('brace was closed in position ' + index + " but never opened"); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "unopened brace closed", position: index}); | ||
} | ||
@@ -548,9 +596,17 @@ if (chr === '{') { | ||
if (chr === '{') { | ||
throw new Error('brace was opened in position ' + braceOpenIndex + " and cannot be reopened in position " + index); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "brace already opened", position: index}); | ||
} | ||
if (chr === '}') { | ||
if (braceOpenIndex + 1 === index) { | ||
throw new Error("empty braces on position " + braceOpenIndex); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex}); | ||
} | ||
expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex, index + 1))); | ||
try { | ||
expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index))); | ||
} | ||
catch (error) { | ||
if (error.prototype === UriTemplateError.prototype) { | ||
throw new UriTemplateError({templateText: uriTemplateText, message: error.options.message, position: braceOpenIndex + error.options.position, details: error.options}); | ||
} | ||
throw error; | ||
} | ||
braceOpenIndex = null; | ||
@@ -564,3 +620,3 @@ literalStart = index + 1; | ||
if (braceOpenIndex !== null) { | ||
throw new Error("brace was opened on position " + braceOpenIndex + ", but never closed"); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex}); | ||
} | ||
@@ -573,3 +629,3 @@ if (literalStart < uriTemplateText.length) { | ||
return parseTemplate; | ||
return parse; | ||
}()); | ||
@@ -583,2 +639,32 @@ | ||
function isEmpty (value) { | ||
if (!isDefined(value)) { | ||
return true; | ||
} | ||
if (value === '') { | ||
return true; | ||
} | ||
if (objectHelper.isArray(value)) { | ||
return value.length === 0; | ||
} | ||
for (var propertyName in value) { | ||
if (value.hasOwnProperty(propertyName)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function propertyArray (object) { | ||
var | ||
result = [], | ||
propertyName; | ||
for (propertyName in object) { | ||
if (object.hasOwnProperty(propertyName)) { | ||
result.push({name: propertyName, value: object[propertyName]}); | ||
} | ||
} | ||
return result; | ||
} | ||
function VariableExpression (templateText, operator, varspecs) { | ||
@@ -594,50 +680,122 @@ this.templateText = templateText; | ||
VariableExpression.prototype.expand = function (variables) { | ||
var | ||
result = '', | ||
index, | ||
varspec, | ||
value, | ||
valueIsArr, | ||
isFirstVarspec = true, | ||
operator = this.operator; | ||
// callback to be used within array.reduce | ||
function reduceUnexploded (result, currentValue, currentKey) { | ||
if (isDefined(currentValue)) { | ||
if (result.length > 0) { | ||
result += ','; | ||
} | ||
if (!valueIsArr) { | ||
result += operator.encode(currentKey) + ','; | ||
} | ||
result += operator.encode(currentValue); | ||
function expandSimpleValue(varspec, operator, value) { | ||
var result = ''; | ||
value = value.toString(); | ||
if (operator.named) { | ||
result += encodingHelper.encodeLiteral(varspec.varname); | ||
if (value === '') { | ||
result += operator.ifEmpty; | ||
return result; | ||
} | ||
return result; | ||
result += '='; | ||
} | ||
if (varspec.maxLength !== null) { | ||
value = value.substr(0, varspec.maxLength); | ||
} | ||
result += operator.encode(value); | ||
return result; | ||
} | ||
function reduceNamedExploded (result, currentValue, currentKey) { | ||
if (isDefined(currentValue)) { | ||
if (result.length > 0) { | ||
result += operator.separator; | ||
} | ||
result += (valueIsArr) ? LiteralExpression.encodeLiteral(varspec.varname) : operator.encode(currentKey); | ||
result += '=' + operator.encode(currentValue); | ||
function valueDefined (nameValue) { | ||
return isDefined(nameValue.value); | ||
} | ||
function expandNotExploded(varspec, operator, value) { | ||
var | ||
arr = [], | ||
result = ''; | ||
if (operator.named) { | ||
result += encodingHelper.encodeLiteral(varspec.varname); | ||
if (isEmpty(value)) { | ||
result += operator.ifEmpty; | ||
return result; | ||
} | ||
return result; | ||
result += '='; | ||
} | ||
if (objectHelper.isArray(value)) { | ||
arr = value; | ||
arr = objectHelper.filter(arr, isDefined); | ||
arr = objectHelper.map(arr, operator.encode); | ||
result += objectHelper.join(arr, ','); | ||
} | ||
else { | ||
arr = propertyArray(value); | ||
arr = objectHelper.filter(arr, valueDefined); | ||
arr = objectHelper.map(arr, function (nameValue) { | ||
return operator.encode(nameValue.name) + ',' + operator.encode(nameValue.value); | ||
}); | ||
result += objectHelper.join(arr, ','); | ||
} | ||
return result; | ||
} | ||
function reduceUnnamedExploded (result, currentValue, currentKey) { | ||
if (isDefined(currentValue)) { | ||
if (result.length > 0) { | ||
result += operator.separator; | ||
function expandExplodedNamed (varspec, operator, value) { | ||
var | ||
isArray = objectHelper.isArray(value), | ||
arr = []; | ||
if (isArray) { | ||
arr = value; | ||
arr = objectHelper.filter(arr, isDefined); | ||
arr = objectHelper.map(arr, function (listElement) { | ||
var tmp = encodingHelper.encodeLiteral(varspec.varname); | ||
if (isEmpty(listElement)) { | ||
tmp += operator.ifEmpty; | ||
} | ||
if (!valueIsArr) { | ||
result += operator.encode(currentKey) + '='; | ||
else { | ||
tmp += '=' + operator.encode(listElement); | ||
} | ||
result += operator.encode(currentValue); | ||
} | ||
return result; | ||
return tmp; | ||
}); | ||
} | ||
else { | ||
arr = propertyArray(value); | ||
arr = objectHelper.filter(arr, valueDefined); | ||
arr = objectHelper.map(arr, function (nameValue) { | ||
var tmp = encodingHelper.encodeLiteral(nameValue.name); | ||
if (isEmpty(nameValue.value)) { | ||
tmp += operator.ifEmpty; | ||
} | ||
else { | ||
tmp += '=' + operator.encode(nameValue.value); | ||
} | ||
return tmp; | ||
}); | ||
} | ||
return objectHelper.join(arr, operator.separator); | ||
} | ||
function expandExplodedUnnamed (operator, value) { | ||
var | ||
arr = [], | ||
result = ''; | ||
if (objectHelper.isArray(value)) { | ||
arr = value; | ||
arr = objectHelper.filter(arr, isDefined); | ||
arr = objectHelper.map(arr, operator.encode); | ||
result += objectHelper.join(arr, operator.separator); | ||
} | ||
else { | ||
arr = propertyArray(value); | ||
arr = objectHelper.filter(arr, function (nameValue) { | ||
return isDefined(nameValue.value); | ||
}); | ||
arr = objectHelper.map(arr, function (nameValue) { | ||
return operator.encode(nameValue.name) + '=' + operator.encode(nameValue.value); | ||
}); | ||
result += objectHelper.join(arr, operator.separator); | ||
} | ||
return result; | ||
} | ||
VariableExpression.prototype.expand = function (variables) { | ||
var | ||
expanded = [], | ||
index, | ||
varspec, | ||
value, | ||
valueIsArr, | ||
oneExploded = false, | ||
operator = this.operator; | ||
// expand each varspec and join with operator's separator | ||
@@ -647,29 +805,15 @@ for (index = 0; index < this.varspecs.length; index += 1) { | ||
value = variables[varspec.varname]; | ||
if (!isDefined(value)) { | ||
continue; | ||
// if (!isDefined(value)) { | ||
// if (variables.hasOwnProperty(varspec.name)) { | ||
if (value === null || value === undefined) { | ||
continue; | ||
} | ||
if (isFirstVarspec) { | ||
result += operator.first; | ||
isFirstVarspec = false; | ||
if (varspec.exploded) { | ||
oneExploded = true; | ||
} | ||
else { | ||
result += operator.separator; | ||
} | ||
valueIsArr = objectHelper.isArray(value); | ||
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { | ||
value = value.toString(); | ||
if (operator.named) { | ||
result += LiteralExpression.encodeLiteral(varspec.varname); | ||
if (value === '') { | ||
result += operator.ifEmpty; | ||
continue; | ||
} | ||
result += '='; | ||
} | ||
if (varspec.maxLength !== null) { | ||
value = value.substr(0, varspec.maxLength); | ||
} | ||
result += operator.encode(value); | ||
expanded.push(expandSimpleValue(varspec, operator, value)); | ||
} | ||
else if (varspec.maxLength) { | ||
else if (varspec.maxLength && isDefined(value)) { | ||
// 2.4.1 of the spec says: "Prefix modifiers are not applicable to variables that have composite values." | ||
@@ -679,34 +823,22 @@ throw new Error('Prefix modifiers are not applicable to variables that have composite values. You tried to expand ' + this + " with " + prettyPrint(value)); | ||
else if (!varspec.exploded) { | ||
if (operator.named || !isEmpty(value)) { | ||
expanded.push(expandNotExploded(varspec, operator, value)); | ||
} | ||
} | ||
else if (isDefined(value)) { | ||
if (operator.named) { | ||
result += LiteralExpression.encodeLiteral(varspec.varname); | ||
if (!isDefined(value)) { | ||
result += operator.ifEmpty; | ||
continue; | ||
} | ||
result += '='; | ||
expanded.push(expandExplodedNamed(varspec, operator, value)); | ||
} | ||
result += objectHelper.reduce(value, reduceUnexploded, ''); | ||
else { | ||
expanded.push(expandExplodedUnnamed(operator, value)); | ||
} | ||
} | ||
else { | ||
// exploded and not string | ||
result += objectHelper.reduce(value, operator.named ? reduceNamedExploded : reduceUnnamedExploded, ''); | ||
} | ||
} | ||
if (isFirstVarspec) { | ||
// so no varspecs produced output. | ||
var oneExploded = false; | ||
for (index = 0; index < this.varspecs.length; index += 1) { | ||
if (this.varspecs[index].exploded) { | ||
oneExploded = true; | ||
break; | ||
} | ||
} | ||
if (operator.named && !oneExploded) { | ||
result += operator.symbol; | ||
result += varspec.varname + operator.ifEmpty; | ||
} | ||
if (expanded.length === 0) { | ||
return ""; | ||
} | ||
return result; | ||
else { | ||
return operator.first + objectHelper.join(expanded, operator.separator); | ||
} | ||
}; | ||
@@ -740,2 +872,3 @@ | ||
UriTemplate.parse = parse; | ||
UriTemplate.UriTemplateError = UriTemplateError; | ||
return UriTemplate; | ||
@@ -742,0 +875,0 @@ }()); |
@@ -35,2 +35,3 @@ /*jshint */ | ||
// cannot use the fileList, because the order matters | ||
path.join(SRC_HOME, 'UriTemplateError.js'), | ||
path.join(SRC_HOME, 'objectHelper.js'), | ||
@@ -73,3 +74,3 @@ path.join(SRC_HOME, 'charHelper.js'), | ||
function closeTask (err) { | ||
function closeAsyncJakeTask (err) { | ||
if (err) { | ||
@@ -90,3 +91,9 @@ fail(JSON.stringify(err, null, 4)); | ||
} | ||
async.forEach([TMP_UNTESTED_UNCOMPRESSED, TMP_UNTESTED_COMPRESSED, TARGET_UNCOMPRESSED, TARGET_COMPRESSED], unlinkWhenExists, closeTask); | ||
async.forEach([ | ||
TMP_UNTESTED_UNCOMPRESSED, | ||
TMP_UNTESTED_COMPRESSED, | ||
TARGET_UNCOMPRESSED, | ||
TARGET_COMPRESSED], | ||
unlinkWhenExists, | ||
closeAsyncJakeTask); | ||
}, ASYNC); | ||
@@ -126,3 +133,3 @@ | ||
} | ||
], closeTask); | ||
], closeAsyncJakeTask); | ||
}, ASYNC); | ||
@@ -146,7 +153,6 @@ | ||
} | ||
], closeTask); | ||
], closeAsyncJakeTask); | ||
}, ASYNC); | ||
// for short test only | ||
desc('unit tests (without jshint)'); | ||
desc('unit tests (without jshint, only a shortcut for development)'); | ||
task('unit', [], function () { | ||
@@ -156,8 +162,10 @@ nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, complete); | ||
desc('build'); | ||
desc('build and test all artifacts'); | ||
task('build', [TARGET_COMPRESSED], function () { | ||
jake.logger.log('done.'); | ||
}); | ||
desc('default -- called, if you call jake without parameters'); | ||
task('default', ['clean', 'build']); | ||
}()); |
@@ -22,3 +22,3 @@ { | ||
}, | ||
"Prefix modifiers": { | ||
"Prefix modifiers and exploded": { | ||
"level": 4, | ||
@@ -44,6 +44,32 @@ "variables": { | ||
["{emptyArr:1}", ""], | ||
["{filledArr:1}", false] | ||
["{filledArr:1}", false], | ||
["{text:1:1}", false], | ||
["{?*}", false], | ||
["{?:1}", false], | ||
["{?a,:1}", false], | ||
["{*}", false], | ||
["{text**}", false], | ||
["{text:1*}", false], | ||
["{text*:1}", false] | ||
] | ||
}, | ||
"Prefix modifiers, next generation": { | ||
"level": 4, | ||
"variables": { | ||
"bool": false, | ||
"int": 123, | ||
"float": 123.4, | ||
"exp": 1.2e+3, | ||
"null": null | ||
}, | ||
"testcases": [ | ||
["{bool:1}", "f"], | ||
["{int:1}", "1"], | ||
["{float:1}", "1"], | ||
["{exp:1}", "1"], | ||
["{null:1}", ""], | ||
["{null:1}", ""] | ||
] | ||
} | ||
} |
@@ -36,3 +36,3 @@ { | ||
], | ||
"version": "0.2.4", | ||
"version": "0.3.1", | ||
"readmeFilename": "README.md", | ||
@@ -39,0 +39,0 @@ "gitHead": "901b85201a821427dfb4591b56aea3a70d45c67c", |
@@ -65,2 +65,4 @@ URI Template JS | ||
------------- | ||
* 0.3.1 fixed https://github.com/fxa/uritemplate-js/issues/10 thank you, Paul-Martin! | ||
* 0.3.0 introduced UriTemplateError as exception, so the interface changed from string to UriTemplateError (as the rfc suggested) | ||
* 0.2.4 fixed double encoding according [RubenVerborgh] and some Prefix modifiers bugs | ||
@@ -67,0 +69,0 @@ * 0.2.3 fixed bug with empty objects ('{?empty}' with '{empty:{}}' shall expand to '?empty=') |
@@ -6,11 +6,11 @@ /*jshint unused: false */ | ||
function isAlpha(chr) { | ||
function isAlpha (chr) { | ||
return (chr >= 'a' && chr <= 'z') || ((chr >= 'A' && chr <= 'Z')); | ||
} | ||
function isDigit(chr) { | ||
function isDigit (chr) { | ||
return chr >= '0' && chr <= '9'; | ||
} | ||
function isHexDigit(chr) { | ||
function isHexDigit (chr) { | ||
return isDigit(chr) || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F'); | ||
@@ -17,0 +17,0 @@ } |
@@ -28,7 +28,36 @@ /*jshint unused: false */ | ||
function encodeLiteralCharacter (literal, index) { | ||
var chr = pctEncoder.pctCharAt(literal, index); | ||
if (chr.length > 1) { | ||
return chr; | ||
} | ||
else { | ||
return rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr); | ||
} | ||
} | ||
function encodeLiteral (literal) { | ||
var | ||
result = '', | ||
index, | ||
chr = ''; | ||
for (index = 0; index < literal.length; index += chr.length) { | ||
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; | ||
} | ||
return { | ||
encode: encode, | ||
encodePassReserved: encodePassReserved | ||
encodePassReserved: encodePassReserved, | ||
encodeLiteral: encodeLiteral, | ||
encodeLiteralCharacter: encodeLiteralCharacter | ||
}; | ||
}()); |
@@ -9,4 +9,4 @@ /*jshint unused:false */ | ||
* * the empty string is defined | ||
* * an array ("list") is defined, if it contains at least one defined element | ||
* * an object ("map") is defined, if it contains at least one defined property | ||
* * an array ("list") is defined, if it is not empty (even if all elements are not defined) | ||
* * an object ("map") is defined, if it contains at least one property with defined value | ||
* @param object | ||
@@ -18,3 +18,2 @@ * @return {Boolean} | ||
var | ||
index, | ||
propertyName; | ||
@@ -25,8 +24,4 @@ if (object === null || object === undefined) { | ||
if (objectHelper.isArray(object)) { | ||
for (index = 0; index < object.length; index += 1) { | ||
if (isDefined(object[index])) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
// Section 2.3: A variable defined as a list value is considered undefined if the list contains zero members | ||
return object.length > 0; | ||
} | ||
@@ -33,0 +28,0 @@ if (typeof object === "string" || typeof object === "number" || typeof object === "boolean") { |
/*jshint unused:false */ | ||
/*global pctEncoder, rfcCharHelper*/ | ||
/*global pctEncoder, rfcCharHelper, encodingHelper*/ | ||
var LiteralExpression = (function () { | ||
"use strict"; | ||
function LiteralExpression (literal) { | ||
this.literal = LiteralExpression.encodeLiteral(literal); | ||
this.literal = encodingHelper.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 > 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; | ||
}; | ||
LiteralExpression.prototype.expand = function () { | ||
@@ -29,0 +10,0 @@ return this.literal; |
@@ -8,30 +8,39 @@ /*jshint unused: false */ | ||
// performs an array.reduce for objects | ||
// TODO handling if initialValue is undefined | ||
function objectReduce (object, callback, initialValue) { | ||
function join (arr, separator) { | ||
var | ||
propertyName, | ||
currentValue = initialValue; | ||
for (propertyName in object) { | ||
if (object.hasOwnProperty(propertyName)) { | ||
currentValue = callback(currentValue, object[propertyName], propertyName, object); | ||
result = '', | ||
first = true, | ||
index; | ||
for (index = 0; index < arr.length; index += 1) { | ||
if (first) { | ||
first = false; | ||
} | ||
else { | ||
result += separator; | ||
} | ||
result += arr[index]; | ||
} | ||
return currentValue; | ||
return result; | ||
} | ||
// performs an array.reduce, if reduce is not present (older browser...) | ||
// TODO handling if initialValue is undefined | ||
function arrayReduce (array, callback, initialValue) { | ||
function map (arr, mapper) { | ||
var | ||
index, | ||
currentValue = initialValue; | ||
for (index = 0; index < array.length; index += 1) { | ||
currentValue = callback(currentValue, array[index], index, array); | ||
result = [], | ||
index = 0; | ||
for (; index < arr.length; index += 1) { | ||
result.push(mapper(arr[index])); | ||
} | ||
return currentValue; | ||
return result; | ||
} | ||
function reduce (arrayOrObject, callback, initialValue) { | ||
return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue); | ||
function filter (arr, predicate) { | ||
var | ||
result = [], | ||
index = 0; | ||
for (; index < arr.length; index += 1) { | ||
if (predicate(arr[index])) { | ||
result.push(arr[index]); | ||
} | ||
} | ||
return result; | ||
} | ||
@@ -67,5 +76,7 @@ | ||
isArray: isArray, | ||
reduce: reduce, | ||
join: join, | ||
map: map, | ||
filter: filter, | ||
deepFreeze: deepFreeze | ||
}; | ||
}()); |
@@ -11,3 +11,3 @@ /*jshint unused: false */ | ||
function create(symbol) { | ||
function create (symbol) { | ||
bySymbol[symbol] = { | ||
@@ -34,11 +34,13 @@ symbol: symbol, | ||
create('&'); | ||
return {valueOf: function (chr) { | ||
if (bySymbol[chr]) { | ||
return bySymbol[chr]; | ||
return { | ||
valueOf: function (chr) { | ||
if (bySymbol[chr]) { | ||
return bySymbol[chr]; | ||
} | ||
if ("=,!@|".indexOf(chr) >= 0) { | ||
return null; | ||
} | ||
return bySymbol['']; | ||
} | ||
if ("=,!@|".indexOf(chr) >= 0) { | ||
throw new Error('Illegal use of reserved operator "' + chr + '"'); | ||
} | ||
return bySymbol['']; | ||
}}; | ||
}; | ||
}()); |
/*jshint unused:false */ | ||
/*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression*/ | ||
/*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression, UriTemplateError*/ | ||
var parse = (function () { | ||
"use strict"; | ||
function parseExpression (outerText) { | ||
function parseExpression (expressionText) { | ||
var | ||
text, | ||
operator, | ||
@@ -17,3 +17,7 @@ varspecs = [], | ||
function closeVarname () { | ||
varspec = {varname: text.substring(varnameStart, index), exploded: false, maxLength: null}; | ||
var varname = expressionText.substring(varnameStart, index); | ||
if (varname.length === 0) { | ||
throw new UriTemplateError({expressionText: expressionText, message: "a varname must be specified", position: index}); | ||
} | ||
varspec = {varname: varname, exploded: false, maxLength: null}; | ||
varnameStart = null; | ||
@@ -24,18 +28,22 @@ } | ||
if (maxLengthStart === index) { | ||
throw new Error("after a ':' you have to specify the length. position = " + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "after a ':' you have to specify the length", position: index}); | ||
} | ||
varspec.maxLength = parseInt(text.substring(maxLengthStart, index), 10); | ||
varspec.maxLength = parseInt(expressionText.substring(maxLengthStart, index), 10); | ||
maxLengthStart = null; | ||
} | ||
// remove outer braces | ||
text = outerText.substr(1, outerText.length - 2); | ||
operator = (function (operatorText) { | ||
var op = operators.valueOf(operatorText); | ||
if (op === null) { | ||
throw new UriTemplateError({expressionText: expressionText, message: "illegal use of reserved operator", position: index, operator: operatorText}); | ||
} | ||
return op; | ||
}(expressionText.charAt(0))); | ||
index = operator.symbol.length; | ||
// determine operator | ||
operator = operators.valueOf(text.charAt(0)); | ||
index = (operator.symbol === '') ? 0 : 1; | ||
varnameStart = index; | ||
for (; index < text.length; index += chr.length) { | ||
chr = pctEncoder.pctCharAt(text, index); | ||
for (; index < expressionText.length; index += chr.length) { | ||
chr = pctEncoder.pctCharAt(expressionText, index); | ||
if (varnameStart !== null) { | ||
@@ -46,3 +54,3 @@ // the spec says: varname = varchar *( ["."] varchar ) | ||
if (varnameStart === index) { | ||
throw new Error('a varname MUST NOT start with a dot -- see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "a varname MUST NOT start with a dot", position: index}); | ||
} | ||
@@ -58,7 +66,7 @@ continue; | ||
if (index === maxLengthStart && chr === '0') { | ||
throw new Error('A :prefix must not start with digit 0 -- see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must not start with digit 0", position: index}); | ||
} | ||
if (charHelper.isDigit(chr)) { | ||
if (index - maxLengthStart >= 4) { | ||
throw new Error('A :prefix must max 4 digits -- see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must have max 4 digits", position: index}); | ||
} | ||
@@ -71,4 +79,7 @@ continue; | ||
if (varspec.maxLength !== null) { | ||
throw new Error('only one :maxLength is allowed per varspec at position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "only one :maxLength is allowed per varspec", position: index}); | ||
} | ||
if (varspec.exploded) { | ||
throw new UriTemplateError({expressionText: expressionText, message: "an exploeded varspec MUST NOT be varspeced", position: index}); | ||
} | ||
maxLengthStart = index + 1; | ||
@@ -79,9 +90,9 @@ continue; | ||
if (varspec === null) { | ||
throw new Error('explode exploded at position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "exploded without varspec", position: index}); | ||
} | ||
if (varspec.exploded) { | ||
throw new Error('explode exploded twice at position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "exploded twice", position: index}); | ||
} | ||
if (varspec.maxLength) { | ||
throw new Error('an explode (*) MUST NOT follow to a prefix, see position ' + index); | ||
throw new UriTemplateError({expressionText: expressionText, message: "an explode (*) MUST NOT follow to a prefix", position: index}); | ||
} | ||
@@ -98,3 +109,3 @@ varspec.exploded = true; | ||
} | ||
throw new Error("illegal character '" + chr + "' at position " + index + ' of "' + text + '"'); | ||
throw new UriTemplateError({expressionText: expressionText, message: "illegal character", character: chr, position: index}); | ||
} // for chr | ||
@@ -108,6 +119,6 @@ if (varnameStart !== null) { | ||
varspecs.push(varspec); | ||
return new VariableExpression(outerText, operator, varspecs); | ||
return new VariableExpression(expressionText, operator, varspecs); | ||
} | ||
function parseTemplate (uriTemplateText) { | ||
function parse (uriTemplateText) { | ||
// assert filled string | ||
@@ -124,3 +135,3 @@ var | ||
if (chr === '}') { | ||
throw new Error('brace was closed in position ' + index + " but never opened"); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "unopened brace closed", position: index}); | ||
} | ||
@@ -140,9 +151,17 @@ if (chr === '{') { | ||
if (chr === '{') { | ||
throw new Error('brace was opened in position ' + braceOpenIndex + " and cannot be reopened in position " + index); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "brace already opened", position: index}); | ||
} | ||
if (chr === '}') { | ||
if (braceOpenIndex + 1 === index) { | ||
throw new Error("empty braces on position " + braceOpenIndex); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex}); | ||
} | ||
expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex, index + 1))); | ||
try { | ||
expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index))); | ||
} | ||
catch (error) { | ||
if (error.prototype === UriTemplateError.prototype) { | ||
throw new UriTemplateError({templateText: uriTemplateText, message: error.options.message, position: braceOpenIndex + error.options.position, details: error.options}); | ||
} | ||
throw error; | ||
} | ||
braceOpenIndex = null; | ||
@@ -156,3 +175,3 @@ literalStart = index + 1; | ||
if (braceOpenIndex !== null) { | ||
throw new Error("brace was opened on position " + braceOpenIndex + ", but never closed"); | ||
throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex}); | ||
} | ||
@@ -165,3 +184,3 @@ if (literalStart < uriTemplateText.length) { | ||
return parseTemplate; | ||
return parse; | ||
}()); |
/*jshint unused:false */ | ||
/*global parse, objectHelper*/ | ||
/*global parse, objectHelper, UriTemplateError*/ | ||
var UriTemplate = (function () { | ||
@@ -27,3 +27,4 @@ "use strict"; | ||
UriTemplate.parse = parse; | ||
UriTemplate.UriTemplateError = UriTemplateError; | ||
return UriTemplate; | ||
}()); |
/*jshint unused:false */ | ||
/*global pctEncoder, rfcCharHelper, isDefined, LiteralExpression, objectHelper*/ | ||
/*global pctEncoder, rfcCharHelper, isDefined, objectHelper, encodingHelper*/ | ||
var VariableExpression = (function () { | ||
@@ -10,2 +10,32 @@ "use strict"; | ||
function isEmpty (value) { | ||
if (!isDefined(value)) { | ||
return true; | ||
} | ||
if (value === '') { | ||
return true; | ||
} | ||
if (objectHelper.isArray(value)) { | ||
return value.length === 0; | ||
} | ||
for (var propertyName in value) { | ||
if (value.hasOwnProperty(propertyName)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function propertyArray (object) { | ||
var | ||
result = [], | ||
propertyName; | ||
for (propertyName in object) { | ||
if (object.hasOwnProperty(propertyName)) { | ||
result.push({name: propertyName, value: object[propertyName]}); | ||
} | ||
} | ||
return result; | ||
} | ||
function VariableExpression (templateText, operator, varspecs) { | ||
@@ -21,50 +51,122 @@ this.templateText = templateText; | ||
VariableExpression.prototype.expand = function (variables) { | ||
var | ||
result = '', | ||
index, | ||
varspec, | ||
value, | ||
valueIsArr, | ||
isFirstVarspec = true, | ||
operator = this.operator; | ||
// callback to be used within array.reduce | ||
function reduceUnexploded (result, currentValue, currentKey) { | ||
if (isDefined(currentValue)) { | ||
if (result.length > 0) { | ||
result += ','; | ||
} | ||
if (!valueIsArr) { | ||
result += operator.encode(currentKey) + ','; | ||
} | ||
result += operator.encode(currentValue); | ||
function expandSimpleValue(varspec, operator, value) { | ||
var result = ''; | ||
value = value.toString(); | ||
if (operator.named) { | ||
result += encodingHelper.encodeLiteral(varspec.varname); | ||
if (value === '') { | ||
result += operator.ifEmpty; | ||
return result; | ||
} | ||
return result; | ||
result += '='; | ||
} | ||
if (varspec.maxLength !== null) { | ||
value = value.substr(0, varspec.maxLength); | ||
} | ||
result += operator.encode(value); | ||
return result; | ||
} | ||
function reduceNamedExploded (result, currentValue, currentKey) { | ||
if (isDefined(currentValue)) { | ||
if (result.length > 0) { | ||
result += operator.separator; | ||
} | ||
result += (valueIsArr) ? LiteralExpression.encodeLiteral(varspec.varname) : operator.encode(currentKey); | ||
result += '=' + operator.encode(currentValue); | ||
function valueDefined (nameValue) { | ||
return isDefined(nameValue.value); | ||
} | ||
function expandNotExploded(varspec, operator, value) { | ||
var | ||
arr = [], | ||
result = ''; | ||
if (operator.named) { | ||
result += encodingHelper.encodeLiteral(varspec.varname); | ||
if (isEmpty(value)) { | ||
result += operator.ifEmpty; | ||
return result; | ||
} | ||
return result; | ||
result += '='; | ||
} | ||
if (objectHelper.isArray(value)) { | ||
arr = value; | ||
arr = objectHelper.filter(arr, isDefined); | ||
arr = objectHelper.map(arr, operator.encode); | ||
result += objectHelper.join(arr, ','); | ||
} | ||
else { | ||
arr = propertyArray(value); | ||
arr = objectHelper.filter(arr, valueDefined); | ||
arr = objectHelper.map(arr, function (nameValue) { | ||
return operator.encode(nameValue.name) + ',' + operator.encode(nameValue.value); | ||
}); | ||
result += objectHelper.join(arr, ','); | ||
} | ||
return result; | ||
} | ||
function reduceUnnamedExploded (result, currentValue, currentKey) { | ||
if (isDefined(currentValue)) { | ||
if (result.length > 0) { | ||
result += operator.separator; | ||
function expandExplodedNamed (varspec, operator, value) { | ||
var | ||
isArray = objectHelper.isArray(value), | ||
arr = []; | ||
if (isArray) { | ||
arr = value; | ||
arr = objectHelper.filter(arr, isDefined); | ||
arr = objectHelper.map(arr, function (listElement) { | ||
var tmp = encodingHelper.encodeLiteral(varspec.varname); | ||
if (isEmpty(listElement)) { | ||
tmp += operator.ifEmpty; | ||
} | ||
if (!valueIsArr) { | ||
result += operator.encode(currentKey) + '='; | ||
else { | ||
tmp += '=' + operator.encode(listElement); | ||
} | ||
result += operator.encode(currentValue); | ||
} | ||
return result; | ||
return tmp; | ||
}); | ||
} | ||
else { | ||
arr = propertyArray(value); | ||
arr = objectHelper.filter(arr, valueDefined); | ||
arr = objectHelper.map(arr, function (nameValue) { | ||
var tmp = encodingHelper.encodeLiteral(nameValue.name); | ||
if (isEmpty(nameValue.value)) { | ||
tmp += operator.ifEmpty; | ||
} | ||
else { | ||
tmp += '=' + operator.encode(nameValue.value); | ||
} | ||
return tmp; | ||
}); | ||
} | ||
return objectHelper.join(arr, operator.separator); | ||
} | ||
function expandExplodedUnnamed (operator, value) { | ||
var | ||
arr = [], | ||
result = ''; | ||
if (objectHelper.isArray(value)) { | ||
arr = value; | ||
arr = objectHelper.filter(arr, isDefined); | ||
arr = objectHelper.map(arr, operator.encode); | ||
result += objectHelper.join(arr, operator.separator); | ||
} | ||
else { | ||
arr = propertyArray(value); | ||
arr = objectHelper.filter(arr, function (nameValue) { | ||
return isDefined(nameValue.value); | ||
}); | ||
arr = objectHelper.map(arr, function (nameValue) { | ||
return operator.encode(nameValue.name) + '=' + operator.encode(nameValue.value); | ||
}); | ||
result += objectHelper.join(arr, operator.separator); | ||
} | ||
return result; | ||
} | ||
VariableExpression.prototype.expand = function (variables) { | ||
var | ||
expanded = [], | ||
index, | ||
varspec, | ||
value, | ||
valueIsArr, | ||
oneExploded = false, | ||
operator = this.operator; | ||
// expand each varspec and join with operator's separator | ||
@@ -74,29 +176,15 @@ for (index = 0; index < this.varspecs.length; index += 1) { | ||
value = variables[varspec.varname]; | ||
if (!isDefined(value)) { | ||
continue; | ||
// if (!isDefined(value)) { | ||
// if (variables.hasOwnProperty(varspec.name)) { | ||
if (value === null || value === undefined) { | ||
continue; | ||
} | ||
if (isFirstVarspec) { | ||
result += operator.first; | ||
isFirstVarspec = false; | ||
if (varspec.exploded) { | ||
oneExploded = true; | ||
} | ||
else { | ||
result += operator.separator; | ||
} | ||
valueIsArr = objectHelper.isArray(value); | ||
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { | ||
value = value.toString(); | ||
if (operator.named) { | ||
result += LiteralExpression.encodeLiteral(varspec.varname); | ||
if (value === '') { | ||
result += operator.ifEmpty; | ||
continue; | ||
} | ||
result += '='; | ||
} | ||
if (varspec.maxLength !== null) { | ||
value = value.substr(0, varspec.maxLength); | ||
} | ||
result += operator.encode(value); | ||
expanded.push(expandSimpleValue(varspec, operator, value)); | ||
} | ||
else if (varspec.maxLength) { | ||
else if (varspec.maxLength && isDefined(value)) { | ||
// 2.4.1 of the spec says: "Prefix modifiers are not applicable to variables that have composite values." | ||
@@ -106,34 +194,22 @@ throw new Error('Prefix modifiers are not applicable to variables that have composite values. You tried to expand ' + this + " with " + prettyPrint(value)); | ||
else if (!varspec.exploded) { | ||
if (operator.named || !isEmpty(value)) { | ||
expanded.push(expandNotExploded(varspec, operator, value)); | ||
} | ||
} | ||
else if (isDefined(value)) { | ||
if (operator.named) { | ||
result += LiteralExpression.encodeLiteral(varspec.varname); | ||
if (!isDefined(value)) { | ||
result += operator.ifEmpty; | ||
continue; | ||
} | ||
result += '='; | ||
expanded.push(expandExplodedNamed(varspec, operator, value)); | ||
} | ||
result += objectHelper.reduce(value, reduceUnexploded, ''); | ||
else { | ||
expanded.push(expandExplodedUnnamed(operator, value)); | ||
} | ||
} | ||
else { | ||
// exploded and not string | ||
result += objectHelper.reduce(value, operator.named ? reduceNamedExploded : reduceUnnamedExploded, ''); | ||
} | ||
} | ||
if (isFirstVarspec) { | ||
// so no varspecs produced output. | ||
var oneExploded = false; | ||
for (index = 0; index < this.varspecs.length; index += 1) { | ||
if (this.varspecs[index].exploded) { | ||
oneExploded = true; | ||
break; | ||
} | ||
} | ||
if (operator.named && !oneExploded) { | ||
result += operator.symbol; | ||
result += varspec.varname + operator.ifEmpty; | ||
} | ||
if (expanded.length === 0) { | ||
return ""; | ||
} | ||
return result; | ||
else { | ||
return operator.first + objectHelper.join(expanded, operator.separator); | ||
} | ||
}; | ||
@@ -140,0 +216,0 @@ |
@@ -12,3 +12,3 @@ module.exports = (function () { | ||
if (NOISY) { | ||
console.log(text); | ||
console.log.call(null, arguments); | ||
} | ||
@@ -18,3 +18,3 @@ } | ||
function loadUriTemplate () { | ||
var context = {module: {}}; | ||
var context = {module: {}, console: console}; | ||
sandbox(global.URI_TEMPLATE_FILE, context); | ||
@@ -37,8 +37,9 @@ return context.module.exports; | ||
catch (error) { | ||
if (expected === false) { | ||
log('ok. expected error found'); | ||
if (expected === false && error.constructor.prototype === UriTemplate.UriTemplateError.prototype) { | ||
log('ok. expected error found', error); | ||
return; | ||
} | ||
console.log('error', error); | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error, when parsing: ' + error); | ||
console.log('error.constructor.prototype', error.constructor.prototype); | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + error); | ||
return; | ||
@@ -50,3 +51,3 @@ } | ||
if (expected === false) { | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' expected to fail, but returned \'' + actual + '\'!'); | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' expected to fail, but returned \'' + actual + '\''); | ||
return; | ||
@@ -60,3 +61,3 @@ } | ||
console.log('error', error); | ||
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error, when expanding: ' + JSON.stringify(error, null, 4)); | ||
test.fail('chapter ' + chapterName + ', template "' + template + '" threw error, when expanding: ' + JSON.stringify(error, null, 4)); | ||
return; | ||
@@ -63,0 +64,0 @@ } |
@@ -15,6 +15,4 @@ module.exports = (function () { | ||
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'); | ||
'LiteralExpression has an expand method': function (test) { | ||
test.equal(typeof LiteralExpression.prototype.expand, 'function'); | ||
test.done(); | ||
@@ -21,0 +19,0 @@ } |
@@ -9,26 +9,2 @@ module.exports = (function () { | ||
return { | ||
'reduce works with initial value': function (test) { | ||
var callNum = 0; | ||
var result = objectHelper.reduce({a: 1, b: 2, c: 17}, function (current, value, name) { | ||
if (callNum === 0) { | ||
test.equal(current, -1); | ||
test.equal(value, 1); | ||
test.equal(name, 'a'); | ||
} | ||
else if (callNum === 1) { | ||
test.equal(current, 1); | ||
test.equal(value, 2); | ||
test.equal(name, 'b'); | ||
} | ||
else { | ||
test.equal(current, 2); | ||
test.equal(value, 17); | ||
test.equal(name, 'c'); | ||
} | ||
callNum += 1; | ||
return Math.max(current, value); | ||
}, -1); | ||
test.equal(result, 17); | ||
test.done(); | ||
}, | ||
'deepFreeze': { | ||
@@ -35,0 +11,0 @@ 'deepFreeze freezes an object': function (test) { |
@@ -8,2 +8,3 @@ | ||
context = {}; | ||
sandbox('src/UriTemplateError.js', context); | ||
sandbox('src/objectHelper.js', context); | ||
@@ -10,0 +11,0 @@ sandbox('src/charHelper.js', context); |
module.exports = (function () { | ||
"use strict"; | ||
var | ||
sandbox = require('nodeunit').utils.sandbox, | ||
context = {}; | ||
context = {console: console}; | ||
sandbox('src/objectHelper.js', context); | ||
@@ -21,2 +20,61 @@ sandbox('src/charHelper.js', context); | ||
return { | ||
'unexploded': { | ||
'empty is in list': function (test) { | ||
var ve = new VariableExpression("{x,empty}", operators.valueOf(''), [ | ||
{varname: 'x', exploded: false, maxLength: null}, | ||
{varname: 'empty', exploded: false, maxLength: null} | ||
]); | ||
test.equal(ve.expand({x: 'x', empty:''}), 'x,'); | ||
test.done(); | ||
}, | ||
'null is not in list': function (test) { | ||
var ve = new VariableExpression("{x,undef}", operators.valueOf(''), [ | ||
{varname: 'x', exploded: false, maxLength: null}, | ||
{varname: 'empty', exploded: false, maxLength: null} | ||
]); | ||
test.equal(ve.expand({x: 'x', undef: null}), 'x'); | ||
test.done(); | ||
}, | ||
'when empty list and not named, the operator is not printed': function (test) { | ||
var ve = new VariableExpression("{.empty_list}", operators.valueOf('.'), [ | ||
{varname: 'empty_list', exploded: false, maxLength: null} | ||
]); | ||
test.equal(ve.expand({empty_list: []}), ''); | ||
test.done(); | ||
}, | ||
'when empty list and named, the operator is printed': function (test) { | ||
var ve = new VariableExpression("{?empty_list}", operators.valueOf('?'), [ | ||
{varname: 'empty_list', exploded: false, maxLength: null} | ||
]); | ||
test.equal(ve.expand({empty_list: []}), '?empty_list='); | ||
test.done(); | ||
} | ||
}, | ||
'exploded': { | ||
'unnamed': { | ||
'a map shows a key-val list': function (test) { | ||
var ve = new VariableExpression("{keys*}", operators.valueOf(''), [ | ||
{varname: 'keys', exploded: true, maxLength: null} | ||
]); | ||
test.equal(ve.expand({keys: {a: 'a', b: 'b', c: 'c'}}), 'a=a,b=b,c=c'); | ||
test.done(); | ||
}, | ||
'a empty map prints no operator': function (test) { | ||
var ve = new VariableExpression("{.empty_map*}", operators.valueOf('.'), [ | ||
{varname: 'empty_map', exploded: true, maxLength: null} | ||
]); | ||
test.equal(ve.expand({empty_map: {}}), ''); | ||
test.done(); | ||
} | ||
}, | ||
'named': { | ||
'a named, exploded list repeats the name': function (test) { | ||
var ve = new VariableExpression("{;count*}", operators.valueOf(';'), [ | ||
{varname: 'count', exploded: true, maxLength: null} | ||
]); | ||
test.equal(ve.expand({count: ['one', 'two', 'three']}), ';count=one;count=two;count=three'); | ||
test.done(); | ||
} | ||
} | ||
}, | ||
"there must be no separator at the end of the level3 list": function (test) { | ||
@@ -32,13 +90,13 @@ var ve = new VariableExpression("{+x,y}", operators.valueOf('+'), [ | ||
"empty lists with ? must show the name": function (test) { | ||
var ve = new VariableExpression("{?empty}", operators.valueOf('?'), [ | ||
{varname: 'empty', exploded: false, maxLength: null} | ||
var ve = new VariableExpression("{?empty_list}", operators.valueOf('?'), [ | ||
{varname: 'empty_list', exploded: false, maxLength: null} | ||
]); | ||
test.equal(ve.expand({empty: {}}), '?empty='); | ||
test.equal(ve.expand({empty_list: {}}), '?empty_list='); | ||
test.done(); | ||
}, | ||
"exploded empty lists with ? must not show the name": function (test) { | ||
var ve = new VariableExpression("{?empty*}", operators.valueOf('?'), [ | ||
{varname: 'empty', exploded: true, maxLength: null} | ||
var ve = new VariableExpression("{?empty_list*}", operators.valueOf('?'), [ | ||
{varname: 'empty_list', exploded: true, maxLength: null} | ||
]); | ||
test.equal(ve.expand({empty: {}}), ''); | ||
test.equal(ve.expand({empty_list: []}), ''); | ||
test.done(); | ||
@@ -66,4 +124,21 @@ }, | ||
test.done(); | ||
}, | ||
'query expression with 1 varname will expand to empty, if data is undef': function (test) { | ||
var ve = new VariableExpression("{?a}", operators.valueOf('?'), [ | ||
{varname: 'a', exploded: false} | ||
]); | ||
test.equal(ve.expand({}), ''); | ||
test.done(); | ||
}, | ||
'query expression with more than 1 varname will expand to empty, if data is undef': function (test) { | ||
var ve = new VariableExpression("{?a,b,c}", operators.valueOf('?'), [ | ||
{varname: 'a', exploded: false}, | ||
{varname: 'b', exploded: false}, | ||
{varname: 'c', exploded: false} | ||
]); | ||
test.equal(ve.expand({}), ''); | ||
test.done(); | ||
} | ||
}; | ||
}()); |
AI-detected possible typosquat
Supply chain riskAI has identified this package as a potential typosquat of a more popular package. This suggests that the package may be intentionally mimicking another package's name, description, or other metadata.
Found 1 instance in 1 package
141670
41
2888
78
2