Commit 5c4fec30 authored by Romain Courteaud's avatar Romain Courteaud

Allow to extract level 0/1 template variables.

parent 01ff518d
...@@ -63,7 +63,8 @@ ...@@ -63,7 +63,8 @@
var TARGET_UNCOMPRESSED_DEPENDENCIES = (function () { var TARGET_UNCOMPRESSED_DEPENDENCIES = (function () {
var all = new jake.FileList(); var all = new jake.FileList();
all.include('./Jakefile.js', 'own-testcases.json'); all.include('./Jakefile.js', 'own-testcases.json',
'extract-spec-examples.json');
all.include('src/**'); all.include('src/**');
all.include('test/**'); all.include('test/**');
all.exclude(TARGET_COMPRESSED); all.exclude(TARGET_COMPRESSED);
......
!function(e){"use strict";function t(e){var n;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(n in e)if(e.hasOwnProperty(n)&&t(e[n]))return!0;return!1}var n=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 t(e){return"[object String]"===Object.prototype.toString.apply(e)}function n(e){return"[object Number]"===Object.prototype.toString.apply(e)}function r(e){return"[object Boolean]"===Object.prototype.toString.apply(e)}function i(e,t){var n,r="",i=!0;for(n=0;n<e.length;n+=1)i?i=!1:r+=t,r+=e[n];return r}function o(e,t){for(var n=[],r=0;r<e.length;r+=1)n.push(t(e[r]));return n}function s(e,t){for(var n=[],r=0;r<e.length;r+=1)t(e[r])&&n.push(e[r]);return n}function a(e){if("object"!=typeof e||null===e)return e;Object.freeze(e);var t,n;for(n in e)e.hasOwnProperty(n)&&(t=e[n],"object"==typeof t&&u(t));return e}function u(e){return"function"==typeof Object.freeze?a(e):e}return{isArray:e,isString:t,isNumber:n,isBoolean:r,join:i,map:o,filter:s,deepFreeze:u}}(),i=function(){function e(e){return e>="a"&&"z">=e||e>="A"&&"Z">=e}function t(e){return e>="0"&&"9">=e}function n(e){return t(e)||e>="a"&&"f">=e||e>="A"&&"F">=e}return{isAlpha:e,isDigit:t,isHexDigit:n}}(),o=function(){function e(e){var t,n,r="",i=s.encode(e);for(n=0;n<i.length;n+=1)t=i.charCodeAt(n),r+="%"+(16>t?"0":"")+t.toString(16).toUpperCase();return r}function t(e,t){return"%"===e.charAt(t)&&i.isHexDigit(e.charAt(t+1))&&i.isHexDigit(e.charAt(t+2))}function n(e,t){return parseInt(e.substr(t,2),16)}function r(e){if(!t(e,0))return!1;var r=n(e,1),i=s.numBytes(r);if(0===i)return!1;for(var o=1;i>o;o+=1)if(!t(e,3*o)||!s.isValidFollowingCharCode(n(e,3*o+1)))return!1;return!0}function o(e,r){var i=e.charAt(r);if(!t(e,r))return i;var o=n(e,r+1),a=s.numBytes(o);if(0===a)return i;for(var u=1;a>u;u+=1)if(!t(e,r+3*u)||!s.isValidFollowingCharCode(n(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 t(e){return i.isAlpha(e)||i.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:t,isReserved:n}}(),a=function(){function e(e,t){var n,r="",i="";for(("number"==typeof e||"boolean"==typeof e)&&(e=e.toString()),n=0;n<e.length;n+=i.length)i=e.charAt(n),r+=s.isUnreserved(i)||t&&s.isReserved(i)?i:o.encodeCharacter(i);return r}function t(t){return e(t,!0)}function n(e,t){var n=o.pctCharAt(e,t);return n.length>1?n:s.isReserved(n)||s.isUnreserved(n)?n:o.encodeCharacter(n)}function r(e){var t,n="",r="";for(t=0;t<e.length;t+=r.length)r=o.pctCharAt(e,t),n+=r.length>1?r:s.isReserved(r)||s.isUnreserved(r)?r:o.encodeCharacter(r);return n}return{encode:e,encodePassReserved:t,encodeLiteral:r,encodeLiteralCharacter:n}}(),u=function(){function e(e){t[e]={symbol:e,separator:"?"===e?"&":""===e||"+"===e||"#"===e?",":e,named:";"===e||"&"===e||"?"===e,ifEmpty:"&"===e||"?"===e?"=":"",first:"+"===e?"":e,encode:"+"===e||"#"===e?a.encodePassReserved:a.encode,toString:function(){return this.symbol}}}var t={};return e(""),e("+"),e("#"),e("."),e("/"),e(";"),e("?"),e("&"),{valueOf:function(e){return t[e]?t[e]:"=,!@|".indexOf(e)>=0?null:t[""]}}}(),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 t(){var t=e.substring(h,f);if(0===t.length)throw new n({expressionText:e,message:"a varname must be specified",position:f});c={varname:t,exploded:!1,maxLength:null},h=null}function r(){if(d===f)throw new n({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,g="";for(a=function(t){var r=u.valueOf(t);if(null===r)throw new n({expressionText:e,message:"illegal use of reserved operator",position:f,operator:t});return r}(e.charAt(0)),f=a.symbol.length,h=f;f<e.length;f+=g.length){if(g=o.pctCharAt(e,f),null!==h){if("."===g){if(h===f)throw new n({expressionText:e,message:"a varname MUST NOT start with a dot",position:f});continue}if(s.isVarchar(g))continue;t()}if(null!==d){if(f===d&&"0"===g)throw new n({expressionText:e,message:"A :prefix must not start with digit 0",position:f});if(i.isDigit(g)){if(f-d>=4)throw new n({expressionText:e,message:"A :prefix must have max 4 digits",position:f});continue}r()}if(":"!==g)if("*"!==g){if(","!==g)throw new n({expressionText:e,message:"illegal character",character:g,position:f});p.push(c),c=null,h=f+1}else{if(null===c)throw new n({expressionText:e,message:"exploded without varspec",position:f});if(c.exploded)throw new n({expressionText:e,message:"exploded twice",position:f});if(c.maxLength)throw new n({expressionText:e,message:"an explode (*) MUST NOT follow to a prefix",position:f});c.exploded=!0}else{if(null!==c.maxLength)throw new n({expressionText:e,message:"only one :maxLength is allowed per varspec",position:f});if(c.exploded)throw new n({expressionText:e,message:"an exploeded varspec MUST NOT be varspeced",position:f});d=f+1}}return null!==h&&t(),null!==d&&r(),p.push(c),new l(e,a,p)}function t(t){var r,i,o=[],s=null,a=0;for(r=0;r<t.length;r+=1)if(i=t.charAt(r),null===a){if(null===s)throw new Error("reached unreachable code");if("{"===i)throw new n({templateText:t,message:"brace already opened",position:r});if("}"===i){if(s+1===r)throw new n({templateText:t,message:"empty braces",position:s});try{o.push(e(t.substring(s+1,r)))}catch(u){if(u.prototype===n.prototype)throw new n({templateText:t,message:u.options.message,position:s+u.options.position,details:u.options});throw u}s=null,a=r+1}}else{if("}"===i)throw new n({templateText:t,message:"unopened brace closed",position:r});"{"===i&&(r>a&&o.push(new f(t.substring(a,r))),a=null,s=r)}if(null!==s)throw new n({templateText:t,message:"unclosed brace",position:s});return a<t.length&&o.push(new f(t.substr(a))),new c(t,o)}return t}(),l=function(){function e(e){return JSON&&JSON.stringify?JSON.stringify(e):e}function n(e){if(!t(e))return!0;if(r.isString(e))return""===e;if(r.isNumber(e)||r.isBoolean(e))return!1;if(r.isArray(e))return 0===e.length;for(var n in e)if(e.hasOwnProperty(n))return!1;return!0}function i(e){var t,n=[];for(t in e)e.hasOwnProperty(t)&&n.push({name:t,value:e[t]});return n}function o(e,t,n){this.templateText=e,this.operator=t,this.varspecs=n}function s(e,t,n){var r="";if(n=n.toString(),t.named){if(r+=a.encodeLiteral(e.varname),""===n)return r+=t.ifEmpty;r+="="}return null!==e.maxLength&&(n=n.substr(0,e.maxLength)),r+=t.encode(n)}function u(e){return t(e.value)}function f(e,o,s){var f=[],p="";if(o.named){if(p+=a.encodeLiteral(e.varname),n(s))return p+=o.ifEmpty;p+="="}return r.isArray(s)?(f=s,f=r.filter(f,t),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,t),p=r.map(p,function(t){var r=a.encodeLiteral(e.varname);return r+=n(t)?o.ifEmpty:"="+o.encode(t)})):(p=i(s),p=r.filter(p,u),p=r.map(p,function(e){var t=a.encodeLiteral(e.name);return t+=n(e.value)?o.ifEmpty:"="+o.encode(e.value)})),r.join(p,o.separator)}function l(e,n){var o=[],s="";return r.isArray(n)?(o=n,o=r.filter(o,t),o=r.map(o,e.encode),s+=r.join(o,e.separator)):(o=i(n),o=r.filter(o,function(e){return t(e.value)}),o=r.map(o,function(t){return e.encode(t.name)+"="+e.encode(t.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,g=this.operator;for(o=0;o<this.varspecs.length;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,g,u));else{if(a.maxLength&&t(u))throw new Error("Prefix modifiers are not applicable to variables that have composite values. You tried to expand "+this+" with "+e(u));a.exploded?t(u)&&(g.named?h.push(p(a,g,u)):h.push(l(g,u))):(g.named||!n(u))&&h.push(f(a,g,u))}return 0===h.length?"":g.first+r.join(h,g.separator)},o}(),c=function(){function e(e,t){this.templateText=e,this.expressions=t,r.deepFreeze(this)}return e.prototype.toString=function(){return this.templateText},e.prototype.expand=function(e){var t,n="";for(t=0;t<this.expressions.length;t+=1)n+=this.expressions[t].expand(e);return n},e.parse=p,e.UriTemplateError=n,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 t(e){var n;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(n in e)if(e.hasOwnProperty(n)&&t(e[n]))return!0;return!1}var n=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 t(e){return"[object String]"===Object.prototype.toString.apply(e)}function n(e){return"[object Number]"===Object.prototype.toString.apply(e)}function r(e){return"[object Boolean]"===Object.prototype.toString.apply(e)}function i(e,t){var n,r="",i=!0;for(n=0;n<e.length;n+=1)i?i=!1:r+=t,r+=e[n];return r}function o(e,t){for(var n=[],r=0;r<e.length;r+=1)n.push(t(e[r]));return n}function s(e,t){for(var n=[],r=0;r<e.length;r+=1)t(e[r])&&n.push(e[r]);return n}function a(e){if("object"!=typeof e||null===e)return e;Object.freeze(e);var t,n;for(n in e)e.hasOwnProperty(n)&&(t=e[n],"object"==typeof t&&u(t));return e}function u(e){return"function"==typeof Object.freeze?a(e):e}return{isArray:e,isString:t,isNumber:n,isBoolean:r,join:i,map:o,filter:s,deepFreeze:u}}(),i=function(){function e(e){return e>="a"&&"z">=e||e>="A"&&"Z">=e}function t(e){return e>="0"&&"9">=e}function n(e){return t(e)||e>="a"&&"f">=e||e>="A"&&"F">=e}return{isAlpha:e,isDigit:t,isHexDigit:n}}(),o=function(){function e(e){var t,n,r="",i=s.encode(e);for(n=0;n<i.length;n+=1)t=i.charCodeAt(n),r+="%"+(16>t?"0":"")+t.toString(16).toUpperCase();return r}function t(e,t){return"%"===e.charAt(t)&&i.isHexDigit(e.charAt(t+1))&&i.isHexDigit(e.charAt(t+2))}function n(e,t){return parseInt(e.substr(t,2),16)}function r(e){if(!t(e,0))return!1;var r=n(e,1),i=s.numBytes(r);if(0===i)return!1;for(var o=1;i>o;o+=1)if(!t(e,3*o)||!s.isValidFollowingCharCode(n(e,3*o+1)))return!1;return!0}function o(e,r){var i=e.charAt(r);if(!t(e,r))return i;var o=n(e,r+1),a=s.numBytes(o);if(0===a)return i;for(var u=1;a>u;u+=1)if(!t(e,r+3*u)||!s.isValidFollowingCharCode(n(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 t(e){return i.isAlpha(e)||i.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:t,isReserved:n}}(),a=function(){function e(e,t){var n,r="",i="";for(("number"==typeof e||"boolean"==typeof e)&&(e=e.toString()),n=0;n<e.length;n+=i.length)i=e.charAt(n),r+=s.isUnreserved(i)||t&&s.isReserved(i)?i:o.encodeCharacter(i);return r}function t(t){return e(t,!0)}function n(e,t){var n=o.pctCharAt(e,t);return n.length>1?n:s.isReserved(n)||s.isUnreserved(n)?n:o.encodeCharacter(n)}function r(e){var t,n="",r="";for(t=0;t<e.length;t+=r.length)r=o.pctCharAt(e,t),n+=r.length>1?r:s.isReserved(r)||s.isUnreserved(r)?r:o.encodeCharacter(r);return n}return{encode:e,encodePassReserved:t,encodeLiteral:r,encodeLiteralCharacter:n}}(),u=function(){function e(e){t[e]={symbol:e,separator:"?"===e?"&":""===e||"+"===e||"#"===e?",":e,named:";"===e||"&"===e||"?"===e,ifEmpty:"&"===e||"?"===e?"=":"",first:"+"===e?"":e,encode:"+"===e||"#"===e?a.encodePassReserved:a.encode,toString:function(){return this.symbol}}}var t={};return e(""),e("+"),e("#"),e("."),e("/"),e(";"),e("?"),e("&"),{valueOf:function(e){return t[e]?t[e]:"=,!@|".indexOf(e)>=0?null:t[""]}}}(),p=function(){function e(e){this.literal=a.encodeLiteral(e)}return e.prototype.expand=function(){return this.literal},e.prototype.toString=e.prototype.expand,e}(),f=function(){function e(e){function t(){var t=e.substring(h,p);if(0===t.length)throw new n({expressionText:e,message:"a varname must be specified",position:p});c={varname:t,exploded:!1,maxLength:null},h=null}function r(){if(d===p)throw new n({expressionText:e,message:"after a ':' you have to specify the length",position:p});c.maxLength=parseInt(e.substring(d,p),10),d=null}var a,p,f=[],c=null,h=null,d=null,g="";for(a=function(t){var r=u.valueOf(t);if(null===r)throw new n({expressionText:e,message:"illegal use of reserved operator",position:p,operator:t});return r}(e.charAt(0)),p=a.symbol.length,h=p;p<e.length;p+=g.length){if(g=o.pctCharAt(e,p),null!==h){if("."===g){if(h===p)throw new n({expressionText:e,message:"a varname MUST NOT start with a dot",position:p});continue}if(s.isVarchar(g))continue;t()}if(null!==d){if(p===d&&"0"===g)throw new n({expressionText:e,message:"A :prefix must not start with digit 0",position:p});if(i.isDigit(g)){if(p-d>=4)throw new n({expressionText:e,message:"A :prefix must have max 4 digits",position:p});continue}r()}if(":"!==g)if("*"!==g){if(","!==g)throw new n({expressionText:e,message:"illegal character",character:g,position:p});f.push(c),c=null,h=p+1}else{if(null===c)throw new n({expressionText:e,message:"exploded without varspec",position:p});if(c.exploded)throw new n({expressionText:e,message:"exploded twice",position:p});if(c.maxLength)throw new n({expressionText:e,message:"an explode (*) MUST NOT follow to a prefix",position:p});c.exploded=!0}else{if(null!==c.maxLength)throw new n({expressionText:e,message:"only one :maxLength is allowed per varspec",position:p});if(c.exploded)throw new n({expressionText:e,message:"an exploeded varspec MUST NOT be varspeced",position:p});d=p+1}}return null!==h&&t(),null!==d&&r(),f.push(c),new l(e,a,f)}function t(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function r(r){var i,o,s,a=[],u=null,f="",l=!0,h=0;for(i=0;i<r.length;i+=1)if(o=r.charAt(i),null===h){if(null===u)throw new Error("reached unreachable code");if("{"===o)throw new n({templateText:r,message:"brace already opened",position:i});if("}"===o){if(u+1===i)throw new n({templateText:r,message:"empty braces",position:u});try{s=e(r.substring(u+1,i))}catch(d){if(d.prototype===n.prototype)throw new n({templateText:r,message:d.options.message,position:u+d.options.position,details:d.options});throw d}a.push(s),0===s.operator.symbol.length?f+="([^/]+)":l=!1,u=null,h=i+1}}else{if("}"===o)throw new n({templateText:r,message:"unopened brace closed",position:i});"{"===o&&(i>h&&(s=new p(r.substring(h,i)),a.push(s),f+=t(s.literal)),h=null,u=i)}if(null!==u)throw new n({templateText:r,message:"unclosed brace",position:u});return h<r.length&&(s=new p(r.substring(h)),a.push(s),f+=t(s.literal)),l===!1&&(f=void 0),new c(r,a,f)}return r}(),l=function(){function e(e){return JSON&&JSON.stringify?JSON.stringify(e):e}function n(e){if(!t(e))return!0;if(r.isString(e))return""===e;if(r.isNumber(e)||r.isBoolean(e))return!1;if(r.isArray(e))return 0===e.length;for(var n in e)if(e.hasOwnProperty(n))return!1;return!0}function i(e){var t,n=[];for(t in e)e.hasOwnProperty(t)&&n.push({name:t,value:e[t]});return n}function o(e,t,n){this.templateText=e,this.operator=t,this.varspecs=n}function s(e,t,n){var r="";if(n=n.toString(),t.named){if(r+=a.encodeLiteral(e.varname),""===n)return r+=t.ifEmpty;r+="="}return null!==e.maxLength&&(n=n.substr(0,e.maxLength)),r+=t.encode(n)}function u(e){return t(e.value)}function p(e,o,s){var p=[],f="";if(o.named){if(f+=a.encodeLiteral(e.varname),n(s))return f+=o.ifEmpty;f+="="}return r.isArray(s)?(p=s,p=r.filter(p,t),p=r.map(p,o.encode),f+=r.join(p,",")):(p=i(s),p=r.filter(p,u),p=r.map(p,function(e){return o.encode(e.name)+","+o.encode(e.value)}),f+=r.join(p,",")),f}function f(e,o,s){var p=r.isArray(s),f=[];return p?(f=s,f=r.filter(f,t),f=r.map(f,function(t){var r=a.encodeLiteral(e.varname);return r+=n(t)?o.ifEmpty:"="+o.encode(t)})):(f=i(s),f=r.filter(f,u),f=r.map(f,function(e){var t=a.encodeLiteral(e.name);return t+=n(e.value)?o.ifEmpty:"="+o.encode(e.value)})),r.join(f,o.separator)}function l(e,n){var o=[],s="";return r.isArray(n)?(o=n,o=r.filter(o,t),o=r.map(o,e.encode),s+=r.join(o,e.separator)):(o=i(n),o=r.filter(o,function(e){return t(e.value)}),o=r.map(o,function(t){return e.encode(t.name)+"="+e.encode(t.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,g=this.operator;for(o=0;o<this.varspecs.length;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,g,u));else{if(a.maxLength&&t(u))throw new Error("Prefix modifiers are not applicable to variables that have composite values. You tried to expand "+this+" with "+e(u));a.exploded?t(u)&&(g.named?h.push(f(a,g,u)):h.push(l(g,u))):(g.named||!n(u))&&h.push(p(a,g,u))}return 0===h.length?"":g.first+r.join(h,g.separator)},o}(),c=function(){function e(e,t,n){this.templateText=e,this.expressions=t,void 0!==n&&(this.regexp=new RegExp("^"+n+"$")),r.deepFreeze(this)}return e.prototype.toString=function(){return this.templateText},e.prototype.expand=function(e){var t,n="";for(t=0;t<this.expressions.length;t+=1)n+=this.expressions[t].expand(e);return n},e.prototype.extract=function(e){var t,n,r,i,o=1,s=!0,a={};if(void 0!==this.regexp&&this.regexp.test(e)){for(i=this.regexp.exec(e),t=0;t<this.expressions.length;t+=1)n=this.expressions[t],void 0===n.literal&&(void 0!==n.operator&&0===n.operator.symbol.length&&1===n.varspecs.length?(r=n.varspecs[0],r.exploded===!1&&null===r.maxLength?-1===i[o].indexOf(",")?(a[r.varname]=decodeURIComponent(i[o]),o+=1):s=!1:s=!1):s=!1);if(s)return a}return!1},e.parse=f,e.UriTemplateError=n,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 \ No newline at end of file
...@@ -559,13 +559,21 @@ var parse = (function () { ...@@ -559,13 +559,21 @@ var parse = (function () {
return new VariableExpression(expressionText, operator, varspecs); return new VariableExpression(expressionText, operator, varspecs);
} }
function escape_regexp_string(string) {
// http://simonwillison.net/2006/Jan/20/escape/
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
function parse (uriTemplateText) { function parse (uriTemplateText) {
// assert filled string // assert filled string
var var
index, index,
chr, chr,
expressions = [], expressions = [],
expression,
braceOpenIndex = null, braceOpenIndex = null,
regexp_string = '',
can_match = true,
literalStart = 0; literalStart = 0;
for (index = 0; index < uriTemplateText.length; index += 1) { for (index = 0; index < uriTemplateText.length; index += 1) {
chr = uriTemplateText.charAt(index); chr = uriTemplateText.charAt(index);
...@@ -575,7 +583,10 @@ var parse = (function () { ...@@ -575,7 +583,10 @@ var parse = (function () {
} }
if (chr === '{') { if (chr === '{') {
if (literalStart < index) { if (literalStart < index) {
expressions.push(new LiteralExpression(uriTemplateText.substring(literalStart, index))); expression = new LiteralExpression(uriTemplateText.substring(literalStart, index));
expressions.push(expression);
regexp_string += escape_regexp_string(
expression.literal);
} }
literalStart = null; literalStart = null;
braceOpenIndex = index; braceOpenIndex = index;
...@@ -593,7 +604,7 @@ var parse = (function () { ...@@ -593,7 +604,7 @@ var parse = (function () {
throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex}); throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex});
} }
try { try {
expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index))); expression = parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index));
} }
catch (error) { catch (error) {
if (error.prototype === UriTemplateError.prototype) { if (error.prototype === UriTemplateError.prototype) {
...@@ -601,6 +612,12 @@ var parse = (function () { ...@@ -601,6 +612,12 @@ var parse = (function () {
} }
throw error; throw error;
} }
expressions.push(expression);
if (expression.operator.symbol.length === 0) {
regexp_string += "([^/]+)";
} else {
can_match = false;
}
braceOpenIndex = null; braceOpenIndex = null;
literalStart = index + 1; literalStart = index + 1;
} }
...@@ -612,9 +629,14 @@ var parse = (function () { ...@@ -612,9 +629,14 @@ var parse = (function () {
throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex}); throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex});
} }
if (literalStart < uriTemplateText.length) { if (literalStart < uriTemplateText.length) {
expressions.push(new LiteralExpression(uriTemplateText.substr(literalStart))); expression = new LiteralExpression(uriTemplateText.substring(literalStart));
expressions.push(expression);
regexp_string += escape_regexp_string(expression.literal);
} }
return new UriTemplate(uriTemplateText, expressions); if (can_match === false) {
regexp_string = undefined;
}
return new UriTemplate(uriTemplateText, expressions, regexp_string);
} }
return parse; return parse;
...@@ -836,9 +858,14 @@ var VariableExpression = (function () { ...@@ -836,9 +858,14 @@ var VariableExpression = (function () {
}()); }());
var UriTemplate = (function () { var UriTemplate = (function () {
function UriTemplate (templateText, expressions) { function UriTemplate (templateText, expressions, regexp_string) {
this.templateText = templateText; this.templateText = templateText;
this.expressions = expressions; this.expressions = expressions;
if (regexp_string !== undefined) {
this.regexp = new RegExp("^" + regexp_string + "$");
}
objectHelper.deepFreeze(this); objectHelper.deepFreeze(this);
} }
...@@ -857,6 +884,44 @@ var UriTemplate = (function () { ...@@ -857,6 +884,44 @@ var UriTemplate = (function () {
return result; return result;
}; };
UriTemplate.prototype.extract = function (text) {
var expression_index,
extracted_index = 1,
expression,
varspec,
matched = true,
variables = {},
result;
if ((this.regexp !== undefined) && (this.regexp.test(text))) {
result = this.regexp.exec(text);
for (expression_index = 0; expression_index < this.expressions.length; expression_index += 1) {
expression = this.expressions[expression_index];
if (expression.literal === undefined) {
if ((expression.operator !== undefined) && (expression.operator.symbol.length === 0) && (expression.varspecs.length === 1)) {
varspec = expression.varspecs[0];
if ((varspec.exploded === false) && (varspec.maxLength === null)) {
if (result[extracted_index].indexOf(',') === -1) {
variables[varspec.varname] = decodeURIComponent(result[extracted_index]);
extracted_index += 1;
} else {
matched = false;
}
} else {
matched = false;
}
} else {
matched = false;
}
}
}
if (matched) {
return variables;
}
}
return false;
};
UriTemplate.parse = parse; UriTemplate.parse = parse;
UriTemplate.UriTemplateError = UriTemplateError; UriTemplate.UriTemplateError = UriTemplateError;
return UriTemplate; return UriTemplate;
......
{
"Level 0 Examples" :
{
"level": 0,
"variables": {
},
"testcases" : [
["var", "var", []],
["hello", "hello", []],
["varhello", "var", false],
["var", "varhello", false]
]
},
"Level 1 Examples" :
{
"level": 1,
"variables": {
"var" : "value",
"hello" : "Hello World!"
},
"testcases" : [
["{var}", "value", ["var"]],
["{hello}", "Hello%20World%21", ["hello"]],
["{hello}foo{var}", "Hello%20World%21foovalue", ["hello", "var"]]
]
},
"Level 2 Examples" :
{
"level": 2,
"variables": {
"var" : "value",
"hello" : "Hello World!",
"path" : "/foo/bar"
},
"testcases" : [
["{+var}", "value", false],
["{+hello}", "Hello%20World!", false],
["{+path}/here", "/foo/bar/here", false],
["here?ref={+path}", "here?ref=/foo/bar", false]
]
},
"Level 3 Examples" :
{
"level": 3,
"variables": {
"var" : "value",
"hello" : "Hello World!",
"empty" : "",
"path" : "/foo/bar",
"x" : "1024",
"y" : "768"
},
"testcases" : [
["map?{x,y}", "map?1024,768", false],
["{x,hello,y}", "1024,Hello%20World%21,768", false],
["{+x,hello,y}", "1024,Hello%20World!,768", false],
["{+path,x}/here", "/foo/bar,1024/here", false],
["{#x,hello,y}", "#1024,Hello%20World!,768", false],
["{#path,x}/here", "#/foo/bar,1024/here", false],
["X{.var}", "X.value", false],
["X{.x,y}", "X.1024.768", false],
["{/var}", "/value", false],
["{/var,x}/here", "/value/1024/here", false],
["{;x,y}", ";x=1024;y=768", false],
["{;x,y,empty}", ";x=1024;y=768;empty", false],
["{?x,y}", "?x=1024&y=768", false],
["{?x,y,empty}", "?x=1024&y=768&empty=", false],
["?fixed=yes{&x}", "?fixed=yes&x=1024", false],
["{&x,y,empty}", "&x=1024&y=768&empty=", false]
]
},
"Level 4 Examples" :
{
"level": 4,
"variables": {
"var": "value",
"hello": "Hello World!",
"path": "/foo/bar",
"list": ["red", "green", "blue"],
"keys": {"semi": ";", "dot": ".", "comma":","}
},
"testcases": [
["{var:3}", "val", false],
["{var:30}", "value", false],
["{list}", "red,green,blue", false],
["{list*}", "red,green,blue", false],
["{keys}", [
"comma,%2C,dot,.,semi,%3B",
"comma,%2C,semi,%3B,dot,.",
"dot,.,comma,%2C,semi,%3B",
"dot,.,semi,%3B,comma,%2C",
"semi,%3B,comma,%2C,dot,.",
"semi,%3B,dot,.,comma,%2C"
], false],
["{keys*}", [
"comma=%2C,dot=.,semi=%3B",
"comma=%2C,semi=%3B,dot=.",
"dot=.,comma=%2C,semi=%3B",
"dot=.,semi=%3B,comma=%2C",
"semi=%3B,comma=%2C,dot=.",
"semi=%3B,dot=.,comma=%2C"
], false],
["{+path:6}/here", "/foo/b/here", false],
["{+list}", "red,green,blue", false],
["{+list*}", "red,green,blue", false],
["{+keys}", [
"comma,,,dot,.,semi,;",
"comma,,,semi,;,dot,.",
"dot,.,comma,,,semi,;",
"dot,.,semi,;,comma,,",
"semi,;,comma,,,dot,.",
"semi,;,dot,.,comma,,"
], false],
["{+keys*}", [
"comma=,,dot=.,semi=;",
"comma=,,semi=;,dot=.",
"dot=.,comma=,,semi=;",
"dot=.,semi=;,comma=,",
"semi=;,comma=,,dot=.",
"semi=;,dot=.,comma=,"
], false],
["{#path:6}/here", "#/foo/b/here", false],
["{#list}", "#red,green,blue", false],
["{#list*}", "#red,green,blue", false],
["{#keys}", [
"#comma,,,dot,.,semi,;",
"#comma,,,semi,;,dot,.",
"#dot,.,comma,,,semi,;",
"#dot,.,semi,;,comma,,",
"#semi,;,comma,,,dot,.",
"#semi,;,dot,.,comma,,"
], false],
["{#keys*}", [
"#comma=,,dot=.,semi=;",
"#comma=,,semi=;,dot=.",
"#dot=.,comma=,,semi=;",
"#dot=.,semi=;,comma=,",
"#semi=;,comma=,,dot=.",
"#semi=;,dot=.,comma=,"
], false],
["X{.var:3}", "X.val", false],
["X{.list}", "X.red,green,blue", false],
["X{.list*}", "X.red.green.blue", false],
["X{.keys}", [
"X.comma,%2C,dot,.,semi,%3B",
"X.comma,%2C,semi,%3B,dot,.",
"X.dot,.,comma,%2C,semi,%3B",
"X.dot,.,semi,%3B,comma,%2C",
"X.semi,%3B,comma,%2C,dot,.",
"X.semi,%3B,dot,.,comma,%2C"
], false],
["{/var:1,var}", "/v/value", false],
["{/list}", "/red,green,blue", false],
["{/list*}", "/red/green/blue", false],
["{/list*,path:4}", "/red/green/blue/%2Ffoo", false],
["{/keys}", [
"/comma,%2C,dot,.,semi,%3B",
"/comma,%2C,semi,%3B,dot,.",
"/dot,.,comma,%2C,semi,%3B",
"/dot,.,semi,%3B,comma,%2C",
"/semi,%3B,comma,%2C,dot,.",
"/semi,%3B,dot,.,comma,%2C"
], false],
["{/keys*}", [
"/comma=%2C/dot=./semi=%3B",
"/comma=%2C/semi=%3B/dot=.",
"/dot=./comma=%2C/semi=%3B",
"/dot=./semi=%3B/comma=%2C",
"/semi=%3B/comma=%2C/dot=.",
"/semi=%3B/dot=./comma=%2C"
], false],
["{;hello:5}", ";hello=Hello", false],
["{;list}", ";list=red,green,blue", false],
["{;list*}", ";list=red;list=green;list=blue", false],
["{;keys}", [
";keys=comma,%2C,dot,.,semi,%3B",
";keys=comma,%2C,semi,%3B,dot,.",
";keys=dot,.,comma,%2C,semi,%3B",
";keys=dot,.,semi,%3B,comma,%2C",
";keys=semi,%3B,comma,%2C,dot,.",
";keys=semi,%3B,dot,.,comma,%2C"
], false],
["{;keys*}", [
";comma=%2C;dot=.;semi=%3B",
";comma=%2C;semi=%3B;dot=.",
";dot=.;comma=%2C;semi=%3B",
";dot=.;semi=%3B;comma=%2C",
";semi=%3B;comma=%2C;dot=.",
";semi=%3B;dot=.;comma=%2C"
], false],
["{?var:3}", "?var=val", false],
["{?list}", "?list=red,green,blue", false],
["{?list*}", "?list=red&list=green&list=blue", false],
["{?keys}", [
"?keys=comma,%2C,dot,.,semi,%3B",
"?keys=comma,%2C,semi,%3B,dot,.",
"?keys=dot,.,comma,%2C,semi,%3B",
"?keys=dot,.,semi,%3B,comma,%2C",
"?keys=semi,%3B,comma,%2C,dot,.",
"?keys=semi,%3B,dot,.,comma,%2C"
], false],
["{?keys*}", [
"?comma=%2C&dot=.&semi=%3B",
"?comma=%2C&semi=%3B&dot=.",
"?dot=.&comma=%2C&semi=%3B",
"?dot=.&semi=%3B&comma=%2C",
"?semi=%3B&comma=%2C&dot=.",
"?semi=%3B&dot=.&comma=%2C"
], false],
["{&var:3}", "&var=val", false],
["{&list}", "&list=red,green,blue", false],
["{&list*}", "&list=red&list=green&list=blue", false],
["{&keys}", [
"&keys=comma,%2C,dot,.,semi,%3B",
"&keys=comma,%2C,semi,%3B,dot,.",
"&keys=dot,.,comma,%2C,semi,%3B",
"&keys=dot,.,semi,%3B,comma,%2C",
"&keys=semi,%3B,comma,%2C,dot,.",
"&keys=semi,%3B,dot,.,comma,%2C"
], false],
["{&keys*}", [
"&comma=%2C&dot=.&semi=%3B",
"&comma=%2C&semi=%3B&dot=.",
"&dot=.&comma=%2C&semi=%3B",
"&dot=.&semi=%3B&comma=%2C",
"&semi=%3B&comma=%2C&dot=.",
"&semi=%3B&dot=.&comma=%2C"
], false]
]
}
}
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
"uritemplate-test", "uritemplate-test",
"Jakefile.js", "Jakefile.js",
"own-testcases.json", "own-testcases.json",
"extract-spec-examples.json",
"demo.html", "demo.html",
"demo_AMD.html", "demo_AMD.html",
"README.md", "README.md",
......
/*jshint unused:false */ /*jshint unused:false */
/*global parse, objectHelper, UriTemplateError*/ /*global parse, objectHelper, UriTemplateError */
var UriTemplate = (function () { var UriTemplate = (function () {
"use strict"; "use strict";
function UriTemplate (templateText, expressions) { function UriTemplate (templateText, expressions, regexp_string) {
this.templateText = templateText; this.templateText = templateText;
this.expressions = expressions; this.expressions = expressions;
if (regexp_string !== undefined) {
this.regexp = new RegExp("^" + regexp_string + "$");
}
objectHelper.deepFreeze(this); objectHelper.deepFreeze(this);
} }
...@@ -23,6 +28,44 @@ var UriTemplate = (function () { ...@@ -23,6 +28,44 @@ var UriTemplate = (function () {
return result; return result;
}; };
UriTemplate.prototype.extract = function (text) {
var expression_index,
extracted_index = 1,
expression,
varspec,
matched = true,
variables = {},
result;
if ((this.regexp !== undefined) && (this.regexp.test(text))) {
result = this.regexp.exec(text);
for (expression_index = 0; expression_index < this.expressions.length; expression_index += 1) {
expression = this.expressions[expression_index];
if (expression.literal === undefined) {
if ((expression.operator !== undefined) && (expression.operator.symbol.length === 0) && (expression.varspecs.length === 1)) {
varspec = expression.varspecs[0];
if ((varspec.exploded === false) && (varspec.maxLength === null)) {
if (result[extracted_index].indexOf(',') === -1) {
variables[varspec.varname] = decodeURIComponent(result[extracted_index]);
extracted_index += 1;
} else {
matched = false;
}
} else {
matched = false;
}
} else {
matched = false;
}
}
}
if (matched) {
return variables;
}
}
return false;
};
UriTemplate.parse = parse; UriTemplate.parse = parse;
UriTemplate.UriTemplateError = UriTemplateError; UriTemplate.UriTemplateError = UriTemplateError;
return UriTemplate; return UriTemplate;
......
...@@ -112,13 +112,21 @@ var parse = (function () { ...@@ -112,13 +112,21 @@ var parse = (function () {
return new VariableExpression(expressionText, operator, varspecs); return new VariableExpression(expressionText, operator, varspecs);
} }
function escape_regexp_string(string) {
// http://simonwillison.net/2006/Jan/20/escape/
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
function parse (uriTemplateText) { function parse (uriTemplateText) {
// assert filled string // assert filled string
var var
index, index,
chr, chr,
expressions = [], expressions = [],
expression,
braceOpenIndex = null, braceOpenIndex = null,
regexp_string = '',
can_match = true,
literalStart = 0; literalStart = 0;
for (index = 0; index < uriTemplateText.length; index += 1) { for (index = 0; index < uriTemplateText.length; index += 1) {
chr = uriTemplateText.charAt(index); chr = uriTemplateText.charAt(index);
...@@ -128,7 +136,10 @@ var parse = (function () { ...@@ -128,7 +136,10 @@ var parse = (function () {
} }
if (chr === '{') { if (chr === '{') {
if (literalStart < index) { if (literalStart < index) {
expressions.push(new LiteralExpression(uriTemplateText.substring(literalStart, index))); expression = new LiteralExpression(uriTemplateText.substring(literalStart, index));
expressions.push(expression);
regexp_string += escape_regexp_string(
expression.literal);
} }
literalStart = null; literalStart = null;
braceOpenIndex = index; braceOpenIndex = index;
...@@ -146,7 +157,7 @@ var parse = (function () { ...@@ -146,7 +157,7 @@ var parse = (function () {
throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex}); throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex});
} }
try { try {
expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index))); expression = parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index));
} }
catch (error) { catch (error) {
if (error.prototype === UriTemplateError.prototype) { if (error.prototype === UriTemplateError.prototype) {
...@@ -154,6 +165,12 @@ var parse = (function () { ...@@ -154,6 +165,12 @@ var parse = (function () {
} }
throw error; throw error;
} }
expressions.push(expression);
if (expression.operator.symbol.length === 0) {
regexp_string += "([^/]+)";
} else {
can_match = false;
}
braceOpenIndex = null; braceOpenIndex = null;
literalStart = index + 1; literalStart = index + 1;
} }
...@@ -165,9 +182,14 @@ var parse = (function () { ...@@ -165,9 +182,14 @@ var parse = (function () {
throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex}); throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex});
} }
if (literalStart < uriTemplateText.length) { if (literalStart < uriTemplateText.length) {
expressions.push(new LiteralExpression(uriTemplateText.substr(literalStart))); expression = new LiteralExpression(uriTemplateText.substring(literalStart));
expressions.push(expression);
regexp_string += escape_regexp_string(expression.literal);
}
if (can_match === false) {
regexp_string = undefined;
} }
return new UriTemplate(uriTemplateText, expressions); return new UriTemplate(uriTemplateText, expressions, regexp_string);
} }
return parse; return parse;
......
...@@ -77,6 +77,44 @@ module.exports = (function () { ...@@ -77,6 +77,44 @@ module.exports = (function () {
} }
} }
function assertExtractMatches (test, template, variables, expected, extracted, chapterName, UriTemplate) {
var
uriTemplate,
actual,
expected_extracted = {},
index;
try {
uriTemplate = UriTemplate.parse(template);
}
catch (error) {
if (expected === false && error.constructor.prototype === UriTemplate.UriTemplateError.prototype) {
log('ok. expected error found', error);
return;
}
console.log('error', error);
console.log('error.constructor.prototype', error.constructor.prototype);
test.fail('chapter ' + chapterName + ', template ' + template + ' threw error: ' + error);
return;
}
test.ok(!!uriTemplate, 'uri template could not be parsed');
try {
actual = uriTemplate.extract(expected);
}
catch (error) {
console.log('error', error);
test.fail('chapter ' + chapterName + ', template "' + template + '" threw error, when extracting: ' + JSON.stringify(error, null, 4));
return;
}
if (extracted === false) {
expected_extracted = false;
} else {
for (index = 0; index < extracted.length; index += 1) {
expected_extracted[extracted[index]] = variables[extracted[index]];
}
}
test.deepEqual(actual, expected_extracted, 'actual: "' + JSON.stringify(actual) + '", expected_extracted: "' + JSON.stringify(expected_extracted) + '", template: "' + template + '"');
}
function runTestFile (test, filename) { function runTestFile (test, filename) {
var var
tests, tests,
...@@ -106,6 +144,38 @@ module.exports = (function () { ...@@ -106,6 +144,38 @@ module.exports = (function () {
test.done(); test.done();
} }
function runExtractTestFile (test, filename) {
var
tests,
chapterName,
chapter,
variables,
index,
template,
expexted,
extracted,
UriTemplate;
log(filename);
UriTemplate = loadUriTemplate();
tests = loadTestFile(filename);
for (chapterName in tests) {
if (tests.hasOwnProperty(chapterName)) {
log('-> ' + chapterName);
chapter = tests[chapterName];
variables = chapter.variables;
for (index = 0; index < chapter.testcases.length; index += 1) {
template = chapter.testcases[index][0];
expexted = chapter.testcases[index][1];
extracted = chapter.testcases[index][2];
log(' -> ' + template);
assertExtractMatches(test, template, variables, expexted,
extracted, chapterName, UriTemplate);
}
}
}
test.done();
}
var SPEC_HOME = 'uritemplate-test'; var SPEC_HOME = 'uritemplate-test';
return { return {
...@@ -127,6 +197,9 @@ module.exports = (function () { ...@@ -127,6 +197,9 @@ module.exports = (function () {
'testfile': function (test) { 'testfile': function (test) {
test.ok(global.URI_TEMPLATE_FILE.substr(0, 3), 'tmp'); test.ok(global.URI_TEMPLATE_FILE.substr(0, 3), 'tmp');
test.done(); test.done();
},
'extract spec examples': function (test) {
runExtractTestFile(test, 'extract-spec-examples.json');
} }
}; };
}()); }());
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