uri-js-rails 1.11.2 → 1.14.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }));