epuber 0.3.5

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