uri-js-rails 1.11.2 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,104 @@
1
+ /*
2
+ * Extending URI.js for fragment abuse
3
+ */
4
+
5
+ // --------------------------------------------------------------------------------
6
+ // EXAMPLE: storing application/x-www-form-urlencoded data in the fragment
7
+ // possibly helpful for Google's hashbangs
8
+ // see http://code.google.com/web/ajaxcrawling/
9
+ // --------------------------------------------------------------------------------
10
+
11
+ // Note: make sure this is the last file loaded!
12
+
13
+ // USAGE:
14
+ // var uri = URI("http://example.org/#?foo=bar");
15
+ // uri.fragment(true) === {foo: "bar"};
16
+ // uri.fragment({bar: "foo"});
17
+ // uri.toString() === "http://example.org/#?bar=foo";
18
+ // uri.addFragment("name", "value");
19
+ // uri.toString() === "http://example.org/#?bar=foo&name=value";
20
+ // uri.removeFragment("name");
21
+ // uri.toString() === "http://example.org/#?bar=foo";
22
+
23
+ (function (root, factory) {
24
+ 'use strict';
25
+ // https://github.com/umdjs/umd/blob/master/returnExports.js
26
+ if (typeof exports === 'object') {
27
+ // Node
28
+ module.exports = factory(require('./URI'));
29
+ } else if (typeof define === 'function' && define.amd) {
30
+ // AMD. Register as an anonymous module.
31
+ define(['./URI'], factory);
32
+ } else {
33
+ // Browser globals (root is window)
34
+ factory(root.URI);
35
+ }
36
+ }(this, function (URI) {
37
+ 'use strict';
38
+
39
+ var p = URI.prototype;
40
+ // old fragment handler we need to wrap
41
+ var f = p.fragment;
42
+
43
+ // make fragmentPrefix configurable
44
+ URI.fragmentPrefix = '?';
45
+ var _parts = URI._parts;
46
+ URI._parts = function() {
47
+ var parts = _parts();
48
+ parts.fragmentPrefix = URI.fragmentPrefix;
49
+ return parts;
50
+ };
51
+ p.fragmentPrefix = function(v) {
52
+ this._parts.fragmentPrefix = v;
53
+ return this;
54
+ };
55
+
56
+ // add fragment(true) and fragment({key: value}) signatures
57
+ p.fragment = function(v, build) {
58
+ var prefix = this._parts.fragmentPrefix;
59
+ var fragment = this._parts.fragment || '';
60
+
61
+ if (v === true) {
62
+ if (fragment.substring(0, prefix.length) !== prefix) {
63
+ return {};
64
+ }
65
+
66
+ return URI.parseQuery(fragment.substring(prefix.length));
67
+ } else if (v !== undefined && typeof v !== 'string') {
68
+ this._parts.fragment = prefix + URI.buildQuery(v);
69
+ this.build(!build);
70
+ return this;
71
+ } else {
72
+ return f.call(this, v, build);
73
+ }
74
+ };
75
+ p.addFragment = function(name, value, build) {
76
+ var prefix = this._parts.fragmentPrefix;
77
+ var data = URI.parseQuery((this._parts.fragment || '').substring(prefix.length));
78
+ URI.addQuery(data, name, value);
79
+ this._parts.fragment = prefix + URI.buildQuery(data);
80
+ if (typeof name !== 'string') {
81
+ build = value;
82
+ }
83
+
84
+ this.build(!build);
85
+ return this;
86
+ };
87
+ p.removeFragment = function(name, value, build) {
88
+ var prefix = this._parts.fragmentPrefix;
89
+ var data = URI.parseQuery((this._parts.fragment || '').substring(prefix.length));
90
+ URI.removeQuery(data, name, value);
91
+ this._parts.fragment = prefix + URI.buildQuery(data);
92
+ if (typeof name !== 'string') {
93
+ build = value;
94
+ }
95
+
96
+ this.build(!build);
97
+ return this;
98
+ };
99
+ p.addHash = p.addFragment;
100
+ p.removeHash = p.removeFragment;
101
+
102
+ // extending existing object rather than defining something new
103
+ return URI;
104
+ }));
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Extending URI.js for fragment abuse
3
+ */
4
+
5
+ // --------------------------------------------------------------------------------
6
+ // EXAMPLE: storing a relative URL in the fragment ("FragmentURI")
7
+ // possibly helpful when working with backbone.js or sammy.js
8
+ // inspired by https://github.com/medialize/URI.js/pull/2
9
+ // --------------------------------------------------------------------------------
10
+
11
+ // Note: make sure this is the last file loaded!
12
+
13
+ // USAGE:
14
+ // var uri = URI("http://example.org/#!/foo/bar/baz.html");
15
+ // var furi = uri.fragment(true);
16
+ // furi.pathname() === '/foo/bar/baz.html';
17
+ // furi.pathname('/hello.html');
18
+ // uri.toString() === "http://example.org/#!/hello.html"
19
+
20
+ (function (root, factory) {
21
+ 'use strict';
22
+ // https://github.com/umdjs/umd/blob/master/returnExports.js
23
+ if (typeof exports === 'object') {
24
+ // Node
25
+ module.exports = factory(require('./URI'));
26
+ } else if (typeof define === 'function' && define.amd) {
27
+ // AMD. Register as an anonymous module.
28
+ define(['./URI'], factory);
29
+ } else {
30
+ // Browser globals (root is window)
31
+ factory(root.URI);
32
+ }
33
+ }(this, function (URI) {
34
+ 'use strict';
35
+
36
+ var p = URI.prototype;
37
+ // old handlers we need to wrap
38
+ var f = p.fragment;
39
+ var b = p.build;
40
+
41
+ // make fragmentPrefix configurable
42
+ URI.fragmentPrefix = '!';
43
+ var _parts = URI._parts;
44
+ URI._parts = function() {
45
+ var parts = _parts();
46
+ parts.fragmentPrefix = URI.fragmentPrefix;
47
+ return parts;
48
+ };
49
+ p.fragmentPrefix = function(v) {
50
+ this._parts.fragmentPrefix = v;
51
+ return this;
52
+ };
53
+
54
+ // add fragment(true) and fragment(URI) signatures
55
+ p.fragment = function(v, build) {
56
+ var prefix = this._parts.fragmentPrefix;
57
+ var fragment = this._parts.fragment || '';
58
+ var furi;
59
+
60
+ if (v === true) {
61
+ if (fragment.substring(0, prefix.length) !== prefix) {
62
+ furi = URI('');
63
+ } else {
64
+ furi = new URI(fragment.substring(prefix.length));
65
+ }
66
+
67
+ this._fragmentURI = furi;
68
+ furi._parentURI = this;
69
+ return furi;
70
+ } else if (v !== undefined && typeof v !== 'string') {
71
+ this._fragmentURI = v;
72
+ v._parentURI = v;
73
+ this._parts.fragment = prefix + v.toString();
74
+ this.build(!build);
75
+ return this;
76
+ } else if (typeof v === 'string') {
77
+ this._fragmentURI = undefined;
78
+ }
79
+
80
+ return f.call(this, v, build);
81
+ };
82
+
83
+ // make .build() of the actual URI aware of the FragmentURI
84
+ p.build = function(deferBuild) {
85
+ var t = b.call(this, deferBuild);
86
+
87
+ if (deferBuild !== false && this._parentURI) {
88
+ // update the parent
89
+ this._parentURI.fragment(this);
90
+ }
91
+
92
+ return t;
93
+ };
94
+
95
+ // extending existing object rather than defining something new
96
+ return URI;
97
+ }));
@@ -1,10 +1,10 @@
1
1
  /*!
2
2
  * URI.js - Mutating URLs
3
3
  *
4
- * Version: 1.11.2
4
+ * Version: 1.14.1
5
5
  *
6
6
  * Author: Rodney Rehm
7
- * Web: http://medialize.github.com/URI.js/
7
+ * Web: http://medialize.github.io/URI.js/
8
8
  *
9
9
  * Licensed under
10
10
  * MIT License http://www.opensource.org/licenses/mit-license
@@ -12,190 +12,207 @@
12
12
  *
13
13
  */
