├── .editorconfig ├── .gitignore ├── README.md ├── UNLICENSE ├── index.js ├── package.json └── test └── IRI.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 3 8 | end_of_line = lf 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # stryker temp files 4 | .stryker-tmp 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IRI: A utility for converting and parsing URIs and IRIs 2 | 3 | Utilities for using RFC 3987 and 4 | RFC 3986 5 | 6 | IRIs are unicode URIs, URIs by definition being 7-bit characters. 7 | 8 | ## Construction of the URI 9 | 10 | From RFC 3986: 11 | 12 | foo://example.com:8042/over/there?name=ferret#nose 13 | \_/ \______________/\_________/ \_________/ \__/ 14 | | | | | | 15 | scheme authority path query fragment 16 | | _____________________|__ 17 | / \ / \ 18 | urn:example:animal:ferret:nose 19 | 20 | The authority exists in a URI/IRI and is marked by a leading `//`. It can be broken down into a number of other components: 21 | 22 | root:hunter2@[::1]:8080 23 | \__________/ \___/ \__/ 24 | | | | 25 | userinfo host port 26 | 27 | ## Usage of `IRI` 28 | 29 | The constructor takes a single argument, a URI or IRI string: 30 | 31 | var iri = require('iri'); 32 | var path = new iri.IRI(str).resolveReference('/'); 33 | 34 | ### toString() 35 | 36 | Returns UTF-16 IRI 37 | 38 | ### defrag() 39 | 40 | Returns the IRI without the fragment component. Useful for dereferencing URLs on a network. 41 | 42 | new IRI().defrag() === 'http://example.com/resource' 43 | 44 | ### isAbsolute() 45 | 46 | IRIs with a fragment are not absolute. 47 | 48 | ### toAbsolute() 49 | 50 | Resolves the IRI against itself, having the effect of stripping the fragment and checking that the supplied IRI is valid (absolute). 51 | 52 | ### authority() 53 | 54 | Returns the authority component of the IRI, such as "user:password@example.com:8080" 55 | 56 | ### fragment() 57 | 58 | Returns the fragment component of the IRI. 59 | 60 | ### hierpart() 61 | 62 | Returns the hier-part of the IRI, the hierarchial component: Everything between the scheme and query, including leading `//` for the host, if it exists. 63 | 64 | ### host() 65 | 66 | Returns the host component of the URI, either a domain name or string-formatted IP address. Excludes port number and username/password. 67 | 68 | ### path() 69 | 70 | Returns the path component of the hier-part. Does not include the authority/host, query, or fragment. 71 | 72 | ### port() 73 | 74 | Returns the port component of the authority as a string, or null if there is no port. 75 | 76 | ### query() 77 | 78 | Returns the query component of the IRI including leading "?", or `null` if there is no query component. 79 | 80 | ### resolveReference(ref) 81 | 82 | Resolve the provided URI/IRI reference against this IRI. 83 | 84 | ### scheme() 85 | 86 | Returns the scheme of the IRI, e.g. "https", "file", or "urn". 87 | 88 | ### userinfo() 89 | 90 | Returns the username/password component of the IRI. 91 | 92 | ### toURIString() 93 | 94 | Returns a URI formatted string with only 7-bit characters. 95 | 96 | ### toIRIString() 97 | 98 | Decodes URI-encoded UTF-8 characters and returns a unicode string (Strings in ECMAScript/JavaScript are UTF-16). 99 | 100 | ### toIRI() 101 | 102 | Returns a new IRI object with URI-encoded UTF-8 characters decoded. 103 | 104 | 105 | ## Function Usage 106 | 107 | ### iri.fromURI(uri) 108 | 109 | Returns an iri.IRI object with UTF-8 escaped characterd decoded. 110 | 111 | ### iri.toIRIString(uri) 112 | 113 | Returns an IRI string decoded from the given URI. 114 | 115 | 116 | ## Tests 117 | 118 | Tests are available as a Vows test suite. Run `vows` in the package directory to execute. 119 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var api = exports; 4 | 5 | api.encodeString = function encodeString(s) { 6 | var out = ""; 7 | var skip = false; 8 | var _g1 = 0, _g = s.length; 9 | while(_g1 < _g) { 10 | var i = _g1++; 11 | if(!skip) { 12 | var code = s.charCodeAt(i); 13 | if(55296 <= code && code <= 56319) { 14 | var low = s.charCodeAt(i + 1); 15 | code = (code - 55296) * 1024 + (low - 56320) + 65536; 16 | skip = true; 17 | } 18 | if(code > 1114111) { throw new Error("Char out of range"); } 19 | var hex = "00000000".concat((new Number(code)).toString(16).toUpperCase()); 20 | if(code >= 65536) { 21 | out += "\\U" + hex.slice(-8); 22 | } else { 23 | if(code >= 127 || code <= 31) { 24 | switch(code) { 25 | case 9: out += "\\t"; break; 26 | case 10: out += "\\n"; break; 27 | case 13: out += "\\r"; break; 28 | default: out += "\\u" + hex.slice(-4); break; 29 | } 30 | } else { 31 | switch(code) { 32 | case 34: out += '\\"'; break; 33 | case 92: out += "\\\\"; break; 34 | default: out += s.charAt(i); break; 35 | } 36 | } 37 | } 38 | } else { 39 | skip = !skip; 40 | } 41 | } 42 | return out; 43 | }; 44 | 45 | /** 46 | * IRI 47 | */ 48 | api.IRI = IRI; 49 | function IRI(iri) { this.value = iri; } 50 | IRI.SCHEME_MATCH = new RegExp("^[a-z0-9-.+]+:", "i"); 51 | //IRI.prototype = new api.RDFNode; 52 | IRI.prototype.toString = function toString() { return this.value; }; 53 | IRI.prototype.nodeType = function nodeType() { return "IRI"; }; 54 | IRI.prototype.toNT = function toNT() { return "<" + api.encodeString(this.value) + ">"; }; 55 | IRI.prototype.n3 = function n3() { return this.toNT(); }; 56 | IRI.prototype.defrag = function defrag() { 57 | var i = this.value.indexOf("#"); 58 | return (i < 0) ? this : new IRI(this.value.slice(0, i)); 59 | }; 60 | IRI.prototype.isAbsolute = function isAbsolute() { 61 | return this.scheme()!=null && this.hierpart()!=null && this.fragment()==null; 62 | }; 63 | IRI.prototype.toAbsolute = function toAbsolute() { 64 | if(this.scheme() == null || this.hierpart() == null) { throw new Error("IRI must have a scheme and a hierpart!"); } 65 | return this.resolveReference(this.value).defrag(); 66 | }; 67 | IRI.prototype.getAuthority = function authority() { 68 | var hierpart = this.hierpart(); 69 | if(hierpart.substring(0, 2) != "//") return null; 70 | var authority = hierpart.slice(2); 71 | var q = authority.indexOf("/"); 72 | return q>=0 ? authority.substring(0, q) : authority; 73 | }; 74 | IRI.prototype.getFragment = function fragment() { 75 | var i = this.value.indexOf("#"); 76 | return (i<0) ? null : this.value.slice(i); 77 | }; 78 | IRI.prototype.getHierpart = function hierpart() { 79 | var hierpart = this.value; 80 | var q = hierpart.indexOf("?"); 81 | if(q >= 0) { 82 | hierpart = hierpart.substring(0, q); 83 | } else { 84 | q = hierpart.indexOf("#"); 85 | if(q >= 0) hierpart = hierpart.substring(0, q); 86 | } 87 | var q2 = this.scheme(); 88 | if(q2 != null) hierpart = hierpart.slice(1 + q2.length); 89 | return hierpart; 90 | }; 91 | IRI.prototype.getHost = function host() { 92 | var host = this.authority(); 93 | var q = host.indexOf("@"); 94 | if(q >= 0) host = host.slice(++q); 95 | if(host.indexOf("[") == 0) { 96 | q = host.indexOf("]"); 97 | if(q > 0) return host.substring(0, q); 98 | } 99 | q = host.lastIndexOf(":"); 100 | return q >= 0 ? host.substring(0, q) : host; 101 | }; 102 | IRI.prototype.getPath = function path() { 103 | var q = this.authority(); 104 | if(q == null) return this.hierpart(); 105 | return this.hierpart().slice(q.length + 2); 106 | }; 107 | IRI.prototype.getPort = function port() { 108 | var host = this.authority(); 109 | var q = host.indexOf("@"); 110 | if(q >= 0) host = host.slice(++q); 111 | if(host.indexOf("[") == 0) { 112 | q = host.indexOf("]"); 113 | if(q > 0) return host.substring(0, q); 114 | } 115 | q = host.lastIndexOf(":"); 116 | if(q < 0) return null; 117 | host = host.slice(++q); 118 | return host.length == 0 ? null : host; 119 | }; 120 | IRI.prototype.getQuery = function query() { 121 | var q = this.value.indexOf("?"); 122 | if(q < 0) return null; 123 | var f = this.value.indexOf("#"); 124 | if(f < 0) return this.value.slice(q); 125 | return this.value.substring(q, f); 126 | }; 127 | api.removeDotSegments = function removeDotSegments(input) { 128 | var output = ""; 129 | var q = 0; 130 | while(input.length > 0) { 131 | if(input.substr(0, 3) == "../" || input.substr(0, 2) == "./") { 132 | input = input.slice(input.indexOf("/")); 133 | }else if(input == "/.") { 134 | input = "/"; 135 | }else if(input.substr(0, 3) == "/./") { 136 | input = input.slice(2); 137 | }else if(input.substr(0, 4) == "/../" || input == "/..") { 138 | input = (input=="/..") ? "/" : input.slice(3); 139 | q = output.lastIndexOf("/"); 140 | output = (q>=0) ? output.substring(0, q) : ""; 141 | }else if(input.substr(0, 2) == ".." || input.substr(0, 1) == ".") { 142 | input = input.slice(input.indexOf(".")); 143 | q = input.indexOf("."); 144 | if(q >= 0) input = input.slice(q); 145 | }else { 146 | if(input.substr(0, 1) == "/") { 147 | output += "/"; 148 | input = input.slice(1); 149 | } 150 | q = input.indexOf("/"); 151 | if(q < 0) { 152 | output += input; 153 | input = ""; 154 | }else { 155 | output += input.substring(0, q); 156 | input = input.slice(q); 157 | } 158 | } 159 | } 160 | return output; 161 | }; 162 | IRI.prototype.resolveReference = function resolveReference(ref) { 163 | var reference; 164 | if(typeof ref == "string") { 165 | reference = new IRI(ref); 166 | }else if(ref.nodeType && ref.nodeType() == "IRI") { 167 | reference = ref; 168 | }else { 169 | throw new Error("Expected IRI or String"); 170 | } 171 | var T = {scheme:"", authority:"", path:"", query:"", fragment:""}; 172 | var q = ""; 173 | if(reference.scheme() != null) { 174 | T.scheme = reference.scheme(); 175 | q = reference.authority(); 176 | T.authority += q!=null ? "//"+q : ""; 177 | T.path = api.removeDotSegments(reference.path()); 178 | T.query += reference.query()||''; 179 | }else { 180 | q = reference.authority(); 181 | if(q != null) { 182 | T.authority = q!=null ? "//"+q : ""; 183 | T.path = api.removeDotSegments(reference.path()); 184 | T.query += reference.query()||''; 185 | }else { 186 | q = reference.path(); 187 | if(q == "" || q == null) { 188 | T.path = this.path(); 189 | q = reference.query(); 190 | if(q != null) { 191 | T.query += q; 192 | }else { 193 | q = this.query(); 194 | T.query += q!=null ? q : ""; 195 | } 196 | }else { 197 | if(q.substring(0, 1) == "/") { 198 | T.path = api.removeDotSegments(q); 199 | }else { 200 | if(this.path() != null) { 201 | var q2 = this.path().lastIndexOf("/"); 202 | if(q2 >= 0) { 203 | T.path = this.path().substring(0, ++q2); 204 | } 205 | T.path += reference.path(); 206 | }else { 207 | T.path = "/" + q; 208 | } 209 | T.path = api.removeDotSegments(T.path); 210 | } 211 | T.query += reference.query()||''; 212 | } 213 | q = this.authority(); 214 | T.authority = q!=null ? "//" + q : ""; 215 | } 216 | T.scheme = this.scheme(); 217 | } 218 | T.fragment = reference.fragment()||''; 219 | return new IRI(T.scheme + ":" + T.authority + T.path + T.query + T.fragment); 220 | }; 221 | IRI.prototype.getScheme = function scheme() { 222 | var scheme = this.value.match(IRI.SCHEME_MATCH); 223 | return (scheme == null) ? null : scheme.shift().slice(0, -1); 224 | }; 225 | IRI.prototype.getUserinfo = function userinfo() { 226 | var authority = this.authority(); 227 | var q = authority.indexOf("@"); 228 | return (q < 0) ? null : authority.substring(0, q); 229 | }; 230 | IRI.prototype.toURIString = function toURIString(){ 231 | return this.value.replace(/([\uA0-\uD7FF\uE000-\uFDCF\uFDF0-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g, function(a){return encodeURI(a);}); 232 | }; 233 | IRI.prototype.toIRIString = function toIRIString(){ 234 | // HEXDIG requires capital characters 235 | // 80-BF is following bytes, (%[89AB][0-9A-F]) 236 | // 00-7F no bytes follow (%[0-7][0-9A-F])(%[89AB][0-9A-F]){0} 237 | // C0-DF one byte follows (%[CD][0-9A-F])(%[89AB][0-9A-F]){1} 238 | // E0-EF two bytes follow (%[E][0-9A-F])(%[89AB][0-9A-F]){2} 239 | // F0-F7 three bytes follow (%[F][0-7])(%[89AB][0-9A-F]){3} 240 | // F8-FB four bytes follow (%[F][89AB])(%[89AB][0-9A-F]){4} 241 | // FC-FD five bytes follow (%[F][CD])(%[89AB][0-9A-F]){5} 242 | var utf8regexp = /%([2-7][0-9A-F])|%[CD][0-9A-F](%[89AB][0-9A-F])|%[E][0-9A-F](%[89AB][0-9A-F]){2}|%[F][0-7](%[89AB][0-9A-F]){3}|%[F][89AB](%[89AB][0-9A-F]){4}|%[F][CD](%[89AB][0-9A-F]){5}/g; 243 | // reserved characters := gen-delims, space, and sub-delims 244 | // : / ? # [ ] @ ! $ & ' ( ) * + , ; = 245 | var reserved = [ '3A', '2F', '3F', '23', '5B', '5D', '40', '20', '21', '24', '26', '27', '28', '29', '2A', '2B', '2C', '3B', '3D']; 246 | var iri = this.toString().replace(utf8regexp, function(a, b){ 247 | if(reserved.indexOf(b)>=0) return a; 248 | return decodeURIComponent(a); 249 | }); 250 | return iri; 251 | }; 252 | 253 | IRI.prototype.toIRI = function toIRI(){ 254 | return new IRI(this.toIRIString()); 255 | }; 256 | 257 | // Alias old names to new ones 258 | IRI.prototype.authority = IRI.prototype.getAuthority; 259 | IRI.prototype.hierpart = IRI.prototype.getHierpart; 260 | IRI.prototype.scheme = IRI.prototype.getScheme; 261 | IRI.prototype.path = IRI.prototype.getPath; 262 | IRI.prototype.query = IRI.prototype.getQuery; 263 | IRI.prototype.fragment = IRI.prototype.getFragment; 264 | IRI.prototype.userinfo = IRI.prototype.getUserinfo; 265 | IRI.prototype.host = IRI.prototype.getHost; 266 | IRI.prototype.port = IRI.prototype.getPort; 267 | 268 | // Create a new IRI object and decode UTF-8 escaped characters 269 | api.fromURI = function fromURI(uri){ 270 | return new IRI(uri).toIRI(); 271 | }; 272 | 273 | api.toIRIString = function toIRIString(uri){ 274 | return new IRI(uri).toIRIString(); 275 | }; 276 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iri", 3 | "description": "IRI parsing and IRI-URI conversion utilities", 4 | "homepage": "https://github.com/awwright/node-iri", 5 | "author": "Austin Wright", 6 | "keywords": [ 7 | "IRI" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/awwright/node-iri.git", 12 | "web": "https://github.com/awwright/node-iri" 13 | }, 14 | "main": "index.js", 15 | "scripts": { 16 | "coverage": "stryker run", 17 | "test": "mocha" 18 | }, 19 | "license": "Unlicense", 20 | "devDependencies": { 21 | "@stryker-mutator/core": "^6.1.2", 22 | "@stryker-mutator/mocha-runner": "^6.1.2", 23 | "eslint": "^8.21.0", 24 | "mocha": "^10.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/IRI.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var assert = require('assert').strict; 4 | var iri = require('../'); 5 | var IRI = iri.IRI; 6 | 7 | function testConversion(irival, urival){ 8 | it(irival, function(){ 9 | assert.equal(new IRI(irival).toURIString(), urival); 10 | assert.equal(new IRI(urival).toIRIString(), irival); 11 | }); 12 | } 13 | 14 | describe('Interface', function(){ 15 | describe("new IRI()", function(){ 16 | var t = new IRI('http://example.com/'); 17 | it(".nodeType() === 'IRI'", function(){ assert.equal(t.scheme(), 'http'); }); 18 | it(".toNT()", function(){ assert.equal(t.toNT(), ''); }); 19 | it(".n3()", function(){ assert.equal(t.n3(), ''); }); 20 | it(".defrag() is self", function(){ assert.equal(t.defrag().value, 'http://example.com/'); }); 21 | it(".isAbsolute() is true", function(){ assert.equal(t.isAbsolute(), true); }); 22 | it(".toAbsolute() is self", function(){ assert.equal(t.toAbsolute().value, 'http://example.com/'); }); 23 | it(".authority() === 'example.com'", function(){ assert.equal(t.authority(), 'example.com'); }); 24 | it(".fragment() is null", function(){ assert.equal(t.fragment(), null); }); 25 | it(".hierpart() === '//example.com/'", function(){ assert.equal(t.hierpart(), '//example.com/'); }); 26 | it(".host() === 'example.com'", function(){ assert.equal(t.host(), 'example.com'); }); 27 | it(".path() === '/'", function(){ assert.equal(t.path(), '/'); }); 28 | it(".port() is null", function(){ assert.equal(t.port(), null); }); 29 | it(".query() is null", function(){ assert.equal(t.query(), null); }); 30 | it(".resolveReference(absoluteURI)", function(){ assert.equal(t.resolveReference('http://xyz.example.org/123').value, 'http://xyz.example.org/123'); }); 31 | it(".resolveReference(path)", function(){ assert.equal(t.resolveReference('/a/b/c').value, 'http://example.com/a/b/c'); }); 32 | it(".resolveReference(authority)", function(){ assert.equal(t.resolveReference('//example.org/1?x').value, 'http://example.org/1?x'); }); 33 | it(".resolveReference(relative)", function(){ assert.equal(t.resolveReference('b/c.js').value, 'http://example.com/b/c.js'); }); 34 | it(".resolveReference(decend)", function(){ assert.equal(t.resolveReference('../..').value, 'http://example.com/'); }); 35 | it(".resolveReference(query)", function(){ assert.equal(t.resolveReference('?query').value, 'http://example.com/?query'); }); 36 | it(".scheme() === 'http'", function(){ assert.equal(t.scheme(), 'http'); }); 37 | it(".userinfo() is null", function(){ assert.equal(t.userinfo(), null); }); 38 | }); 39 | describe("(new iri.IRI())", function(){ 40 | var t = new iri.IRI('https://user:pass@a.example.com:8080/b/c/d/?123&aa=1&aa=2#455'); 41 | it(".nodeType() === 'IRI'", function(){ assert.equal(t.nodeType(), 'IRI'); }); 42 | it(".toNT()", function(){ assert.equal(t.toNT(), ''); }); 43 | it(".n3()", function(){ assert.equal(t.n3(), ''); }); 44 | it(".defrag() strips fragment", function(){ assert.equal(t.defrag().value, 'https://user:pass@a.example.com:8080/b/c/d/?123&aa=1&aa=2'); }); 45 | it(".isAbsolute() is false", function(){ assert.equal(t.isAbsolute(), false); }); 46 | it(".toAbsolute() strips fragment", function(){ assert.equal(t.toAbsolute().value, 'https://user:pass@a.example.com:8080/b/c/d/?123&aa=1&aa=2'); }); 47 | it(".authority() === 'user:pass@a.example.com:8080'", function(){ assert.equal(t.authority(), 'user:pass@a.example.com:8080'); }); 48 | it(".fragment()", function(){ assert.equal(t.fragment(), '#455'); }); 49 | it(".hierpart()", function(){ assert.equal(t.hierpart(), '//user:pass@a.example.com:8080/b/c/d/'); }); 50 | it(".host() === 'a.example.com'", function(){ assert.equal(t.host(), 'a.example.com'); }); 51 | it(".path() === '/b/c/d/?123&aa=1&aa=2'", function(){ assert.equal(t.path(), '/b/c/d/'); }); 52 | it(".port() is '8080'", function(){ assert.equal(t.port(), '8080'); }); 53 | it(".query()", function(){ assert.equal(t.query(), '?123&aa=1&aa=2'); }); 54 | it(".resolveReference(absoluteURI)", function(){ assert.equal(t.resolveReference('http://xyz.example.org/123').value, 'http://xyz.example.org/123'); }); 55 | it(".resolveReference(path)", function(){ assert.equal(t.resolveReference('/a/b/c').value, 'https://user:pass@a.example.com:8080/a/b/c'); }); 56 | it(".resolveReference(authority)", function(){ assert.equal(t.resolveReference('//example.org/1?x').value, 'https://example.org/1?x'); }); 57 | it(".resolveReference(relative)", function(){ assert.equal(t.resolveReference('b/c.js').value, 'https://user:pass@a.example.com:8080/b/c/d/b/c.js'); }); 58 | it(".resolveReference(cwd)", function(){ assert.equal(t.resolveReference('.').value, 'https://user:pass@a.example.com:8080/b/c/d/'); }); 59 | it(".resolveReference(decend)", function(){ assert.equal(t.resolveReference('../..').value, 'https://user:pass@a.example.com:8080/b/'); }); 60 | it(".resolveReference(query)", function(){ assert.equal(t.resolveReference('?query').value, 'https://user:pass@a.example.com:8080/b/c/d/?query'); }); 61 | it(".scheme() === 'https'", function(){ assert.equal(t.scheme(), 'https'); }); 62 | it(".userinfo()", function(){ assert.equal(t.userinfo(), 'user:pass'); }); 63 | }); 64 | describe("(new iri.IRI())", function(){ 65 | // The examples from RFC 3986 66 | var t = new iri.IRI('http://a/b/c/d;p?q'); 67 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g:h").value, "g:h"); }); 68 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g").value, "http://a/b/c/g"); }); 69 | it(".resolveReference(<./g>)", function(){ assert.equal(t.resolveReference("./g").value, "http://a/b/c/g"); }); 70 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g/").value, "http://a/b/c/g/"); }); 71 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("/g").value, "http://a/g"); }); 72 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("//g").value, "http://g"); }); 73 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("?y").value, "http://a/b/c/d;p?y"); }); 74 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g?y").value, "http://a/b/c/g?y"); }); 75 | it(".resolveReference(<#s>)", function(){ assert.equal(t.resolveReference("#s").value, "http://a/b/c/d;p?q#s"); }); 76 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g#s").value, "http://a/b/c/g#s"); }); 77 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g?y#s").value, "http://a/b/c/g?y#s"); }); 78 | it(".resolveReference(<;x>)", function(){ assert.equal(t.resolveReference(";x").value, "http://a/b/c/;x"); }); 79 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g;x").value, "http://a/b/c/g;x"); }); 80 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g;x?y#s").value, "http://a/b/c/g;x?y#s"); }); 81 | it(".resolveReference(<>)", function(){ assert.equal(t.resolveReference("").value, "http://a/b/c/d;p?q"); }); 82 | it(".resolveReference(<.>)", function(){ assert.equal(t.resolveReference(".").value, "http://a/b/c/"); }); 83 | it(".resolveReference(<./>)", function(){ assert.equal(t.resolveReference("./").value, "http://a/b/c/"); }); 84 | it(".resolveReference(<..>)", function(){ assert.equal(t.resolveReference("..").value, "http://a/b/"); }); 85 | it(".resolveReference(<../>)", function(){ assert.equal(t.resolveReference("../").value, "http://a/b/"); }); 86 | it(".resolveReference(<../g>)", function(){ assert.equal(t.resolveReference("../g").value, "http://a/b/g"); }); 87 | it(".resolveReference(<../..>)", function(){ assert.equal(t.resolveReference("../..").value, "http://a/"); }); 88 | it(".resolveReference(<../../>)", function(){ assert.equal(t.resolveReference("../../").value, "http://a/"); }); 89 | it(".resolveReference(<../../g>)", function(){ assert.equal(t.resolveReference("../../g").value, "http://a/g"); }); 90 | it(".resolveReference(<../../../g>)", function(){ assert.equal(t.resolveReference("../../../g").value, "http://a/g"); }); 91 | it(".resolveReference(<../../../../g>)", function(){ assert.equal(t.resolveReference("../../../../g").value, "http://a/g"); }); 92 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("/./g").value, "http://a/g"); }); 93 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("/../g").value, "http://a/g"); }); 94 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g.").value, "http://a/b/c/g."); }); 95 | it(".resolveReference(<.g>)", function(){ assert.equal(t.resolveReference(".g").value, "http://a/b/c/.g"); }); 96 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g..").value, "http://a/b/c/g.."); }); 97 | it(".resolveReference(<..g>)", function(){ assert.equal(t.resolveReference("..g").value, "http://a/b/c/..g"); }); 98 | it(".resolveReference(<./../g>)", function(){ assert.equal(t.resolveReference("./../g").value, "http://a/b/g"); }); 99 | it(".resolveReference(<./g/.>)", function(){ assert.equal(t.resolveReference("./g/.").value, "http://a/b/c/g/"); }); 100 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g/./h").value, "http://a/b/c/g/h"); }); 101 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g/../h").value, "http://a/b/c/h"); }); 102 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g;x=1/./y").value, "http://a/b/c/g;x=1/y"); }); 103 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g;x=1/../y").value, "http://a/b/c/y"); }); 104 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g?y/./x").value, "http://a/b/c/g?y/./x"); }); 105 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g?y/../x").value, "http://a/b/c/g?y/../x"); }); 106 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g#s/./x").value, "http://a/b/c/g#s/./x"); }); 107 | it(".resolveReference()", function(){ assert.equal(t.resolveReference("g#s/../x").value, "http://a/b/c/g#s/../x"); }); 108 | }); 109 | describe("IRI to URI conversion", function(){ 110 | var t = new IRI('http://www.example.org/red%09ros\xE9#red'); 111 | it(".toURIString()", function(){ assert.equal(t.toURIString(), "http://www.example.org/red%09ros%C3%A9#red"); }); 112 | }); 113 | describe("IRI to URI conversion with surrogate pairs", function(){ 114 | var t = new IRI('http://example.com/\uD800\uDF00\uD800\uDF01\uD800\uDF02'); 115 | it(".toURIString()", function(){ assert.equal(t.toURIString(), "http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82"); }); 116 | }); 117 | describe("IRI<->URI conversion", function(){ 118 | describe("Standard examples", function(){ 119 | testConversion('http://www.example.org/red%09ros\xE9#red', 'http://www.example.org/red%09ros%C3%A9#red'); 120 | testConversion('http://example.com/\uD800\uDF00\uD800\uDF01\uD800\uDF02', 'http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82'); 121 | testConversion('http://www.example.org/r\xE9sum\xE9.html', 'http://www.example.org/r%C3%A9sum%C3%A9.html'); 122 | testConversion('http://www.example.org/%2F', 'http://www.example.org/%2F'); 123 | }); 124 | describe("Reserved characters are not encoded", function(){ 125 | testConversion('http://www.example.org/%3A', 'http://www.example.org/%3A'); 126 | testConversion('http://www.example.org/%2F', 'http://www.example.org/%2F'); 127 | testConversion('http://www.example.org/%3F', 'http://www.example.org/%3F'); 128 | testConversion('http://www.example.org/%23', 'http://www.example.org/%23'); 129 | testConversion('http://www.example.org/%5B', 'http://www.example.org/%5B'); 130 | testConversion('http://www.example.org/%5D', 'http://www.example.org/%5D'); 131 | testConversion('http://www.example.org/%40', 'http://www.example.org/%40'); 132 | testConversion('http://www.example.org/%20', 'http://www.example.org/%20'); 133 | testConversion('http://www.example.org/%21', 'http://www.example.org/%21'); 134 | testConversion('http://www.example.org/%24', 'http://www.example.org/%24'); 135 | testConversion('http://www.example.org/%26', 'http://www.example.org/%26'); 136 | testConversion('http://www.example.org/%27', 'http://www.example.org/%27'); 137 | testConversion('http://www.example.org/%28', 'http://www.example.org/%28'); 138 | testConversion('http://www.example.org/%29', 'http://www.example.org/%29'); 139 | testConversion('http://www.example.org/%2A', 'http://www.example.org/%2A'); 140 | testConversion('http://www.example.org/%2B', 'http://www.example.org/%2B'); 141 | testConversion('http://www.example.org/%2C', 'http://www.example.org/%2C'); 142 | testConversion('http://www.example.org/%3B', 'http://www.example.org/%3B'); 143 | testConversion('http://www.example.org/%3D', 'http://www.example.org/%3D'); 144 | }); 145 | }); 146 | }); 147 | --------------------------------------------------------------------------------