14
14
  (function (root, factory) {
15
- // https://github.com/umdjs/umd/blob/master/returnExports.js
16
- if (typeof exports === 'object') {
17
- // Node
18
- module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains'));
19
- } else if (typeof define === 'function' && define.amd) {
20
- // AMD. Register as an anonymous module.
21
- define(['./punycode', './IPv6', './SecondLevelDomains'], factory);
22
- } else {
23
- // Browser globals (root is window)
24
- root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root);
25
- }
15
+ 'use strict';
16
+ // https://github.com/umdjs/umd/blob/master/returnExports.js
17
+ if (typeof exports === 'object') {
18
+ // Node
19
+ module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains'));
20
+ } else if (typeof define === 'function' && define.amd) {
21
+ // AMD. Register as an anonymous module.
22
+ define(['./punycode', './IPv6', './SecondLevelDomains'], factory);
23
+ } else {
24
+ // Browser globals (root is window)
25
+ root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root);
26
+ }
26
27
  }(this, function (punycode, IPv6, SLD, root) {
27
- "use strict";
28
+ 'use strict';
29
+ /*global location, escape, unescape */
30
+ // FIXME: v2.0.0 renamce non-camelCase properties to uppercase
31
+ /*jshint camelcase: false */
28
32
 
29
- // save current URI variable, if any
30
- var _URI = root && root.URI;
33
+ // save current URI variable, if any
34
+ var _URI = root && root.URI;
31
35
 
32
- function URI(url, base) {
36
+ function URI(url, base) {
33
37
  // Allow instantiation without the 'new' keyword
34
38
  if (!(this instanceof URI)) {
35
- return new URI(url, base);
39
+ return new URI(url, base);
36
40
  }
37
41
 
38
42
  if (url === undefined) {
39
- if (typeof location !== 'undefined') {
40
- url = location.href + "";
41
- } else {
42
- url = "";
43
- }
43
+ if (typeof location !== 'undefined') {
44
+ url = location.href + '';
45
+ } else {
46
+ url = '';
47
+ }
44
48
  }
45
49
 
46
50
  this.href(url);
47
51
 
48
52
  // resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor
49
53
  if (base !== undefined) {
50
- return this.absoluteTo(base);
54
+ return this.absoluteTo(base);
51
55
  }
52
56
 
53
57
  return this;
54
- };
58
+ }
59
+
60
+ URI.version = '1.14.1';
55
61
 
56
- var p = URI.prototype;
57
- var hasOwn = Object.prototype.hasOwnProperty;
62
+ var p = URI.prototype;
63
+ var hasOwn = Object.prototype.hasOwnProperty;
58
64
 
59
- function escapeRegEx(string) {
65
+ function escapeRegEx(string) {
60
66
  // https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963
61
67
  return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
62
- }
68
+ }
63
69
 
64
- function getType(value) {
70
+ function getType(value) {
65
71
  // IE8 doesn't return [Object Undefined] but [Object Object] for undefined value
66
72
  if (value === undefined) {
67
- return 'Undefined';
73
+ return 'Undefined';
68
74
  }
69
75
 
70
76
  return String(Object.prototype.toString.call(value)).slice(8, -1);
71
- }
77
+ }
72
78
 
73
- function isArray(obj) {
74
- return getType(obj) === "Array";
75
- }
79
+ function isArray(obj) {
80
+ return getType(obj) === 'Array';
81
+ }
76
82
 
77
- function filterArrayValues(data, value) {
83
+ function filterArrayValues(data, value) {
78
84
  var lookup = {};
79
85
  var i, length;
80
86
 
81
87
  if (isArray(value)) {
82
- for (i = 0, length = value.length; i < length; i++) {
83
- lookup[value[i]] = true;
84
- }
88
+ for (i = 0, length = value.length; i < length; i++) {
89
+ lookup[value[i]] = true;
90
+ }
85
91
  } else {
86
- lookup[value] = true;
92
+ lookup[value] = true;
87
93
  }
88
94
 
89
95
  for (i = 0, length = data.length; i < length; i++) {
90
- if (lookup[data[i]] !== undefined) {
91
- data.splice(i, 1);
92
- length--;
93
- i--;
94
- }
96
+ if (lookup[data[i]] !== undefined) {
97
+ data.splice(i, 1);
98
+ length--;
99
+ i--;
100
+ }
95
101
  }
96
102
 
97
103
  return data;
98
- }
104
+ }
99
105
 
100
- function arrayContains(list, value) {
106
+ function arrayContains(list, value) {
101
107
  var i, length;
102
-
108
+
103
109
  // value may be string, number, array, regexp
104
110
  if (isArray(value)) {
105
- // Note: this can be optimized to O(n) (instead of current O(m * n))
106
- for (i = 0, length = value.length; i < length; i++) {
107
- if (!arrayContains(list, value[i])) {
108
- return false;
109
- }
111
+ // Note: this can be optimized to O(n) (instead of current O(m * n))
112
+ for (i = 0, length = value.length; i < length; i++) {
113
+ if (!arrayContains(list, value[i])) {
114
+ return false;
110
115
  }
111
-
112
- return true;
116
+ }
117
+
118
+ return true;
113
119
  }
114
-
120
+
115
121
  var _type = getType(value);
116
122
  for (i = 0, length = list.length; i < length; i++) {
117
- if (_type === 'RegExp') {
118
- if (typeof list[i] === 'string' && list[i].match(value)) {
119
- return true;
120
- }
121
- } else if (list[i] === value) {
122
- return true;
123
+ if (_type === 'RegExp') {
124
+ if (typeof list[i] === 'string' && list[i].match(value)) {
125
+ return true;
123
126
  }
127
+ } else if (list[i] === value) {
128
+ return true;
129
+ }
124
130
  }
125
131
 
126
132
  return false;
127
- }
133
+ }
128
134
 
129
- function arraysEqual(one, two) {
135
+ function arraysEqual(one, two) {
130
136
  if (!isArray(one) || !isArray(two)) {
131
- return false;
137
+ return false;
132
138
  }
133
-
139
+
134
140
  // arrays can't be equal if they have different amount of content
135
141
  if (one.length !== two.length) {
136
- return false;
142
+ return false;
137
143
  }
138
144
 
139
145
  one.sort();
140
146
  two.sort();
141
147
 
142
148
  for (var i = 0, l = one.length; i < l; i++) {
143
- if (one[i] !== two[i]) {
144
- return false;
145
- }
149
+ if (one[i] !== two[i]) {
150
+ return false;
151
+ }
146
152
  }
147
-
153
+
148
154
  return true;
149
- }
155
+ }
150
156
 
151
- URI._parts = function() {
157
+ URI._parts = function() {
152
158
  return {
153
- protocol: null,
154
- username: null,
155
- password: null,
156
- hostname: null,
157
- urn: null,
158
- port: null,
159
- path: null,
160
- query: null,
161
- fragment: null,
162
- // state
163
- duplicateQueryParameters: URI.duplicateQueryParameters,
164
- escapeQuerySpace: URI.escapeQuerySpace
159
+ protocol: null,
160
+ username: null,
161
+ password: null,
162
+ hostname: null,
163
+ urn: null,
164
+ port: null,
165
+ path: null,
166
+ query: null,
167
+ fragment: null,
168
+ // state
169
+ duplicateQueryParameters: URI.duplicateQueryParameters,
170
+ escapeQuerySpace: URI.escapeQuerySpace
165
171
  };
166
- };
167
- // state: allow duplicate query parameters (a=1&a=1)
168
- URI.duplicateQueryParameters = false;
169
- // state: replaces + with %20 (space in query strings)
170
- URI.escapeQuerySpace = true;
171
- // static properties
172
- URI.protocol_expression = /^[a-z][a-z0-9-+-]*$/i;
173
- URI.idn_expression = /[^a-z0-9\.-]/i;
174
- URI.punycode_expression = /(xn--)/i;
175
- // well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care?
176
- URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
177
- // credits to Rich Brown
178
- // source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
179
- // specification: http://www.ietf.org/rfc/rfc4291.txt
180
- URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
181
- // gruber revised expression - http://rodneyrehm.de/t/url-regex.html
182
- URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
183
- // http://www.iana.org/assignments/uri-schemes.html
184
- // http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports
185
- URI.defaultPorts = {
186
- http: "80",
187
- https: "443",
188
- ftp: "21",
189
- gopher: "70",
190
- ws: "80",
191
- wss: "443"
192
- };
193
- // allowed hostname characters according to RFC 3986
194
- // ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded
195
- // I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . -
196
- URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/;
197
- // map DOM Elements to their URI attribute
198
- URI.domAttributes = {
172
+ };
173
+ // state: allow duplicate query parameters (a=1&a=1)
174
+ URI.duplicateQueryParameters = false;
175
+ // state: replaces + with %20 (space in query strings)
176
+ URI.escapeQuerySpace = true;
177
+ // static properties
178
+ URI.protocol_expression = /^[a-z][a-z0-9.+-]*$/i;
179
+ URI.idn_expression = /[^a-z0-9\.-]/i;
180
+ URI.punycode_expression = /(xn--)/i;
181
+ // well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care?
182
+ URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
183
+ // credits to Rich Brown
184
+ // source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
185
+ // specification: http://www.ietf.org/rfc/rfc4291.txt
186
+ URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
187
+ // expression used is "gruber revised" (@gruber v2) determined to be the
188
+ // best solution in a regex-golf we did a couple of ages ago at
189
+ // * http://mathiasbynens.be/demo/url-regex
190
+ // * http://rodneyrehm.de/t/url-regex.html
191
+ URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
192
+ URI.findUri = {
193
+ // valid "scheme://" or "www."
194
+ start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,
195
+ // everything up to the next whitespace
196
+ end: /[\s\r\n]|$/,
197
+ // trim trailing punctuation captured by end RegExp
198
+ trim: /[`!()\[\]{};:'".,<>?«»“”„‘’]+$/
199
+ };
200
+ // http://www.iana.org/assignments/uri-schemes.html
201
+ // http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports
202
+ URI.defaultPorts = {
203
+ http: '80',
204
+ https: '443',
205
+ ftp: '21',
206
+ gopher: '70',
207
+ ws: '80',
208
+ wss: '443'
209
+ };
210
+ // allowed hostname characters according to RFC 3986
211
+ // ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded
212
+ // I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . -
213
+ URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/;
214
+ // map DOM Elements to their URI attribute
215
+ URI.domAttributes = {
199
216
  'a': 'href',
200
217
  'blockquote': 'cite',
201
218
  'link': 'href',
@@ -208,200 +225,215 @@ URI.domAttributes = {
208
225
  'embed': 'src',
209
226
  'source': 'src',
210
227
  'track': 'src',
211
- 'input': 'src' // but only if type="image"
212
- };
213
- URI.getDomAttribute = function(node) {
228
+ 'input': 'src', // but only if type="image"
229
+ 'audio': 'src',
230
+ 'video': 'src'
231
+ };
232
+ URI.getDomAttribute = function(node) {
214
233
  if (!node || !node.nodeName) {
215
- return undefined;
234
+ return undefined;
216
235
  }
217
-
236
+
218
237
  var nodeName = node.nodeName.toLowerCase();
219
238
  // <input> should only expose src for type="image"
220
239
  if (nodeName === 'input' && node.type !== 'image') {
221
- return undefined;
240
+ return undefined;
222
241
  }
223
-
242
+
224
243
  return URI.domAttributes[nodeName];
225
- };
244
+ };
226
245
 
227
- function escapeForDumbFirefox36(value) {
246
+ function escapeForDumbFirefox36(value) {
228
247
  // https://github.com/medialize/URI.js/issues/91
229
248
  return escape(value);
230
- }
249
+ }
231
250
 
232
- // encoding / decoding according to RFC3986
233
- function strictEncodeURIComponent(string) {
251
+ // encoding / decoding according to RFC3986
252
+ function strictEncodeURIComponent(string) {
234
253
  // see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent
235
254
  return encodeURIComponent(string)
236
- .replace(/[!'()*]/g, escapeForDumbFirefox36)
237
- .replace(/\*/g, "%2A");
238
- }
239
- URI.encode = strictEncodeURIComponent;
240
- URI.decode = decodeURIComponent;
241
- URI.iso8859 = function() {
255
+ .replace(/[!'()*]/g, escapeForDumbFirefox36)
256
+ .replace(/\*/g, '%2A');
257
+ }
258
+ URI.encode = strictEncodeURIComponent;
259
+ URI.decode = decodeURIComponent;
260
+ URI.iso8859 = function() {
242
261
  URI.encode = escape;
243
262
  URI.decode = unescape;
244
- };
245
- URI.unicode = function() {
263
+ };
264
+ URI.unicode = function() {
246
265
  URI.encode = strictEncodeURIComponent;
247
266
  URI.decode = decodeURIComponent;
248
- };
249
- URI.characters = {
267
+ };
268
+ URI.characters = {
250
269
  pathname: {
251
- encode: {
252
- // RFC3986 2.1: For consistency, URI producers and normalizers should
253
- // use uppercase hexadecimal digits for all percent-encodings.
254
- expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig,
255
- map: {
256
- // -._~!'()*
257
- "%24": "$",
258
- "%26": "&",
259
- "%2B": "+",
260
- "%2C": ",",
261
- "%3B": ";",
262
- "%3D": "=",
263
- "%3A": ":",
264
- "%40": "@"
265
- }
266
- },
267
- decode: {
268
- expression: /[\/\?#]/g,
269
- map: {
270
- "/": "%2F",
271
- "?": "%3F",
272
- "#": "%23"
273
- }
274
- }
270
+ encode: {
271
+ // RFC3986 2.1: For consistency, URI producers and normalizers should
272
+ // use uppercase hexadecimal digits for all percent-encodings.
273
+ expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig,
274
+ map: {
275
+ // -._~!'()*
276
+ '%24': '$',
277
+ '%26': '&',
278
+ '%2B': '+',
279
+ '%2C': ',',
280
+ '%3B': ';',
281
+ '%3D': '=',
282
+ '%3A': ':',
283
+ '%40': '@'
284
+ }
285
+ },
286
+ decode: {
287
+ expression: /[\/\?#]/g,
288
+ map: {
289
+ '/': '%2F',
290
+ '?': '%3F',
291
+ '#': '%23'
292
+ }
293
+ }
275
294
  },
276
295
  reserved: {
277
- encode: {
278
- // RFC3986 2.1: For consistency, URI producers and normalizers should
279
- // use uppercase hexadecimal digits for all percent-encodings.
280
- expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,
281
- map: {
282
- // gen-delims
283
- "%3A": ":",
284
- "%2F": "/",
285
- "%3F": "?",
286
- "%23": "#",
287
- "%5B": "[",
288
- "%5D": "]",
289
- "%40": "@",
290
- // sub-delims
291
- "%21": "!",
292
- "%24": "$",
293
- "%26": "&",
294
- "%27": "'",
295
- "%28": "(",
296
- "%29": ")",
297
- "%2A": "*",
298
- "%2B": "+",
299
- "%2C": ",",
300
- "%3B": ";",
301
- "%3D": "="
302
- }
303
- }
296
+ encode: {
297
+ // RFC3986 2.1: For consistency, URI producers and normalizers should
298
+ // use uppercase hexadecimal digits for all percent-encodings.
299
+ expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,
300
+ map: {
301
+ // gen-delims
302
+ '%3A': ':',
303
+ '%2F': '/',
304
+ '%3F': '?',
305
+ '%23': '#',
306
+ '%5B': '[',
307
+ '%5D': ']',
308
+ '%40': '@',
309
+ // sub-delims
310
+ '%21': '!',
311
+ '%24': '$',
312
+ '%26': '&',
313
+ '%27': '\'',
314
+ '%28': '(',
315
+ '%29': ')',
316
+ '%2A': '*',
317
+ '%2B': '+',
318
+ '%2C': ',',
319
+ '%3B': ';',
320
+ '%3D': '='
321
+ }
322
+ }
323
+ }
324
+ };
325
+ URI.encodeQuery = function(string, escapeQuerySpace) {
326
+ var escaped = URI.encode(string + '');
327
+ if (escapeQuerySpace === undefined) {
328
+ escapeQuerySpace = URI.escapeQuerySpace;
304
329
  }
305
- };
306
- URI.encodeQuery = function(string, escapeQuerySpace) {
307
- var escaped = URI.encode(string + "");
330
+
308
331
  return escapeQuerySpace ? escaped.replace(/%20/g, '+') : escaped;
309
- };
310
- URI.decodeQuery = function(string, escapeQuerySpace) {
311
- string += "";
332
+ };
333
+ URI.decodeQuery = function(string, escapeQuerySpace) {
334
+ string += '';
335
+ if (escapeQuerySpace === undefined) {
336
+ escapeQuerySpace = URI.escapeQuerySpace;
337
+ }
338
+
312
339
  try {
313
- return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string);
340
+ return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string);
314
341
  } catch(e) {
315
- // we're not going to mess with weird encodings,
316
- // give up and return the undecoded original string
317
- // see https://github.com/medialize/URI.js/issues/87
318
- // see https://github.com/medialize/URI.js/issues/92
319
- return string;
320
- }
321
- };
322
- URI.recodePath = function(string) {
323
- var segments = (string + "").split('/');
342
+ // we're not going to mess with weird encodings,
343
+ // give up and return the undecoded original string
344
+ // see https://github.com/medialize/URI.js/issues/87
345
+ // see https://github.com/medialize/URI.js/issues/92
346
+ return string;
347
+ }
348
+ };
349
+ URI.recodePath = function(string) {
350
+ var segments = (string + '').split('/');
324
351
  for (var i = 0, length = segments.length; i < length; i++) {
325
- segments[i] = URI.encodePathSegment(URI.decode(segments[i]));
352
+ segments[i] = URI.encodePathSegment(URI.decode(segments[i]));
326
353
  }
327
354
 
328
355
  return segments.join('/');
329
- };
330
- URI.decodePath = function(string) {
331
- var segments = (string + "").split('/');
356
+ };
357
+ URI.decodePath = function(string) {
358
+ var segments = (string + '').split('/');
332
359
  for (var i = 0, length = segments.length; i < length; i++) {
333
- segments[i] = URI.decodePathSegment(segments[i]);
360
+ segments[i] = URI.decodePathSegment(segments[i]);
334
361
  }
335
362
 
336
363
  return segments.join('/');
337
- };
338
- // generate encode/decode path functions
339
- var _parts = {'encode':'encode', 'decode':'decode'};
340
- var _part;
341
- var generateAccessor = function(_group, _part) {
364
+ };
365
+ // generate encode/decode path functions
366
+ var _parts = {'encode':'encode', 'decode':'decode'};
367
+ var _part;
368
+ var generateAccessor = function(_group, _part) {
342
369
  return function(string) {
343
- return URI[_part](string + "").replace(URI.characters[_group][_part].expression, function(c) {
344
- return URI.characters[_group][_part].map[c];
370
+ try {
371
+ return URI[_part](string + '').replace(URI.characters[_group][_part].expression, function(c) {
372
+ return URI.characters[_group][_part].map[c];
345
373
  });
374
+ } catch (e) {
375
+ // we're not going to mess with weird encodings,
376
+ // give up and return the undecoded original string
377
+ // see https://github.com/medialize/URI.js/issues/87
378
+ // see https://github.com/medialize/URI.js/issues/92
379
+ return string;
380
+ }
346
381
  };
347
- };
382
+ };
348
383
 
349
- for (_part in _parts) {
350
- URI[_part + "PathSegment"] = generateAccessor("pathname", _parts[_part]);
351
- }
384
+ for (_part in _parts) {
385
+ URI[_part + 'PathSegment'] = generateAccessor('pathname', _parts[_part]);
386
+ }
352
387
 
353
- URI.encodeReserved = generateAccessor("reserved", "encode");
388
+ URI.encodeReserved = generateAccessor('reserved', 'encode');
354
389
 
355
- URI.parse = function(string, parts) {
390
+ URI.parse = function(string, parts) {
356
391
  var pos;
357
392
  if (!parts) {
358
- parts = {};
393
+ parts = {};
359
394
  }
360
395
  // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment]
361
396
 
362
397
  // extract fragment
363
398
  pos = string.indexOf('#');
364
399
  if (pos > -1) {
365
- // escaping?
366
- parts.fragment = string.substring(pos + 1) || null;
367
- string = string.substring(0, pos);
400
+ // escaping?
401
+ parts.fragment = string.substring(pos + 1) || null;
402
+ string = string.substring(0, pos);
368
403
  }
369
404
 
370
405
  // extract query
371
406
  pos = string.indexOf('?');
372
407
  if (pos > -1) {
373
- // escaping?
374
- parts.query = string.substring(pos + 1) || null;
375
- string = string.substring(0, pos);
408
+ // escaping?
409
+ parts.query = string.substring(pos + 1) || null;
410
+ string = string.substring(0, pos);
376
411
  }
377
412
 
378
413
  // extract protocol
379
414
  if (string.substring(0, 2) === '//') {
380
- // relative-scheme
381
- parts.protocol = null;
382
- string = string.substring(2);
383
- // extract "user:pass@host:port"
384
- string = URI.parseAuthority(string, parts);
415
+ // relative-scheme
416
+ parts.protocol = null;
417
+ string = string.substring(2);
418
+ // extract "user:pass@host:port"
419
+ string = URI.parseAuthority(string, parts);
385
420
  } else {
386
- pos = string.indexOf(':');
387
- if (pos > -1) {
388
- parts.protocol = string.substring(0, pos) || null;
389
- if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) {
390
- // : may be within the path
391
- parts.protocol = undefined;
392
- } else if (parts.protocol === 'file') {
393
- // the file scheme: does not contain an authority
394
- string = string.substring(pos + 3);
395
- } else if (string.substring(pos + 1, pos + 3) === '//') {
396
- string = string.substring(pos + 3);
397
-
398
- // extract "user:pass@host:port"
399
- string = URI.parseAuthority(string, parts);
400
- } else {
401
- string = string.substring(pos + 1);
402
- parts.urn = true;
403
- }
421
+ pos = string.indexOf(':');
422
+ if (pos > -1) {
423
+ parts.protocol = string.substring(0, pos) || null;
424
+ if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) {
425
+ // : may be within the path
426
+ parts.protocol = undefined;
427
+ } else if (string.substring(pos + 1, pos + 3) === '//') {
428
+ string = string.substring(pos + 3);
429
+
430
+ // extract "user:pass@host:port"
431
+ string = URI.parseAuthority(string, parts);
432
+ } else {
433
+ string = string.substring(pos + 1);
434
+ parts.urn = true;
404
435
  }
436
+ }
405
437
  }
406
438
 
407
439
  // what's left must be the path
@@ -409,78 +441,79 @@ URI.parse = function(string, parts) {
409
441
 
410
442
  // and we're done
411
443
  return parts;
412
- };
413
- URI.parseHost = function(string, parts) {
444
+ };
445
+ URI.parseHost = function(string, parts) {
414
446
  // extract host:port
415
447
  var pos = string.indexOf('/');
416
448
  var bracketPos;
417
449
  var t;
418
450
 
419
451
  if (pos === -1) {
420
- pos = string.length;
452
+ pos = string.length;
421
453
  }
422
454
 
423
- if (string.charAt(0) === "[") {
424
- // IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6
425
- // I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts
426
- // IPv6+port in the format [2001:db8::1]:80 (for the time being)
427
- bracketPos = string.indexOf(']');
428
- parts.hostname = string.substring(1, bracketPos) || null;
429
- parts.port = string.substring(bracketPos+2, pos) || null;
430
- } else if (string.indexOf(':') !== string.lastIndexOf(':')) {
431
- // IPv6 host contains multiple colons - but no port
432
- // this notation is actually not allowed by RFC 3986, but we're a liberal parser
433
- parts.hostname = string.substring(0, pos) || null;
455
+ if (string.charAt(0) === '[') {
456
+ // IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6
457
+ // I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts
458
+ // IPv6+port in the format [2001:db8::1]:80 (for the time being)
459
+ bracketPos = string.indexOf(']');
460
+ parts.hostname = string.substring(1, bracketPos) || null;
461
+ parts.port = string.substring(bracketPos + 2, pos) || null;
462
+ if (parts.port === '/') {
434
463
  parts.port = null;
464
+ }
465
+ } else if (string.indexOf(':') !== string.lastIndexOf(':')) {
466
+ // IPv6 host contains multiple colons - but no port
467
+ // this notation is actually not allowed by RFC 3986, but we're a liberal parser
468
+ parts.hostname = string.substring(0, pos) || null;
469
+ parts.port = null;
435
470
  } else {
436
- t = string.substring(0, pos).split(':');
437
- parts.hostname = t[0] || null;
438
- parts.port = t[1] || null;
471
+ t = string.substring(0, pos).split(':');
472
+ parts.hostname = t[0] || null;
473
+ parts.port = t[1] || null;
439
474
  }
440
475
 
441
476
  if (parts.hostname && string.substring(pos).charAt(0) !== '/') {
442
- pos++;
443
- string = "/" + string;
477
+ pos++;
478
+ string = '/' + string;
444
479
  }
445
480
 
446
481
  return string.substring(pos) || '/';
447
- };
448
- URI.parseAuthority = function(string, parts) {
482
+ };
483
+ URI.parseAuthority = function(string, parts) {
449
484
  string = URI.parseUserinfo(string, parts);
450
485
  return URI.parseHost(string, parts);
451
- };
452
- URI.parseUserinfo = function(string, parts) {
486
+ };
487
+ URI.parseUserinfo = function(string, parts) {
453
488
  // extract username:password
454
489
  var firstSlash = string.indexOf('/');
455
- var pos = firstSlash > -1
456
- ? string.lastIndexOf('@', firstSlash)
457
- : string.indexOf('@');
490
+ var pos = string.lastIndexOf('@', firstSlash > -1 ? firstSlash : string.length - 1);
458
491
  var t;
459
492
 
460
493
  // authority@ must come before /path
461
494
  if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) {
462
- t = string.substring(0, pos).split(':');
463
- parts.username = t[0] ? URI.decode(t[0]) : null;
464
- t.shift();
465
- parts.password = t[0] ? URI.decode(t.join(':')) : null;
466
- string = string.substring(pos + 1);
495
+ t = string.substring(0, pos).split(':');
496
+ parts.username = t[0] ? URI.decode(t[0]) : null;
497
+ t.shift();
498
+ parts.password = t[0] ? URI.decode(t.join(':')) : null;
499
+ string = string.substring(pos + 1);
467
500
  } else {
468
- parts.username = null;
469
- parts.password = null;
501
+ parts.username = null;
502
+ parts.password = null;
470
503
  }
471
504
 
472
505
  return string;
473
- };
474
- URI.parseQuery = function(string, escapeQuerySpace) {
506
+ };
507
+ URI.parseQuery = function(string, escapeQuerySpace) {
475
508
  if (!string) {
476
- return {};
509
+ return {};
477
510
  }
478
511
 
479
512
  // throw out the funky business - "?"[name"="value"&"]+
480
513
  string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, '');
481
514
 
482
515
  if (!string) {
483
- return {};
516
+ return {};
484
517
  }
485
518
 
486
519
  var items = {};
@@ -489,459 +522,477 @@ URI.parseQuery = function(string, escapeQuerySpace) {
489
522
  var v, name, value;
490
523
 
491
524
  for (var i = 0; i < length; i++) {
492
- v = splits[i].split('=');
493
- name = URI.decodeQuery(v.shift(), escapeQuerySpace);
494
- // no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters
495
- value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null;
496
-
497
- if (items[name]) {
498
- if (typeof items[name] === "string") {
499
- items[name] = [items[name]];
500
- }
525
+ v = splits[i].split('=');
526
+ name = URI.decodeQuery(v.shift(), escapeQuerySpace);
527
+ // no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters
528
+ value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null;
501
529
 
502
- items[name].push(value);
503
- } else {
504
- items[name] = value;
530
+ if (hasOwn.call(items, name)) {
531
+ if (typeof items[name] === 'string') {
532
+ items[name] = [items[name]];
505
533
  }
534
+
535
+ items[name].push(value);
536
+ } else {
537
+ items[name] = value;
538
+ }
506
539
  }
507
540
 
508
541
  return items;
509
- };
542
+ };
510
543
 
511
- URI.build = function(parts) {
512
- var t = "";
544
+ URI.build = function(parts) {
545
+ var t = '';
513
546
 
514
547
  if (parts.protocol) {
515
- t += parts.protocol + ":";
548
+ t += parts.protocol + ':';
516
549
  }
517
550
 
518
551
  if (!parts.urn && (t || parts.hostname)) {
519
- t += '//';
552
+ t += '//';
520
553
  }
521
554
 
522
555
  t += (URI.buildAuthority(parts) || '');
523
556
 
524
- if (typeof parts.path === "string") {
525
- if (parts.path.charAt(0) !== '/' && typeof parts.hostname === "string") {
526
- t += '/';
527
- }
557
+ if (typeof parts.path === 'string') {
558
+ if (parts.path.charAt(0) !== '/' && typeof parts.hostname === 'string') {
559
+ t += '/';
560
+ }
528
561
 
529
- t += parts.path;
562
+ t += parts.path;
530
563
  }
531
564
 
532
- if (typeof parts.query === "string" && parts.query) {
533
- t += '?' + parts.query;
565
+ if (typeof parts.query === 'string' && parts.query) {
566
+ t += '?' + parts.query;
534
567
  }
535
568
 
536
- if (typeof parts.fragment === "string" && parts.fragment) {
537
- t += '#' + parts.fragment;
569
+ if (typeof parts.fragment === 'string' && parts.fragment) {
570
+ t += '#' + parts.fragment;
538
571
  }
539
572
  return t;
540
- };
541
- URI.buildHost = function(parts) {
542
- var t = "";
573
+ };
574
+ URI.buildHost = function(parts) {
575
+ var t = '';
543
576
 
544
577
  if (!parts.hostname) {
545
- return "";
578
+ return '';
546
579
  } else if (URI.ip6_expression.test(parts.hostname)) {
547
- if (parts.port) {
548
- t += "[" + parts.hostname + "]:" + parts.port;
549
- } else {
550
- // don't know if we should always wrap IPv6 in []
551
- // the RFC explicitly says SHOULD, not MUST.
552
- t += parts.hostname;
553
- }
580
+ t += '[' + parts.hostname + ']';
554
581
  } else {
555
- t += parts.hostname;
556
- if (parts.port) {
557
- t += ':' + parts.port;
558
- }
582
+ t += parts.hostname;
583
+ }
584
+
585
+ if (parts.port) {
586
+ t += ':' + parts.port;
559
587
  }
560
588
 
561
589
  return t;
562
- };
563
- URI.buildAuthority = function(parts) {
590
+ };
591
+ URI.buildAuthority = function(parts) {
564
592
  return URI.buildUserinfo(parts) + URI.buildHost(parts);
565
- };
566
- URI.buildUserinfo = function(parts) {
567
- var t = "";
593
+ };
594
+ URI.buildUserinfo = function(parts) {
595
+ var t = '';
568
596
 
569
597
  if (parts.username) {
570
- t += URI.encode(parts.username);
598
+ t += URI.encode(parts.username);
571
599
 
572
- if (parts.password) {
573
- t += ':' + URI.encode(parts.password);
574
- }
600
+ if (parts.password) {
601
+ t += ':' + URI.encode(parts.password);
602
+ }
575
603
 
576
- t += "@";
604
+ t += '@';
577
605
  }
578
606
 
579
607
  return t;
580
- };
581
- URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) {
608
+ };
609
+ URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) {
582
610
  // according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html
583
611
  // being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed
584
612
  // the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax!
585
613
  // URI.js treats the query string as being application/x-www-form-urlencoded
586
614
  // see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
587
615
 
588
- var t = "";
616
+ var t = '';
589
617
  var unique, key, i, length;
590
618
  for (key in data) {
591
- if (hasOwn.call(data, key) && key) {
592
- if (isArray(data[key])) {
593
- unique = {};
594
- for (i = 0, length = data[key].length; i < length; i++) {
595
- if (data[key][i] !== undefined && unique[data[key][i] + ""] === undefined) {
596
- t += "&" + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace);
597
- if (duplicateQueryParameters !== true) {
598
- unique[data[key][i] + ""] = true;
599
- }
600
- }
601
- }
602
- } else if (data[key] !== undefined) {
603
- t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace);
619
+ if (hasOwn.call(data, key) && key) {
620
+ if (isArray(data[key])) {
621
+ unique = {};
622
+ for (i = 0, length = data[key].length; i < length; i++) {
623
+ if (data[key][i] !== undefined && unique[data[key][i] + ''] === undefined) {
624
+ t += '&' + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace);
625
+ if (duplicateQueryParameters !== true) {
626
+ unique[data[key][i] + ''] = true;
627
+ }
604
628
  }
629
+ }
630
+ } else if (data[key] !== undefined) {
631
+ t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace);
605
632
  }
633
+ }
606
634
  }
607
635
 
608
636
  return t.substring(1);
609
- };
610
- URI.buildQueryParameter = function(name, value, escapeQuerySpace) {
637
+ };
638
+ URI.buildQueryParameter = function(name, value, escapeQuerySpace) {
611
639
  // http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded
612
640
  // don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization
613
- return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? "=" + URI.encodeQuery(value, escapeQuerySpace) : "");
614
- };
615
-
616
- URI.addQuery = function(data, name, value) {
617
- if (typeof name === "object") {
618
- for (var key in name) {
619
- if (hasOwn.call(name, key)) {
620
- URI.addQuery(data, key, name[key]);
621
- }
622
- }
623
- } else if (typeof name === "string") {
624
- if (data[name] === undefined) {
625
- data[name] = value;
626
- return;
627
- } else if (typeof data[name] === "string") {
628
- data[name] = [data[name]];
629
- }
630
-
631
- if (!isArray(value)) {
632
- value = [value];
633
- }
634
-
635
- data[name] = data[name].concat(value);
641
+ return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? '=' + URI.encodeQuery(value, escapeQuerySpace) : '');
642
+ };
643
+
644
+ URI.addQuery = function(data, name, value) {
645
+ if (typeof name === 'object') {
646
+ for (var key in name) {
647
+ if (hasOwn.call(name, key)) {
648
+ URI.addQuery(data, key, name[key]);
649
+ }
650
+ }
651
+ } else if (typeof name === 'string') {
652
+ if (data[name] === undefined) {
653
+ data[name] = value;
654
+ return;
655
+ } else if (typeof data[name] === 'string') {
656
+ data[name] = [data[name]];
657
+ }
658
+
659
+ if (!isArray(value)) {
660
+ value = [value];
661
+ }
662
+
663
+ data[name] = (data[name] || []).concat(value);
636
664
  } else {
637
- throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");
665
+ throw new TypeError('URI.addQuery() accepts an object, string as the name parameter');
638
666
  }
639
- };
640
- URI.removeQuery = function(data, name, value) {
667
+ };
668
+ URI.removeQuery = function(data, name, value) {
641
669
  var i, length, key;
642
-
670
+
643
671
  if (isArray(name)) {
644
- for (i = 0, length = name.length; i < length; i++) {
645
- data[name[i]] = undefined;
646
- }
647
- } else if (typeof name === "object") {
648
- for (key in name) {
649
- if (hasOwn.call(name, key)) {
650
- URI.removeQuery(data, key, name[key]);
651
- }
652
- }
653
- } else if (typeof name === "string") {
654
- if (value !== undefined) {
655
- if (data[name] === value) {
656
- data[name] = undefined;
657
- } else if (isArray(data[name])) {
658
- data[name] = filterArrayValues(data[name], value);
659
- }
660
- } else {
661
- data[name] = undefined;
662
- }
672
+ for (i = 0, length = name.length; i < length; i++) {
673
+ data[name[i]] = undefined;
674
+ }
675
+ } else if (typeof name === 'object') {
676
+ for (key in name) {
677
+ if (hasOwn.call(name, key)) {
678
+ URI.removeQuery(data, key, name[key]);
679
+ }
680
+ }
681
+ } else if (typeof name === 'string') {
682
+ if (value !== undefined) {
683
+ if (data[name] === value) {
684
+ data[name] = undefined;
685
+ } else if (isArray(data[name])) {
686
+ data[name] = filterArrayValues(data[name], value);
687
+ }
688
+ } else {
689
+ data[name] = undefined;
690
+ }
663
691
  } else {
664
- throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");
665
- }
666
- };
667
- URI.hasQuery = function(data, name, value, withinArray) {
668
- if (typeof name === "object") {
669
- for (var key in name) {
670
- if (hasOwn.call(name, key)) {
671
- if (!URI.hasQuery(data, key, name[key])) {
672
- return false;
673
- }
674
- }
692
+ throw new TypeError('URI.addQuery() accepts an object, string as the first parameter');
693
+ }
694
+ };
695
+ URI.hasQuery = function(data, name, value, withinArray) {
696
+ if (typeof name === 'object') {
697
+ for (var key in name) {
698
+ if (hasOwn.call(name, key)) {
699
+ if (!URI.hasQuery(data, key, name[key])) {
700
+ return false;
701
+ }
675
702
  }
676
-
677
- return true;
678
- } else if (typeof name !== "string") {
679
- throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");
703
+ }
704
+
705
+ return true;
706
+ } else if (typeof name !== 'string') {
707
+ throw new TypeError('URI.hasQuery() accepts an object, string as the name parameter');
680
708
  }
681
709
 
682
710
  switch (getType(value)) {
683
- case 'Undefined':
684
- // true if exists (but may be empty)
685
- return name in data; // data[name] !== undefined;
686
-
687
- case 'Boolean':
688
- // true if exists and non-empty
689
- var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]);
690
- return value === _booly;
691
-
692
- case 'Function':
693
- // allow complex comparison
694
- return !!value(data[name], name, data);
695
-
696
- case 'Array':
697
- if (!isArray(data[name])) {
698
- return false;
699
- }
711
+ case 'Undefined':
712
+ // true if exists (but may be empty)
713
+ return name in data; // data[name] !== undefined;
700
714
 
701
- var op = withinArray ? arrayContains : arraysEqual;
702
- return op(data[name], value);
715
+ case 'Boolean':
716
+ // true if exists and non-empty
717
+ var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]);
718
+ return value === _booly;
703
719
 
704
- case 'RegExp':
705
- if (!isArray(data[name])) {
706
- return Boolean(data[name] && data[name].match(value));
707
- }
720
+ case 'Function':
721
+ // allow complex comparison
722
+ return !!value(data[name], name, data);
708
723
 
709
- if (!withinArray) {
710
- return false;
711
- }
724
+ case 'Array':
725
+ if (!isArray(data[name])) {
726
+ return false;
727
+ }
712
728
 
713
- return arrayContains(data[name], value);
729
+ var op = withinArray ? arrayContains : arraysEqual;
730
+ return op(data[name], value);
714
731
 
715
- case 'Number':
716
- value = String(value);
717
- // omit break;
718
- case 'String':
719
- if (!isArray(data[name])) {
720
- return data[name] === value;
721
- }
732
+ case 'RegExp':
733
+ if (!isArray(data[name])) {
734
+ return Boolean(data[name] && data[name].match(value));
735
+ }
722
736
 
723
- if (!withinArray) {
724
- return false;
725
- }
737
+ if (!withinArray) {
738
+ return false;
739
+ }
740
+
741
+ return arrayContains(data[name], value);
742
+
743
+ case 'Number':
744
+ value = String(value);
745
+ /* falls through */
746
+ case 'String':
747
+ if (!isArray(data[name])) {
748
+ return data[name] === value;
749
+ }
750
+
751
+ if (!withinArray) {
752
+ return false;
753
+ }
726
754
 
727
- return arrayContains(data[name], value);
755
+ return arrayContains(data[name], value);
728
756
 
729
- default:
730
- throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");
757
+ default:
758
+ throw new TypeError('URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter');
731
759
  }
732
- };
760
+ };
733
761
 
734
762
 
735
- URI.commonPath = function(one, two) {
763
+ URI.commonPath = function(one, two) {
736
764
  var length = Math.min(one.length, two.length);
737
765
  var pos;
738
766
 
739
767
  // find first non-matching character
740
768
  for (pos = 0; pos < length; pos++) {
741
- if (one.charAt(pos) !== two.charAt(pos)) {
742
- pos--;
743
- break;
744
- }
769
+ if (one.charAt(pos) !== two.charAt(pos)) {
770
+ pos--;
771
+ break;
772
+ }
745
773
  }
746
774
 
747
775
  if (pos < 1) {
748
- return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : '';
776
+ return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : '';
749
777
  }
750
-
778
+
751
779
  // revert to last /
752
780
  if (one.charAt(pos) !== '/' || two.charAt(pos) !== '/') {
753
- pos = one.substring(0, pos).lastIndexOf('/');
781
+ pos = one.substring(0, pos).lastIndexOf('/');
754
782
  }
755
783
 
756
784
  return one.substring(0, pos + 1);
757
- };
785
+ };
786
+
787
+ URI.withinString = function(string, callback, options) {
788
+ options || (options = {});
789
+ var _start = options.start || URI.findUri.start;
790
+ var _end = options.end || URI.findUri.end;
791
+ var _trim = options.trim || URI.findUri.trim;
792
+ var _attributeOpen = /[a-z0-9-]=["']?$/i;
793
+
794
+ _start.lastIndex = 0;
795
+ while (true) {
796
+ var match = _start.exec(string);
797
+ if (!match) {
798
+ break;
799
+ }
800
+
801
+ var start = match.index;
802
+ if (options.ignoreHtml) {
803
+ // attribut(e=["']?$)
804
+ var attributeOpen = string.slice(Math.max(start - 3, 0), start);
805
+ if (attributeOpen && _attributeOpen.test(attributeOpen)) {
806
+ continue;
807
+ }
808
+ }
758
809
 
759
- URI.withinString = function(string, callback) {
760
- // expression used is "gruber revised" (@gruber v2) determined to be the best solution in
761
- // a regex sprint we did a couple of ages ago at
762
- // * http://mathiasbynens.be/demo/url-regex
763
- // * http://rodneyrehm.de/t/url-regex.html
810
+ var end = start + string.slice(start).search(_end);
811
+ var slice = string.slice(start, end).replace(_trim, '');
812
+ if (options.ignore && options.ignore.test(slice)) {
813
+ continue;
814
+ }
764
815
 
765
- return string.replace(URI.find_uri_expression, callback);
766
- };
816
+ end = start + slice.length;
817
+ var result = callback(slice, start, end, string);
818
+ string = string.slice(0, start) + result + string.slice(end);
819
+ _start.lastIndex = start + result.length;
820
+ }
767
821
 
768
- URI.ensureValidHostname = function(v) {
822
+ _start.lastIndex = 0;
823
+ return string;
824
+ };
825
+
826
+ URI.ensureValidHostname = function(v) {
769
827
  // Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986)
770
828
  // they are not part of DNS and therefore ignored by URI.js
771
829
 
772
830
  if (v.match(URI.invalid_hostname_characters)) {
773
- // test punycode
774
- if (!punycode) {
775
- throw new TypeError("Hostname '" + v + "' contains characters other than [A-Z0-9.-] and Punycode.js is not available");
776
- }
831
+ // test punycode
832
+ if (!punycode) {
833
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-] and Punycode.js is not available');
834
+ }
777
835
 
778
- if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) {
779
- throw new TypeError("Hostname '" + v + "' contains characters other than [A-Z0-9.-]");
780
- }
836
+ if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) {
837
+ throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
838
+ }
781
839
  }
782
- };
840
+ };
783
841
 
784
- // noConflict
785
- URI.noConflict = function(removeAll) {
842
+ // noConflict
843
+ URI.noConflict = function(removeAll) {
786
844
  if (removeAll) {
787
- var unconflicted = {
788
- URI: this.noConflict()
789
- };
845
+ var unconflicted = {
846
+ URI: this.noConflict()
847
+ };
790
848
 
791
- if (URITemplate && typeof URITemplate.noConflict == "function") {
792
- unconflicted.URITemplate = URITemplate.noConflict();
793
- }
849
+ if (root.URITemplate && typeof root.URITemplate.noConflict === 'function') {
850
+ unconflicted.URITemplate = root.URITemplate.noConflict();
851
+ }
794
852
 
795
- if (IPv6 && typeof IPv6.noConflict == "function") {
796
- unconflicted.IPv6 = IPv6.noConflict();
797
- }
853
+ if (root.IPv6 && typeof root.IPv6.noConflict === 'function') {
854
+ unconflicted.IPv6 = root.IPv6.noConflict();
855
+ }
798
856
 
799
- if (SecondLevelDomains && typeof SecondLevelDomains.noConflict == "function") {
800
- unconflicted.SecondLevelDomains = SecondLevelDomains.noConflict();
801
- }
857
+ if (root.SecondLevelDomains && typeof root.SecondLevelDomains.noConflict === 'function') {
858
+ unconflicted.SecondLevelDomains = root.SecondLevelDomains.noConflict();
859
+ }
802
860
 
803
- return unconflicted;
861
+ return unconflicted;
804
862
  } else if (root.URI === this) {
805
- root.URI = _URI;
863
+ root.URI = _URI;
806
864
  }
807
865
 
808
866
  return this;
809
- };
867
+ };
810
868
 
811
- p.build = function(deferBuild) {
869
+ p.build = function(deferBuild) {
812
870
  if (deferBuild === true) {
813
- this._deferred_build = true;
871
+ this._deferred_build = true;
814
872
  } else if (deferBuild === undefined || this._deferred_build) {
815
- this._string = URI.build(this._parts);
816
- this._deferred_build = false;
873
+ this._string = URI.build(this._parts);
874
+ this._deferred_build = false;
817
875
  }
818
876
 
819
877
  return this;
820
- };
878
+ };
821
879
 
822
- p.clone = function() {
880
+ p.clone = function() {
823
881
  return new URI(this);
824
- };
882
+ };
825
883
 
826
- p.valueOf = p.toString = function() {
884
+ p.valueOf = p.toString = function() {
827
885
  return this.build(false)._string;
828
- };
886
+ };
887
+
829
888
 
830
- // generate simple accessors
831
- _parts = {protocol: 'protocol', username: 'username', password: 'password', hostname: 'hostname', port: 'port'};
832
- generateAccessor = function(_part){
889
+ function generateSimpleAccessor(_part){
833
890
  return function(v, build) {
834
- if (v === undefined) {
835
- return this._parts[_part] || "";
836
- } else {
837
- this._parts[_part] = v || null;
838
- this.build(!build);
839
- return this;
840
- }
891
+ if (v === undefined) {
892
+ return this._parts[_part] || '';
893
+ } else {
894
+ this._parts[_part] = v || null;
895
+ this.build(!build);
896
+ return this;
897
+ }
841
898
  };
842
- };
899
+ }
843
900
 
844
- for (_part in _parts) {
845
- p[_part] = generateAccessor(_parts[_part]);
846
- }
847
-
848
- // generate accessors with optionally prefixed input
849
- _parts = {query: '?', fragment: '#'};
850
- generateAccessor = function(_part, _key){
901
+ function generatePrefixAccessor(_part, _key){
851
902
  return function(v, build) {
852
- if (v === undefined) {
853
- return this._parts[_part] || "";
854
- } else {
855
- if (v !== null) {
856
- v = v + "";
857
- if (v.charAt(0) === _key) {
858
- v = v.substring(1);
859
- }
860
- }
861
-
862
- this._parts[_part] = v;
863
- this.build(!build);
864
- return this;
903
+ if (v === undefined) {
904
+ return this._parts[_part] || '';
905
+ } else {
906
+ if (v !== null) {
907
+ v = v + '';
908
+ if (v.charAt(0) === _key) {
909
+ v = v.substring(1);
910
+ }
865
911
  }
866
- };
867
- };
868
912
 
869
- for (_part in _parts) {
870
- p[_part] = generateAccessor(_part, _parts[_part]);
871
- }
872
-
873
- // generate accessors with prefixed output
874
- _parts = {search: ['?', 'query'], hash: ['#', 'fragment']};
875
- generateAccessor = function(_part, _key){
876
- return function(v, build) {
877
- var t = this[_part](v, build);
878
- return typeof t === "string" && t.length ? (_key + t) : t;
913
+ this._parts[_part] = v;
914
+ this.build(!build);
915
+ return this;
916
+ }
879
917
  };
880
- };
881
-
882
- for (_part in _parts) {
883
- p[_part] = generateAccessor(_parts[_part][1], _parts[_part][0]);
884
- }
885
-
886
- p.pathname = function(v, build) {
918
+ }
919
+
920
+ p.protocol = generateSimpleAccessor('protocol');
921
+ p.username = generateSimpleAccessor('username');
922
+ p.password = generateSimpleAccessor('password');
923
+ p.hostname = generateSimpleAccessor('hostname');
924
+ p.port = generateSimpleAccessor('port');
925
+ p.query = generatePrefixAccessor('query', '?');
926
+ p.fragment = generatePrefixAccessor('fragment', '#');
927
+
928
+ p.search = function(v, build) {
929
+ var t = this.query(v, build);
930
+ return typeof t === 'string' && t.length ? ('?' + t) : t;
931
+ };
932
+ p.hash = function(v, build) {
933
+ var t = this.fragment(v, build);
934
+ return typeof t === 'string' && t.length ? ('#' + t) : t;
935
+ };
936
+
937
+ p.pathname = function(v, build) {
887
938
  if (v === undefined || v === true) {
888
- var res = this._parts.path || (this._parts.hostname ? '/' : '');
889
- return v ? URI.decodePath(res) : res;
939
+ var res = this._parts.path || (this._parts.hostname ? '/' : '');
940
+ return v ? URI.decodePath(res) : res;
890
941
  } else {
891
- this._parts.path = v ? URI.recodePath(v) : "/";
892
- this.build(!build);
893
- return this;
942
+ this._parts.path = v ? URI.recodePath(v) : '/';
943
+ this.build(!build);
944
+ return this;
894
945
  }
895
- };
896
- p.path = p.pathname;
897
- p.href = function(href, build) {
946
+ };
947
+ p.path = p.pathname;
948
+ p.href = function(href, build) {
898
949
  var key;
899
-
950
+
900
951
  if (href === undefined) {
901
- return this.toString();
952
+ return this.toString();
902
953
  }
903
954
 
904
- this._string = "";
955
+ this._string = '';
905
956
  this._parts = URI._parts();
906
957
 
907
958
  var _URI = href instanceof URI;
908
- var _object = typeof href === "object" && (href.hostname || href.path || href.pathname);
959
+ var _object = typeof href === 'object' && (href.hostname || href.path || href.pathname);
909
960
  if (href.nodeName) {
910
- var attribute = URI.getDomAttribute(href);
911
- href = href[attribute] || "";
912
- _object = false;
961
+ var attribute = URI.getDomAttribute(href);
962
+ href = href[attribute] || '';
963
+ _object = false;
913
964
  }
914
-
965
+
915
966
  // window.location is reported to be an object, but it's not the sort
916
- // of object we're looking for:
967
+ // of object we're looking for:
917
968
  // * location.protocol ends with a colon
918
969
  // * location.query != object.search
919
970
  // * location.hash != object.fragment
920
- // simply serializing the unknown object should do the trick
971
+ // simply serializing the unknown object should do the trick
921
972
  // (for location, not for everything...)
922
973
  if (!_URI && _object && href.pathname !== undefined) {
923
- href = href.toString();
974
+ href = href.toString();
924
975
  }
925
976
 
926
- if (typeof href === "string") {
927
- this._parts = URI.parse(href, this._parts);
977
+ if (typeof href === 'string' || href instanceof String) {
978
+ this._parts = URI.parse(String(href), this._parts);
928
979
  } else if (_URI || _object) {
929
- var src = _URI ? href._parts : href;
930
- for (key in src) {
931
- if (hasOwn.call(this._parts, key)) {
932
- this._parts[key] = src[key];
933
- }
980
+ var src = _URI ? href._parts : href;
981
+ for (key in src) {
982
+ if (hasOwn.call(this._parts, key)) {
983
+ this._parts[key] = src[key];
934
984
  }
985
+ }
935
986
  } else {
936
- throw new TypeError("invalid input");
987
+ throw new TypeError('invalid input');
937
988
  }
938
989
 
939
990
  this.build(!build);
940
991
  return this;
941
- };
992
+ };
942
993
 
943
- // identification accessors
944
- p.is = function(what) {
994
+ // identification accessors
995
+ p.is = function(what) {
945
996
  var ip = false;
946
997
  var ip4 = false;
947
998
  var ip6 = false;
@@ -952,740 +1003,751 @@ p.is = function(what) {
952
1003
  var relative = !this._parts.urn;
953
1004
 
954
1005
  if (this._parts.hostname) {
955
- relative = false;
956
- ip4 = URI.ip4_expression.test(this._parts.hostname);
957
- ip6 = URI.ip6_expression.test(this._parts.hostname);
958
- ip = ip4 || ip6;
959
- name = !ip;
960
- sld = name && SLD && SLD.has(this._parts.hostname);
961
- idn = name && URI.idn_expression.test(this._parts.hostname);
962
- punycode = name && URI.punycode_expression.test(this._parts.hostname);
1006
+ relative = false;
1007
+ ip4 = URI.ip4_expression.test(this._parts.hostname);
1008
+ ip6 = URI.ip6_expression.test(this._parts.hostname);
1009
+ ip = ip4 || ip6;
1010
+ name = !ip;
1011
+ sld = name && SLD && SLD.has(this._parts.hostname);
1012
+ idn = name && URI.idn_expression.test(this._parts.hostname);
1013
+ punycode = name && URI.punycode_expression.test(this._parts.hostname);
963
1014
  }
964
1015
 
965
1016
  switch (what.toLowerCase()) {
966
- case 'relative':
967
- return relative;
1017
+ case 'relative':
1018
+ return relative;
968
1019
 
969
- case 'absolute':
970
- return !relative;
1020
+ case 'absolute':
1021
+ return !relative;
971
1022
 
972
- // hostname identification
973
- case 'domain':
974
- case 'name':
975
- return name;
1023
+ // hostname identification
1024
+ case 'domain':
1025
+ case 'name':
1026
+ return name;
976
1027
 
977
- case 'sld':
978
- return sld;
1028
+ case 'sld':
1029
+ return sld;
979
1030
 
980
- case 'ip':
981
- return ip;
1031
+ case 'ip':
1032
+ return ip;
982
1033
 
983
- case 'ip4':
984
- case 'ipv4':
985
- case 'inet4':
986
- return ip4;
1034
+ case 'ip4':
1035
+ case 'ipv4':
1036
+ case 'inet4':
1037
+ return ip4;
987
1038
 
988
- case 'ip6':
989
- case 'ipv6':
990
- case 'inet6':
991
- return ip6;
1039
+ case 'ip6':
1040
+ case 'ipv6':
1041
+ case 'inet6':
1042
+ return ip6;
992
1043
 
993
- case 'idn':
994
- return idn;
1044
+ case 'idn':
1045
+ return idn;
995
1046
 
996
- case 'url':
997
- return !this._parts.urn;
1047
+ case 'url':
1048
+ return !this._parts.urn;
998
1049
 
999
- case 'urn':
1000
- return !!this._parts.urn;
1050
+ case 'urn':
1051
+ return !!this._parts.urn;
1001
1052
 
1002
- case 'punycode':
1003
- return punycode;
1053
+ case 'punycode':
1054
+ return punycode;
1004
1055
  }
1005
1056
 
1006
1057
  return null;
1007
- };
1058
+ };
1008
1059
 
1009
- // component specific input validation
1010
- var _protocol = p.protocol;
1011
- var _port = p.port;
1012
- var _hostname = p.hostname;
1060
+ // component specific input validation
1061
+ var _protocol = p.protocol;
1062
+ var _port = p.port;
1063
+ var _hostname = p.hostname;
1013
1064
 
1014
- p.protocol = function(v, build) {
1065
+ p.protocol = function(v, build) {
1015
1066
  if (v !== undefined) {
1016
- if (v) {
1017
- // accept trailing ://
1018
- v = v.replace(/:(\/\/)?$/, '');
1067
+ if (v) {
1068
+ // accept trailing ://
1069
+ v = v.replace(/:(\/\/)?$/, '');
1019
1070
 
1020
- if (v.match(/[^a-zA-z0-9\.+-]/)) {
1021
- throw new TypeError("Protocol '" + v + "' contains characters other than [A-Z0-9.+-]");
1022
- }
1071
+ if (!v.match(URI.protocol_expression)) {
1072
+ throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]');
1023
1073
  }
1074
+ }
1024
1075
  }
1025
1076
  return _protocol.call(this, v, build);
1026
- };
1027
- p.scheme = p.protocol;
1028
- p.port = function(v, build) {
1077
+ };
1078
+ p.scheme = p.protocol;
1079
+ p.port = function(v, build) {
1029
1080
  if (this._parts.urn) {
1030
- return v === undefined ? '' : this;
1081
+ return v === undefined ? '' : this;
1031
1082
  }
1032
1083
 
1033
1084
  if (v !== undefined) {
1034
- if (v === 0) {
1035
- v = null;
1036
- }
1085
+ if (v === 0) {
1086
+ v = null;
1087
+ }
1037
1088
 
1038
- if (v) {
1039
- v += "";
1040
- if (v.charAt(0) === ":") {
1041
- v = v.substring(1);
1042
- }
1089
+ if (v) {
1090
+ v += '';
1091
+ if (v.charAt(0) === ':') {
1092
+ v = v.substring(1);
1093
+ }
1043
1094
 
1044
- if (v.match(/[^0-9]/)) {
1045
- throw new TypeError("Port '" + v + "' contains characters other than [0-9]");
1046
- }
1095
+ if (v.match(/[^0-9]/)) {
1096
+ throw new TypeError('Port "' + v + '" contains characters other than [0-9]');
1047
1097
  }
1098
+ }
1048
1099
  }
1049
1100
  return _port.call(this, v, build);
1050
- };
1051
- p.hostname = function(v, build) {
1101
+ };
1102
+ p.hostname = function(v, build) {
1052
1103
  if (this._parts.urn) {
1053
- return v === undefined ? '' : this;
1104
+ return v === undefined ? '' : this;
1054
1105
  }
1055
1106
 
1056
1107
  if (v !== undefined) {
1057
- var x = {};
1058
- URI.parseHost(v, x);
1059
- v = x.hostname;
1108
+ var x = {};
1109
+ URI.parseHost(v, x);
1110
+ v = x.hostname;
1060
1111
  }
1061
1112
  return _hostname.call(this, v, build);
1062
- };
1113
+ };
1063
1114
 
1064
- // compound accessors
1065
- p.host = function(v, build) {
1115
+ // compound accessors
1116
+ p.host = function(v, build) {
1066
1117
  if (this._parts.urn) {
1067
- return v === undefined ? '' : this;
1118
+ return v === undefined ? '' : this;
1068
1119
  }
1069
1120
 
1070
1121
  if (v === undefined) {
1071
- return this._parts.hostname ? URI.buildHost(this._parts) : "";
1122
+ return this._parts.hostname ? URI.buildHost(this._parts) : '';
1072
1123
  } else {
1073
- URI.parseHost(v, this._parts);
1074
- this.build(!build);
1075
- return this;
1124
+ URI.parseHost(v, this._parts);
1125
+ this.build(!build);
1126
+ return this;
1076
1127
  }
1077
- };
1078
- p.authority = function(v, build) {
1128
+ };
1129
+ p.authority = function(v, build) {
1079
1130
  if (this._parts.urn) {
1080
- return v === undefined ? '' : this;
1131
+ return v === undefined ? '' : this;
1081
1132
  }
1082
1133
 
1083
1134
  if (v === undefined) {
1084
- return this._parts.hostname ? URI.buildAuthority(this._parts) : "";
1135
+ return this._parts.hostname ? URI.buildAuthority(this._parts) : '';
1085
1136
  } else {
1086
- URI.parseAuthority(v, this._parts);
1087
- this.build(!build);
1088
- return this;
1137
+ URI.parseAuthority(v, this._parts);
1138
+ this.build(!build);
1139
+ return this;
1089
1140
  }
1090
- };
1091
- p.userinfo = function(v, build) {
1141
+ };
1142
+ p.userinfo = function(v, build) {
1092
1143
  if (this._parts.urn) {
1093
- return v === undefined ? '' : this;
1144
+ return v === undefined ? '' : this;
1094
1145
  }
1095
1146
 
1096
1147
  if (v === undefined) {
1097
- if (!this._parts.username) {
1098
- return "";
1099
- }
1148
+ if (!this._parts.username) {
1149
+ return '';
1150
+ }
1100
1151
 
1101
- var t = URI.buildUserinfo(this._parts);
1102
- return t.substring(0, t.length -1);
1152
+ var t = URI.buildUserinfo(this._parts);
1153
+ return t.substring(0, t.length -1);
1103
1154
  } else {
1104
- if (v[v.length-1] !== '@') {
1105
- v += '@';
1106
- }
1155
+ if (v[v.length-1] !== '@') {
1156
+ v += '@';
1157
+ }
1107
1158
 
1108
- URI.parseUserinfo(v, this._parts);
1109
- this.build(!build);
1110
- return this;
1159
+ URI.parseUserinfo(v, this._parts);
1160
+ this.build(!build);
1161
+ return this;
1111
1162
  }
1112
- };
1113
- p.resource = function(v, build) {
1163
+ };
1164
+ p.resource = function(v, build) {
1114
1165
  var parts;
1115
-
1166
+
1116
1167
  if (v === undefined) {
1117
- return this.path() + this.search() + this.hash();
1168
+ return this.path() + this.search() + this.hash();
1118
1169
  }
1119
-
1170
+
1120
1171
  parts = URI.parse(v);
1121
1172
  this._parts.path = parts.path;
1122
1173
  this._parts.query = parts.query;
1123
1174
  this._parts.fragment = parts.fragment;
1124
1175
  this.build(!build);
1125
1176
  return this;
1126
- };
1177
+ };
1127
1178
 
1128
- // fraction accessors
1129
- p.subdomain = function(v, build) {
1179
+ // fraction accessors
1180
+ p.subdomain = function(v, build) {
1130
1181
  if (this._parts.urn) {
1131
- return v === undefined ? '' : this;
1182
+ return v === undefined ? '' : this;
1132
1183
  }
1133
1184
 
1134
1185
  // convenience, return "www" from "www.example.org"
1135
1186
  if (v === undefined) {
1136
- if (!this._parts.hostname || this.is('IP')) {
1137
- return "";
1138
- }
1187
+ if (!this._parts.hostname || this.is('IP')) {
1188
+ return '';
1189
+ }
1139
1190
 
1140
- // grab domain and add another segment
1141
- var end = this._parts.hostname.length - this.domain().length - 1;
1142
- return this._parts.hostname.substring(0, end) || "";
1191
+ // grab domain and add another segment
1192
+ var end = this._parts.hostname.length - this.domain().length - 1;
1193
+ return this._parts.hostname.substring(0, end) || '';
1143
1194
  } else {
1144
- var e = this._parts.hostname.length - this.domain().length;
1145
- var sub = this._parts.hostname.substring(0, e);
1146
- var replace = new RegExp('^' + escapeRegEx(sub));
1195
+ var e = this._parts.hostname.length - this.domain().length;
1196
+ var sub = this._parts.hostname.substring(0, e);
1197
+ var replace = new RegExp('^' + escapeRegEx(sub));
1147
1198
 
1148
- if (v && v.charAt(v.length - 1) !== '.') {
1149
- v += ".";
1150
- }
1199
+ if (v && v.charAt(v.length - 1) !== '.') {
1200
+ v += '.';
1201
+ }
1151
1202
 
1152
- if (v) {
1153
- URI.ensureValidHostname(v);
1154
- }
1203
+ if (v) {
1204
+ URI.ensureValidHostname(v);
1205
+ }
1155
1206
 
1156
- this._parts.hostname = this._parts.hostname.replace(replace, v);
1157
- this.build(!build);
1158
- return this;
1207
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
1208
+ this.build(!build);
1209
+ return this;
1159
1210
  }
1160
- };
1161
- p.domain = function(v, build) {
1211
+ };
1212
+ p.domain = function(v, build) {
1162
1213
  if (this._parts.urn) {
1163
- return v === undefined ? '' : this;
1214
+ return v === undefined ? '' : this;
1164
1215
  }
1165
1216
 
1166
1217
  if (typeof v === 'boolean') {
1167
- build = v;
1168
- v = undefined;
1218
+ build = v;
1219
+ v = undefined;
1169
1220
  }
1170
1221
 
1171
1222
  // convenience, return "example.org" from "www.example.org"
1172
1223
  if (v === undefined) {
1173
- if (!this._parts.hostname || this.is('IP')) {
1174
- return "";
1175
- }
1176
-
1177
- // if hostname consists of 1 or 2 segments, it must be the domain
1178
- var t = this._parts.hostname.match(/\./g);
1179
- if (t && t.length < 2) {
1180
- return this._parts.hostname;
1181
- }
1182
-
1183
- // grab tld and add another segment
1184
- var end = this._parts.hostname.length - this.tld(build).length - 1;
1185
- end = this._parts.hostname.lastIndexOf('.', end -1) + 1;
1186
- return this._parts.hostname.substring(end) || "";
1224
+ if (!this._parts.hostname || this.is('IP')) {
1225
+ return '';
1226
+ }
1227
+
1228
+ // if hostname consists of 1 or 2 segments, it must be the domain
1229
+ var t = this._parts.hostname.match(/\./g);
1230
+ if (t && t.length < 2) {
1231
+ return this._parts.hostname;
1232
+ }
1233
+
1234
+ // grab tld and add another segment
1235
+ var end = this._parts.hostname.length - this.tld(build).length - 1;
1236
+ end = this._parts.hostname.lastIndexOf('.', end -1) + 1;
1237
+ return this._parts.hostname.substring(end) || '';
1187
1238
  } else {
1188
- if (!v) {
1189
- throw new TypeError("cannot set domain empty");
1190
- }
1239
+ if (!v) {
1240
+ throw new TypeError('cannot set domain empty');
1241
+ }
1191
1242
 
1192
- URI.ensureValidHostname(v);
1243
+ URI.ensureValidHostname(v);
1193
1244
 
1194
- if (!this._parts.hostname || this.is('IP')) {
1195
- this._parts.hostname = v;
1196
- } else {
1197
- var replace = new RegExp(escapeRegEx(this.domain()) + "$");
1198
- this._parts.hostname = this._parts.hostname.replace(replace, v);
1199
- }
1245
+ if (!this._parts.hostname || this.is('IP')) {
1246
+ this._parts.hostname = v;
1247
+ } else {
1248
+ var replace = new RegExp(escapeRegEx(this.domain()) + '$');
1249
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
1250
+ }
1200
1251
 
1201
- this.build(!build);
1202
- return this;
1252
+ this.build(!build);
1253
+ return this;
1203
1254
  }
1204
- };
1205
- p.tld = function(v, build) {
1255
+ };
1256
+ p.tld = function(v, build) {
1206
1257
  if (this._parts.urn) {
1207
- return v === undefined ? '' : this;
1258
+ return v === undefined ? '' : this;
1208
1259
  }
1209
1260
 
1210
1261
  if (typeof v === 'boolean') {
1211
- build = v;
1212
- v = undefined;
1262
+ build = v;
1263
+ v = undefined;
1213
1264
  }
1214
1265
 
1215
1266
  // return "org" from "www.example.org"
1216
1267
  if (v === undefined) {
1217
- if (!this._parts.hostname || this.is('IP')) {
1218
- return "";
1219
- }
1268
+ if (!this._parts.hostname || this.is('IP')) {
1269
+ return '';
1270
+ }
1220
1271
 
1221
- var pos = this._parts.hostname.lastIndexOf('.');
1222
- var tld = this._parts.hostname.substring(pos + 1);
1272
+ var pos = this._parts.hostname.lastIndexOf('.');
1273
+ var tld = this._parts.hostname.substring(pos + 1);
1223
1274
 
1224
- if (build !== true && SLD && SLD.list[tld.toLowerCase()]) {
1225
- return SLD.get(this._parts.hostname) || tld;
1226
- }
1275
+ if (build !== true && SLD && SLD.list[tld.toLowerCase()]) {
1276
+ return SLD.get(this._parts.hostname) || tld;
1277
+ }
1227
1278
 
1228
- return tld;
1279
+ return tld;
1229
1280
  } else {
1230
- var replace;
1231
-
1232
- if (!v) {
1233
- throw new TypeError("cannot set TLD empty");
1234
- } else if (v.match(/[^a-zA-Z0-9-]/)) {
1235
- if (SLD && SLD.is(v)) {
1236
- replace = new RegExp(escapeRegEx(this.tld()) + "$");
1237
- this._parts.hostname = this._parts.hostname.replace(replace, v);
1238
- } else {
1239
- throw new TypeError("TLD '" + v + "' contains characters other than [A-Z0-9]");
1240
- }
1241
- } else if (!this._parts.hostname || this.is('IP')) {
1242
- throw new ReferenceError("cannot set TLD on non-domain host");
1281
+ var replace;
1282
+
1283
+ if (!v) {
1284
+ throw new TypeError('cannot set TLD empty');
1285
+ } else if (v.match(/[^a-zA-Z0-9-]/)) {
1286
+ if (SLD && SLD.is(v)) {
1287
+ replace = new RegExp(escapeRegEx(this.tld()) + '$');
1288
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
1243
1289
  } else {
1244
- replace = new RegExp(escapeRegEx(this.tld()) + "$");
1245
- this._parts.hostname = this._parts.hostname.replace(replace, v);
1290
+ throw new TypeError('TLD "' + v + '" contains characters other than [A-Z0-9]');
1246
1291
  }
1292
+ } else if (!this._parts.hostname || this.is('IP')) {
1293
+ throw new ReferenceError('cannot set TLD on non-domain host');
1294
+ } else {
1295
+ replace = new RegExp(escapeRegEx(this.tld()) + '$');
1296
+ this._parts.hostname = this._parts.hostname.replace(replace, v);
1297
+ }
1247
1298
 
1248
- this.build(!build);
1249
- return this;
1299
+ this.build(!build);
1300
+ return this;
1250
1301
  }
1251
- };
1252
- p.directory = function(v, build) {
1302
+ };
1303
+ p.directory = function(v, build) {
1253
1304
  if (this._parts.urn) {
1254
- return v === undefined ? '' : this;
1305
+ return v === undefined ? '' : this;
1255
1306
  }
1256
1307
 
1257
1308
  if (v === undefined || v === true) {
1258
- if (!this._parts.path && !this._parts.hostname) {
1259
- return '';
1260
- }
1309
+ if (!this._parts.path && !this._parts.hostname) {
1310
+ return '';
1311
+ }
1261
1312
 
1262
- if (this._parts.path === '/') {
1263
- return '/';
1264
- }
1313
+ if (this._parts.path === '/') {
1314
+ return '/';
1315
+ }
1265
1316
 
1266
- var end = this._parts.path.length - this.filename().length - 1;
1267
- var res = this._parts.path.substring(0, end) || (this._parts.hostname ? "/" : "");
1317
+ var end = this._parts.path.length - this.filename().length - 1;
1318
+ var res = this._parts.path.substring(0, end) || (this._parts.hostname ? '/' : '');
1268
1319
 
1269
- return v ? URI.decodePath(res) : res;
1320
+ return v ? URI.decodePath(res) : res;
1270
1321
 
1271
1322
  } else {
1272
- var e = this._parts.path.length - this.filename().length;
1273
- var directory = this._parts.path.substring(0, e);
1274
- var replace = new RegExp('^' + escapeRegEx(directory));
1275
-
1276
- // fully qualifier directories begin with a slash
1277
- if (!this.is('relative')) {
1278
- if (!v) {
1279
- v = '/';
1280
- }
1323
+ var e = this._parts.path.length - this.filename().length;
1324
+ var directory = this._parts.path.substring(0, e);
1325
+ var replace = new RegExp('^' + escapeRegEx(directory));
1281
1326
 
1282
- if (v.charAt(0) !== '/') {
1283
- v = "/" + v;
1284
- }
1327
+ // fully qualifier directories begin with a slash
1328
+ if (!this.is('relative')) {
1329
+ if (!v) {
1330
+ v = '/';
1285
1331
  }
1286
1332
 
1287
- // directories always end with a slash
1288
- if (v && v.charAt(v.length - 1) !== '/') {
1289
- v += '/';
1333
+ if (v.charAt(0) !== '/') {
1334
+ v = '/' + v;
1290
1335
  }
1336
+ }
1291
1337
 
1292
- v = URI.recodePath(v);
1293
- this._parts.path = this._parts.path.replace(replace, v);
1294
- this.build(!build);
1295
- return this;
1338
+ // directories always end with a slash
1339
+ if (v && v.charAt(v.length - 1) !== '/') {
1340
+ v += '/';
1341
+ }
1342
+
1343
+ v = URI.recodePath(v);
1344
+ this._parts.path = this._parts.path.replace(replace, v);
1345
+ this.build(!build);
1346
+ return this;
1296
1347
  }
1297
- };
1298
- p.filename = function(v, build) {
1348
+ };
1349
+ p.filename = function(v, build) {
1299
1350
  if (this._parts.urn) {
1300
- return v === undefined ? '' : this;
1351
+ return v === undefined ? '' : this;
1301
1352
  }
1302
1353
 
1303
1354
  if (v === undefined || v === true) {
1304
- if (!this._parts.path || this._parts.path === '/') {
1305
- return "";
1306
- }
1355
+ if (!this._parts.path || this._parts.path === '/') {
1356
+ return '';
1357
+ }
1307
1358
 
1308
- var pos = this._parts.path.lastIndexOf('/');
1309
- var res = this._parts.path.substring(pos+1);
1359
+ var pos = this._parts.path.lastIndexOf('/');
1360
+ var res = this._parts.path.substring(pos+1);
1310
1361
 
1311
- return v ? URI.decodePathSegment(res) : res;
1362
+ return v ? URI.decodePathSegment(res) : res;
1312
1363
  } else {
1313
- var mutatedDirectory = false;
1314
-
1315
- if (v.charAt(0) === '/') {
1316
- v = v.substring(1);
1317
- }
1364
+ var mutatedDirectory = false;
1318
1365
 
1319
- if (v.match(/\.?\//)) {
1320
- mutatedDirectory = true;
1321
- }
1366
+ if (v.charAt(0) === '/') {
1367
+ v = v.substring(1);
1368
+ }
1322
1369
 
1323
- var replace = new RegExp(escapeRegEx(this.filename()) + "$");
1324
- v = URI.recodePath(v);
1325
- this._parts.path = this._parts.path.replace(replace, v);
1370
+ if (v.match(/\.?\//)) {
1371
+ mutatedDirectory = true;
1372
+ }
1326
1373
 
1327
- if (mutatedDirectory) {
1328
- this.normalizePath(build);
1329
- } else {
1330
- this.build(!build);
1331
- }
1374
+ var replace = new RegExp(escapeRegEx(this.filename()) + '$');
1375
+ v = URI.recodePath(v);
1376
+ this._parts.path = this._parts.path.replace(replace, v);
1332
1377
 
1333
- return this;
1378
+ if (mutatedDirectory) {
1379
+ this.normalizePath(build);
1380
+ } else {
1381
+ this.build(!build);
1382
+ }
1383
+
1384
+ return this;
1334
1385
  }
1335
- };
1336
- p.suffix = function(v, build) {
1386
+ };
1387
+ p.suffix = function(v, build) {
1337
1388
  if (this._parts.urn) {
1338
- return v === undefined ? '' : this;
1389
+ return v === undefined ? '' : this;
1339
1390
  }
1340
1391
 
1341
1392
  if (v === undefined || v === true) {
1342
- if (!this._parts.path || this._parts.path === '/') {
1343
- return "";
1344
- }
1345
-
1346
- var filename = this.filename();
1347
- var pos = filename.lastIndexOf('.');
1348
- var s, res;
1349
-
1350
- if (pos === -1) {
1351
- return "";
1352
- }
1353
-
1354
- // suffix may only contain alnum characters (yup, I made this up.)
1355
- s = filename.substring(pos+1);
1356
- res = (/^[a-z0-9%]+$/i).test(s) ? s : "";
1357
- return v ? URI.decodePathSegment(res) : res;
1393
+ if (!this._parts.path || this._parts.path === '/') {
1394
+ return '';
1395
+ }
1396
+
1397
+ var filename = this.filename();
1398
+ var pos = filename.lastIndexOf('.');
1399
+ var s, res;
1400
+
1401
+ if (pos === -1) {
1402
+ return '';
1403
+ }
1404
+
1405
+ // suffix may only contain alnum characters (yup, I made this up.)
1406
+ s = filename.substring(pos+1);
1407
+ res = (/^[a-z0-9%]+$/i).test(s) ? s : '';
1408
+ return v ? URI.decodePathSegment(res) : res;
1358
1409
  } else {
1359
- if (v.charAt(0) === '.') {
1360
- v = v.substring(1);
1361
- }
1362
-
1363
- var suffix = this.suffix();
1364
- var replace;
1410
+ if (v.charAt(0) === '.') {
1411
+ v = v.substring(1);
1412
+ }
1365
1413
 
1366
- if (!suffix) {
1367
- if (!v) {
1368
- return this;
1369
- }
1414
+ var suffix = this.suffix();
1415
+ var replace;
1370
1416
 
1371
- this._parts.path += '.' + URI.recodePath(v);
1372
- } else if (!v) {
1373
- replace = new RegExp(escapeRegEx("." + suffix) + "$");
1374
- } else {
1375
- replace = new RegExp(escapeRegEx(suffix) + "$");
1417
+ if (!suffix) {
1418
+ if (!v) {
1419
+ return this;
1376
1420
  }
1377
1421
 
1378
- if (replace) {
1379
- v = URI.recodePath(v);
1380
- this._parts.path = this._parts.path.replace(replace, v);
1381
- }
1422
+ this._parts.path += '.' + URI.recodePath(v);
1423
+ } else if (!v) {
1424
+ replace = new RegExp(escapeRegEx('.' + suffix) + '$');
1425
+ } else {
1426
+ replace = new RegExp(escapeRegEx(suffix) + '$');
1427
+ }
1382
1428
 
1383
- this.build(!build);
1384
- return this;
1429
+ if (replace) {
1430
+ v = URI.recodePath(v);
1431
+ this._parts.path = this._parts.path.replace(replace, v);
1432
+ }
1433
+
1434
+ this.build(!build);
1435
+ return this;
1385
1436
  }
1386
- };
1387
- p.segment = function(segment, v, build) {
1437
+ };
1438
+ p.segment = function(segment, v, build) {
1388
1439
  var separator = this._parts.urn ? ':' : '/';
1389
1440
  var path = this.path();
1390
1441
  var absolute = path.substring(0, 1) === '/';
1391
1442
  var segments = path.split(separator);
1392
1443
 
1393
1444
  if (segment !== undefined && typeof segment !== 'number') {
1394
- build = v;
1395
- v = segment;
1396
- segment = undefined;
1445
+ build = v;
1446
+ v = segment;
1447
+ segment = undefined;
1397
1448
  }
1398
1449
 
1399
1450
  if (segment !== undefined && typeof segment !== 'number') {
1400
- throw new Error("Bad segment '" + segment + "', must be 0-based integer");
1451
+ throw new Error('Bad segment "' + segment + '", must be 0-based integer');
1401
1452
  }
1402
1453
 
1403
1454
  if (absolute) {
1404
- segments.shift();
1455
+ segments.shift();
1405
1456
  }
1406
1457
 
1407
1458
  if (segment < 0) {
1408
- // allow negative indexes to address from the end
1409
- segment = Math.max(segments.length + segment, 0);
1459
+ // allow negative indexes to address from the end
1460
+ segment = Math.max(segments.length + segment, 0);
1410
1461
  }
1411
1462
 
1412
1463
  if (v === undefined) {
1413
- return segment === undefined
1414
- ? segments
1415
- : segments[segment];
1464
+ /*jshint laxbreak: true */
1465
+ return segment === undefined
1466
+ ? segments
1467
+ : segments[segment];
1468
+ /*jshint laxbreak: false */
1416
1469
  } else if (segment === null || segments[segment] === undefined) {
1417
- if (isArray(v)) {
1418
- segments = [];
1419
- // collapse empty elements within array
1420
- for (var i=0, l=v.length; i < l; i++) {
1421
- if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) {
1422
- continue;
1423
- }
1424
-
1425
- if (segments.length && !segments[segments.length -1].length) {
1426
- segments.pop();
1427
- }
1428
-
1429
- segments.push(v[i]);
1430
- }
1431
- } else if (v || (typeof v === "string")) {
1432
- if (segments[segments.length -1] === "") {
1433
- // empty trailing elements have to be overwritten
1434
- // to prevent results such as /foo//bar
1435
- segments[segments.length -1] = v;
1436
- } else {
1437
- segments.push(v);
1438
- }
1439
- }
1440
- } else {
1441
- if (v || (typeof v === "string" && v.length)) {
1442
- segments[segment] = v;
1470
+ if (isArray(v)) {
1471
+ segments = [];
1472
+ // collapse empty elements within array
1473
+ for (var i=0, l=v.length; i < l; i++) {
1474
+ if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) {
1475
+ continue;
1476
+ }
1477
+
1478
+ if (segments.length && !segments[segments.length -1].length) {
1479
+ segments.pop();
1480
+ }
1481
+
1482
+ segments.push(v[i]);
1483
+ }
1484
+ } else if (v || typeof v === 'string') {
1485
+ if (segments[segments.length -1] === '') {
1486
+ // empty trailing elements have to be overwritten
1487
+ // to prevent results such as /foo//bar
1488
+ segments[segments.length -1] = v;
1443
1489
  } else {
1444
- segments.splice(segment, 1);
1490
+ segments.push(v);
1445
1491
  }
1492
+ }
1493
+ } else {
1494
+ if (v) {
1495
+ segments[segment] = v;
1496
+ } else {
1497
+ segments.splice(segment, 1);
1498
+ }
1446
1499
  }
1447
1500
 
1448
1501
  if (absolute) {
1449
- segments.unshift("");
1502
+ segments.unshift('');
1450
1503
  }
1451
1504
 
1452
1505
  return this.path(segments.join(separator), build);
1453
- };
1454
- p.segmentCoded = function(segment, v, build) {
1506
+ };
1507
+ p.segmentCoded = function(segment, v, build) {
1455
1508
  var segments, i, l;
1456
1509
 
1457
1510
  if (typeof segment !== 'number') {
1458
- build = v;
1459
- v = segment;
1460
- segment = undefined;
1511
+ build = v;
1512
+ v = segment;
1513
+ segment = undefined;
1461
1514
  }
1462
1515
 
1463
1516
  if (v === undefined) {
1464
- segments = this.segment(segment, v, build);
1465
- if (!isArray(segments)) {
1466
- segments = segments !== undefined ? URI.decode(segments) : undefined;
1467
- } else {
1468
- for (i = 0, l = segments.length; i < l; i++) {
1469
- segments[i] = URI.decode(segments[i]);
1470
- }
1517
+ segments = this.segment(segment, v, build);
1518
+ if (!isArray(segments)) {
1519
+ segments = segments !== undefined ? URI.decode(segments) : undefined;
1520
+ } else {
1521
+ for (i = 0, l = segments.length; i < l; i++) {
1522
+ segments[i] = URI.decode(segments[i]);
1471
1523
  }
1524
+ }
1472
1525
 
1473
- return segments;
1526
+ return segments;
1474
1527
  }
1475
1528
 
1476
1529
  if (!isArray(v)) {
1477
- v = typeof v === 'string' ? URI.encode(v) : v;
1530
+ v = (typeof v === 'string' || v instanceof String) ? URI.encode(v) : v;
1478
1531
  } else {
1479
- for (i = 0, l = v.length; i < l; i++) {
1480
- v[i] = URI.decode(v[i]);
1481
- }
1532
+ for (i = 0, l = v.length; i < l; i++) {
1533
+ v[i] = URI.decode(v[i]);
1534
+ }
1482
1535
  }
1483
1536
 
1484
1537
  return this.segment(segment, v, build);
1485
- };
1538
+ };
1486
1539
 
1487
- // mutating query string
1488
- var q = p.query;
1489
- p.query = function(v, build) {
1540
+ // mutating query string
1541
+ var q = p.query;
1542
+ p.query = function(v, build) {
1490
1543
  if (v === true) {
1491
- return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1492
- } else if (typeof v === "function") {
1493
- var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1494
- var result = v.call(this, data);
1495
- this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1496
- this.build(!build);
1497
- return this;
1498
- } else if (v !== undefined && typeof v !== "string") {
1499
- this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1500
- this.build(!build);
1501
- return this;
1544
+ return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1545
+ } else if (typeof v === 'function') {
1546
+ var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1547
+ var result = v.call(this, data);
1548
+ this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1549
+ this.build(!build);
1550
+ return this;
1551
+ } else if (v !== undefined && typeof v !== 'string') {
1552
+ this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1553
+ this.build(!build);
1554
+ return this;
1502
1555
  } else {
1503
- return q.call(this, v, build);
1556
+ return q.call(this, v, build);
1504
1557
  }
1505
- };
1506
- p.setQuery = function(name, value, build) {
1558
+ };
1559
+ p.setQuery = function(name, value, build) {
1507
1560
  var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1508
-
1509
- if (typeof name === "object") {
1510
- for (var key in name) {
1511
- if (hasOwn.call(name, key)) {
1512
- data[key] = name[key];
1513
- }
1561
+
1562
+ if (typeof name === 'string' || name instanceof String) {
1563
+ data[name] = value !== undefined ? value : null;
1564
+ } else if (typeof name === 'object') {
1565
+ for (var key in name) {
1566
+ if (hasOwn.call(name, key)) {
1567
+ data[key] = name[key];
1514
1568
  }
1515
- } else if (typeof name === "string") {
1516
- data[name] = value !== undefined ? value : null;
1569
+ }
1517
1570
  } else {
1518
- throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");
1571
+ throw new TypeError('URI.addQuery() accepts an object, string as the name parameter');
1519
1572
  }
1520
-
1573
+
1521
1574
  this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1522
- if (typeof name !== "string") {
1523
- build = value;
1575
+ if (typeof name !== 'string') {
1576
+ build = value;
1524
1577
  }
1525
1578
 
1526
1579
  this.build(!build);
1527
1580
  return this;
1528
- };
1529
- p.addQuery = function(name, value, build) {
1581
+ };
1582
+ p.addQuery = function(name, value, build) {
1530
1583
  var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1531
1584
  URI.addQuery(data, name, value === undefined ? null : value);
1532
1585
  this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1533
- if (typeof name !== "string") {
1534
- build = value;
1586
+ if (typeof name !== 'string') {
1587
+ build = value;
1535
1588
  }
1536
1589
 
1537
1590
  this.build(!build);
1538
1591
  return this;
1539
- };
1540
- p.removeQuery = function(name, value, build) {
1592
+ };
1593
+ p.removeQuery = function(name, value, build) {
1541
1594
  var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1542
1595
  URI.removeQuery(data, name, value);
1543
1596
  this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
1544
- if (typeof name !== "string") {
1545
- build = value;
1597
+ if (typeof name !== 'string') {
1598
+ build = value;
1546
1599
  }
1547
1600
 
1548
1601
  this.build(!build);
1549
1602
  return this;
1550
- };
1551
- p.hasQuery = function(name, value, withinArray) {
1603
+ };
1604
+ p.hasQuery = function(name, value, withinArray) {
1552
1605
  var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
1553
1606
  return URI.hasQuery(data, name, value, withinArray);
1554
- };
1555
- p.setSearch = p.setQuery;
1556
- p.addSearch = p.addQuery;
1557
- p.removeSearch = p.removeQuery;
1558
- p.hasSearch = p.hasQuery;
1559
-
1560
- // sanitizing URLs
1561
- p.normalize = function() {
1607
+ };
1608
+ p.setSearch = p.setQuery;
1609
+ p.addSearch = p.addQuery;
1610
+ p.removeSearch = p.removeQuery;
1611
+ p.hasSearch = p.hasQuery;
1612
+
1613
+ // sanitizing URLs
1614
+ p.normalize = function() {
1562
1615
  if (this._parts.urn) {
1563
- return this
1564
- .normalizeProtocol(false)
1565
- .normalizeQuery(false)
1566
- .normalizeFragment(false)
1567
- .build();
1568
- }
1569
-
1570
- return this
1616
+ return this
1571
1617
  .normalizeProtocol(false)
1572
- .normalizeHostname(false)
1573
- .normalizePort(false)
1574
- .normalizePath(false)
1575
1618
  .normalizeQuery(false)
1576
1619
  .normalizeFragment(false)
1577
1620
  .build();
1578
- };
1579
- p.normalizeProtocol = function(build) {
1580
- if (typeof this._parts.protocol === "string") {
1581
- this._parts.protocol = this._parts.protocol.toLowerCase();
1582
- this.build(!build);
1621
+ }
1622
+
1623
+ return this
1624
+ .normalizeProtocol(false)
1625
+ .normalizeHostname(false)
1626
+ .normalizePort(false)
1627
+ .normalizePath(false)
1628
+ .normalizeQuery(false)
1629
+ .normalizeFragment(false)
1630
+ .build();
1631
+ };
1632
+ p.normalizeProtocol = function(build) {
1633
+ if (typeof this._parts.protocol === 'string') {
1634
+ this._parts.protocol = this._parts.protocol.toLowerCase();
1635
+ this.build(!build);
1583
1636
  }
1584
1637
 
1585
1638
  return this;
1586
- };
1587
- p.normalizeHostname = function(build) {
1639
+ };
1640
+ p.normalizeHostname = function(build) {
1588
1641
  if (this._parts.hostname) {
1589
- if (this.is('IDN') && punycode) {
1590
- this._parts.hostname = punycode.toASCII(this._parts.hostname);
1591
- } else if (this.is('IPv6') && IPv6) {
1592
- this._parts.hostname = IPv6.best(this._parts.hostname);
1593
- }
1642
+ if (this.is('IDN') && punycode) {
1643
+ this._parts.hostname = punycode.toASCII(this._parts.hostname);
1644
+ } else if (this.is('IPv6') && IPv6) {
1645
+ this._parts.hostname = IPv6.best(this._parts.hostname);
1646
+ }
1594
1647
 
1595
- this._parts.hostname = this._parts.hostname.toLowerCase();
1596
- this.build(!build);
1648
+ this._parts.hostname = this._parts.hostname.toLowerCase();
1649
+ this.build(!build);
1597
1650
  }
1598
1651
 
1599
1652
  return this;
1600
- };
1601
- p.normalizePort = function(build) {
1653
+ };
1654
+ p.normalizePort = function(build) {
1602
1655
  // remove port of it's the protocol's default
1603
- if (typeof this._parts.protocol === "string" && this._parts.port === URI.defaultPorts[this._parts.protocol]) {
1604
- this._parts.port = null;
1605
- this.build(!build);
1656
+ if (typeof this._parts.protocol === 'string' && this._parts.port === URI.defaultPorts[this._parts.protocol]) {
1657
+ this._parts.port = null;
1658
+ this.build(!build);
1606
1659
  }
1607
1660
 
1608
1661
  return this;
1609
- };
1610
- p.normalizePath = function(build) {
1662
+ };
1663
+ p.normalizePath = function(build) {
1611
1664
  if (this._parts.urn) {
1612
- return this;
1665
+ return this;
1613
1666
  }
1614
1667
 
1615
1668
  if (!this._parts.path || this._parts.path === '/') {
1616
- return this;
1669
+ return this;
1617
1670
  }
1618
1671
 
1619
1672
  var _was_relative;
1620
1673
  var _path = this._parts.path;
1674
+ var _leadingParents = '';
1621
1675
  var _parent, _pos;
1622
1676
 
1623
1677
  // handle relative paths
1624
1678
  if (_path.charAt(0) !== '/') {
1625
- _was_relative = true;
1626
- _path = '/' + _path;
1679
+ _was_relative = true;
1680
+ _path = '/' + _path;
1627
1681
  }
1628
1682
 
1629
1683
  // resolve simples
1630
1684
  _path = _path
1631
- .replace(/(\/(\.\/)+)|(\/\.$)/g, '/')
1632
- .replace(/\/{2,}/g, '/');
1685
+ .replace(/(\/(\.\/)+)|(\/\.$)/g, '/')
1686
+ .replace(/\/{2,}/g, '/');
1687
+
1688
+ // remember leading parents
1689
+ if (_was_relative) {
1690
+ _leadingParents = _path.substring(1).match(/^(\.\.\/)+/) || '';
1691
+ if (_leadingParents) {
1692
+ _leadingParents = _leadingParents[0];
1693
+ }
1694
+ }
1633
1695
 
1634
1696
  // resolve parents
1635
1697
  while (true) {
1636
- _parent = _path.indexOf('/../');
1637
- if (_parent === -1) {
1638
- // no more ../ to resolve
1639
- break;
1640
- } else if (_parent === 0) {
1641
- // top level cannot be relative...
1642
- _path = _path.substring(3);
1643
- break;
1644
- }
1645
-
1646
- _pos = _path.substring(0, _parent).lastIndexOf('/');
1647
- if (_pos === -1) {
1648
- _pos = _parent;
1649
- }
1650
- _path = _path.substring(0, _pos) + _path.substring(_parent + 3);
1698
+ _parent = _path.indexOf('/..');
1699
+ if (_parent === -1) {
1700
+ // no more ../ to resolve
1701
+ break;
1702
+ } else if (_parent === 0) {
1703
+ // top level cannot be relative, skip it
1704
+ _path = _path.substring(3);
1705
+ continue;
1706
+ }
1707
+
1708
+ _pos = _path.substring(0, _parent).lastIndexOf('/');
1709
+ if (_pos === -1) {
1710
+ _pos = _parent;
1711
+ }
1712
+ _path = _path.substring(0, _pos) + _path.substring(_parent + 3);
1651
1713
  }
1652
1714
 
1653
1715
  // revert to relative
1654
1716
  if (_was_relative && this.is('relative')) {
1655
- _path = _path.substring(1);
1717
+ _path = _leadingParents + _path.substring(1);
1656
1718
  }
1657
1719
 
1658
1720
  _path = URI.recodePath(_path);
1659
1721
  this._parts.path = _path;
1660
1722
  this.build(!build);
1661
1723
  return this;
1662
- };
1663
- p.normalizePathname = p.normalizePath;
1664
- p.normalizeQuery = function(build) {
1665
- if (typeof this._parts.query === "string") {
1666
- if (!this._parts.query.length) {
1667
- this._parts.query = null;
1668
- } else {
1669
- this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace));
1670
- }
1724
+ };
1725
+ p.normalizePathname = p.normalizePath;
1726
+ p.normalizeQuery = function(build) {
1727
+ if (typeof this._parts.query === 'string') {
1728
+ if (!this._parts.query.length) {
1729
+ this._parts.query = null;
1730
+ } else {
1731
+ this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace));
1732
+ }
1671
1733
 
1672
- this.build(!build);
1734
+ this.build(!build);
1673
1735
  }
1674
1736
 
1675
1737
  return this;
1676
- };
1677
- p.normalizeFragment = function(build) {
1738
+ };
1739
+ p.normalizeFragment = function(build) {
1678
1740
  if (!this._parts.fragment) {
1679
- this._parts.fragment = null;
1680
- this.build(!build);
1741
+ this._parts.fragment = null;
1742
+ this.build(!build);
1681
1743
  }
1682
1744
 
1683
1745
  return this;
1684
- };
1685
- p.normalizeSearch = p.normalizeQuery;
1686
- p.normalizeHash = p.normalizeFragment;
1746
+ };
1747
+ p.normalizeSearch = p.normalizeQuery;
1748
+ p.normalizeHash = p.normalizeFragment;
1687
1749
 
1688
- p.iso8859 = function() {
1750
+ p.iso8859 = function() {
1689
1751
  // expect unicode input, iso8859 output
1690
1752
  var e = URI.encode;
1691
1753
  var d = URI.decode;
@@ -1696,9 +1758,9 @@ p.iso8859 = function() {
1696
1758
  URI.encode = e;
1697
1759
  URI.decode = d;
1698
1760
  return this;
1699
- };
1761
+ };
1700
1762
 
1701
- p.unicode = function() {
1763
+ p.unicode = function() {
1702
1764
  // expect iso8859 input, unicode output
1703
1765
  var e = URI.encode;
1704
1766
  var d = URI.decode;
@@ -1709,100 +1771,102 @@ p.unicode = function() {
1709
1771
  URI.encode = e;
1710
1772
  URI.decode = d;
1711
1773
  return this;
1712
- };
1774
+ };
1713
1775
 
1714
- p.readable = function() {
1776
+ p.readable = function() {
1715
1777
  var uri = this.clone();
1716
1778
  // removing username, password, because they shouldn't be displayed according to RFC 3986
1717
- uri.username("").password("").normalize();
1779
+ uri.username('').password('').normalize();
1718
1780
  var t = '';
1719
1781
  if (uri._parts.protocol) {
1720
- t += uri._parts.protocol + '://';
1782
+ t += uri._parts.protocol + '://';
1721
1783
  }
1722
1784
 
1723
1785
  if (uri._parts.hostname) {
1724
- if (uri.is('punycode') && punycode) {
1725
- t += punycode.toUnicode(uri._parts.hostname);
1726
- if (uri._parts.port) {
1727
- t += ":" + uri._parts.port;
1728
- }
1729
- } else {
1730
- t += uri.host();
1786
+ if (uri.is('punycode') && punycode) {
1787
+ t += punycode.toUnicode(uri._parts.hostname);
1788
+ if (uri._parts.port) {
1789
+ t += ':' + uri._parts.port;
1731
1790
  }
1791
+ } else {
1792
+ t += uri.host();
1793
+ }
1732
1794
  }
1733
1795
 
1734
1796
  if (uri._parts.hostname && uri._parts.path && uri._parts.path.charAt(0) !== '/') {
1735
- t += '/';
1797
+ t += '/';
1736
1798
  }
1737
1799
 
1738
1800
  t += uri.path(true);
1739
1801
  if (uri._parts.query) {
1740
- var q = '';
1741
- for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) {
1742
- var kv = (qp[i] || "").split('=');
1743
- q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace)
1744
- .replace(/&/g, '%26');
1745
-
1746
- if (kv[1] !== undefined) {
1747
- q += "=" + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace)
1748
- .replace(/&/g, '%26');
1749
- }
1802
+ var q = '';
1803
+ for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) {
1804
+ var kv = (qp[i] || '').split('=');
1805
+ q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace)
1806
+ .replace(/&/g, '%26');
1807
+
1808
+ if (kv[1] !== undefined) {
1809
+ q += '=' + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace)
1810
+ .replace(/&/g, '%26');
1750
1811
  }
1751
- t += '?' + q.substring(1);
1812
+ }
1813
+ t += '?' + q.substring(1);
1752
1814
  }
1753
1815
 
1754
1816
  t += URI.decodeQuery(uri.hash(), true);
1755
1817
  return t;
1756
- };
1818
+ };
1757
1819
 
1758
- // resolving relative and absolute URLs
1759
- p.absoluteTo = function(base) {
1820
+ // resolving relative and absolute URLs
1821
+ p.absoluteTo = function(base) {
1760
1822
  var resolved = this.clone();
1761
1823
  var properties = ['protocol', 'username', 'password', 'hostname', 'port'];
1762
1824
  var basedir, i, p;
1763
1825
 
1764
1826
  if (this._parts.urn) {
1765
- throw new Error('URNs do not have any generally defined hierarchical components');
1827
+ throw new Error('URNs do not have any generally defined hierarchical components');
1766
1828
  }
1767
1829
 
1768
1830
  if (!(base instanceof URI)) {
1769
- base = new URI(base);
1831
+ base = new URI(base);
1770
1832
  }
1771
-
1833
+
1772
1834
  if (!resolved._parts.protocol) {
1773
- resolved._parts.protocol = base._parts.protocol;
1835
+ resolved._parts.protocol = base._parts.protocol;
1774
1836
  }
1775
-
1837
+
1776
1838
  if (this._parts.hostname) {
1777
- return resolved;
1839
+ return resolved;
1778
1840
  }
1779
1841
 
1780
- for (i = 0; p = properties[i]; i++) {
1781
- resolved._parts[p] = base._parts[p];
1842
+ for (i = 0; (p = properties[i]); i++) {
1843
+ resolved._parts[p] = base._parts[p];
1782
1844
  }
1783
-
1784
- properties = ['query', 'path'];
1785
- for (i = 0; p = properties[i]; i++) {
1786
- if (!resolved._parts[p] && base._parts[p]) {
1787
- resolved._parts[p] = base._parts[p];
1788
- }
1845
+
1846
+ if (!resolved._parts.path) {
1847
+ resolved._parts.path = base._parts.path;
1848
+ if (!resolved._parts.query) {
1849
+ resolved._parts.query = base._parts.query;
1850
+ }
1851
+ } else if (resolved._parts.path.substring(-2) === '..') {
1852
+ resolved._parts.path += '/';
1789
1853
  }
1790
1854
 
1791
1855
  if (resolved.path().charAt(0) !== '/') {
1792
- basedir = base.directory();
1793
- resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path;
1794
- resolved.normalizePath();
1856
+ basedir = base.directory();
1857
+ resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path;
1858
+ resolved.normalizePath();
1795
1859
  }
1796
1860
 
1797
1861
  resolved.build();
1798
1862
  return resolved;
1799
- };
1800
- p.relativeTo = function(base) {
1863
+ };
1864
+ p.relativeTo = function(base) {
1801
1865
  var relative = this.clone().normalize();
1802
1866
  var relativeParts, baseParts, common, relativePath, basePath;
1803
1867
 
1804
1868
  if (relative._parts.urn) {
1805
- throw new Error('URNs do not have any generally defined hierarchical components');
1869
+ throw new Error('URNs do not have any generally defined hierarchical components');
1806
1870
  }
1807
1871
 
1808
1872
  base = new URI(base).normalize();
@@ -1812,57 +1876,57 @@ p.relativeTo = function(base) {
1812
1876
  basePath = base.path();
1813
1877
 
1814
1878
  if (relativePath.charAt(0) !== '/') {
1815
- throw new Error('URI is already relative');
1879
+ throw new Error('URI is already relative');
1816
1880
  }
1817
1881
 
1818
1882
  if (basePath.charAt(0) !== '/') {
1819
- throw new Error('Cannot calculate a URI relative to another relative URI');
1883
+ throw new Error('Cannot calculate a URI relative to another relative URI');
1820
1884
  }
1821
1885
 
1822
1886
  if (relativeParts.protocol === baseParts.protocol) {
1823
- relativeParts.protocol = null;
1887
+ relativeParts.protocol = null;
1824
1888
  }
1825
1889
 
1826
1890
  if (relativeParts.username !== baseParts.username || relativeParts.password !== baseParts.password) {
1827
- return relative.build();
1891
+ return relative.build();
1828
1892
  }
1829
1893
 
1830
1894
  if (relativeParts.protocol !== null || relativeParts.username !== null || relativeParts.password !== null) {
1831
- return relative.build();
1895
+ return relative.build();
1832
1896
  }
1833
1897
 
1834
1898
  if (relativeParts.hostname === baseParts.hostname && relativeParts.port === baseParts.port) {
1835
- relativeParts.hostname = null;
1836
- relativeParts.port = null;
1899
+ relativeParts.hostname = null;
1900
+ relativeParts.port = null;
1837
1901
  } else {
1838
- return relative.build();
1902
+ return relative.build();
1839
1903
  }
1840
1904
 
1841
1905
  if (relativePath === basePath) {
1842
- relativeParts.path = '';
1843
- return relative.build();
1906
+ relativeParts.path = '';
1907
+ return relative.build();
1844
1908
  }
1845
-
1909
+
1846
1910
  // determine common sub path
1847
1911
  common = URI.commonPath(relative.path(), base.path());
1848
1912
 
1849
1913
  // If the paths have nothing in common, return a relative URL with the absolute path.
1850
1914
  if (!common) {
1851
- return relative.build();
1915
+ return relative.build();
1852
1916
  }
1853
1917
 
1854
1918
  var parents = baseParts.path
1855
- .substring(common.length)
1856
- .replace(/[^\/]*$/, '')
1857
- .replace(/.*?\//g, '../');
1919
+ .substring(common.length)
1920
+ .replace(/[^\/]*$/, '')
1921
+ .replace(/.*?\//g, '../');
1858
1922
 
1859
1923
  relativeParts.path = parents + relativeParts.path.substring(common.length);
1860
1924
 
1861
1925
  return relative.build();
1862
- };
1926
+ };
1863
1927
 
1864
- // comparing URIs
1865
- p.equals = function(uri) {
1928
+ // comparing URIs
1929
+ p.equals = function(uri) {
1866
1930
  var one = this.clone();
1867
1931
  var two = new URI(uri);
1868
1932
  var one_map = {};
@@ -1875,64 +1939,64 @@ p.equals = function(uri) {
1875
1939
 
1876
1940
  // exact match
1877
1941
  if (one.toString() === two.toString()) {
1878
- return true;
1942
+ return true;
1879
1943
  }
1880
1944
 
1881
1945
  // extract query string
1882
1946
  one_query = one.query();
1883
1947
  two_query = two.query();
1884
- one.query("");
1885
- two.query("");
1948
+ one.query('');
1949
+ two.query('');
1886
1950
 
1887
1951
  // definitely not equal if not even non-query parts match
1888
1952
  if (one.toString() !== two.toString()) {
1889
- return false;
1953
+ return false;
1890
1954
  }
1891
1955
 
1892
1956
  // query parameters have the same length, even if they're permuted
1893
1957
  if (one_query.length !== two_query.length) {
1894
- return false;
1958
+ return false;
1895
1959
  }
1896
1960
 
1897
1961
  one_map = URI.parseQuery(one_query, this._parts.escapeQuerySpace);
1898
1962
  two_map = URI.parseQuery(two_query, this._parts.escapeQuerySpace);
1899
1963
 
1900
1964
  for (key in one_map) {
1901
- if (hasOwn.call(one_map, key)) {
1902
- if (!isArray(one_map[key])) {
1903
- if (one_map[key] !== two_map[key]) {
1904
- return false;
1905
- }
1906
- } else if (!arraysEqual(one_map[key], two_map[key])) {
1907
- return false;
1908
- }
1909
-
1910
- checked[key] = true;
1965
+ if (hasOwn.call(one_map, key)) {
1966
+ if (!isArray(one_map[key])) {
1967
+ if (one_map[key] !== two_map[key]) {
1968
+ return false;
1969
+ }
1970
+ } else if (!arraysEqual(one_map[key], two_map[key])) {
1971
+ return false;
1911
1972
  }
1973
+
1974
+ checked[key] = true;
1975
+ }
1912
1976
  }
1913
1977
 
1914
1978
  for (key in two_map) {
1915
- if (hasOwn.call(two_map, key)) {
1916
- if (!checked[key]) {
1917
- // two contains a parameter not present in one
1918
- return false;
1919
- }
1979
+ if (hasOwn.call(two_map, key)) {
1980
+ if (!checked[key]) {
1981
+ // two contains a parameter not present in one
1982
+ return false;
1920
1983
  }
1984
+ }
1921
1985
  }
1922
1986
 
1923
1987
  return true;
1924
- };
1988
+ };
1925
1989
 
1926
- // state
1927
- p.duplicateQueryParameters = function(v) {
1990
+ // state
1991
+ p.duplicateQueryParameters = function(v) {
1928
1992
  this._parts.duplicateQueryParameters = !!v;
1929
1993
  return this;
1930
- };
1994
+ };
1931
1995
 
1932
- p.escapeQuerySpace = function(v) {
1996
+ p.escapeQuerySpace = function(v) {
1933
1997
  this._parts.escapeQuerySpace = !!v;
1934
1998
  return this;
1935
- };
1999
+ };
1936
2000
 
1937
- return URI;
2001
+ return URI;
1938
2002
  }));