@angular-wave/angular.ts 0.7.8 → 0.8.0

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 (285) hide show
  1. package/@types/core/parse/parse.d.ts +6 -7
  2. package/@types/directive/bind/bind.d.ts +2 -1
  3. package/@types/index.d.ts +1 -1
  4. package/@types/interface.d.ts +3 -1
  5. package/@types/{public.d.ts → ng.d.ts} +2 -2
  6. package/@types/router/globals.d.ts +1 -1
  7. package/@types/router/path/path-utils.d.ts +8 -11
  8. package/@types/router/state/interface.d.ts +1 -1
  9. package/@types/router/state/state-object.d.ts +1 -1
  10. package/@types/router/state/state-service.d.ts +8 -7
  11. package/@types/router/state-filters.d.ts +24 -2
  12. package/@types/router/transition/transition.d.ts +12 -15
  13. package/@types/router/url/url-matcher.d.ts +3 -3
  14. package/@types/router/url/url-rule.d.ts +1 -0
  15. package/@types/router/url/url-rules.d.ts +26 -6
  16. package/@types/router/url/url-service.d.ts +28 -38
  17. package/@types/services/http/http.d.ts +48 -1
  18. package/@types/services/http-backend/http-backend.d.ts +48 -35
  19. package/@types/services/location/interface.d.ts +55 -0
  20. package/@types/services/location/location.d.ts +225 -252
  21. package/@types/shared/common.d.ts +0 -2
  22. package/@types/shared/interface.d.ts +0 -4
  23. package/@types/{router/common → shared}/queue.d.ts +2 -2
  24. package/@types/shared/url-utils/interface.d.ts +0 -1
  25. package/@types/shared/url-utils/url-utils.d.ts +0 -5
  26. package/@types/shared/utils.d.ts +29 -6
  27. package/Makefile +6 -3
  28. package/dist/angular-ts.esm.js +960 -1062
  29. package/dist/angular-ts.umd.js +960 -1062
  30. package/dist/angular-ts.umd.min.js +1 -1
  31. package/docs/assets/scss/index.scss +23 -0
  32. package/docs/content/_index.md +9 -8
  33. package/docs/content/docs/_index.md +1 -1
  34. package/docs/content/docs/directive/app.md +1 -1
  35. package/docs/content/docs/directive/bind.md +1 -1
  36. package/docs/content/docs/directive/blur.md +1 -1
  37. package/docs/content/docs/directive/channel.md +2 -2
  38. package/docs/content/docs/directive/class-even.md +1 -1
  39. package/docs/content/docs/directive/class-odd.md +1 -1
  40. package/docs/content/docs/directive/class.md +1 -1
  41. package/docs/content/docs/directive/click.md +1 -1
  42. package/docs/content/docs/directive/copy.md +1 -1
  43. package/docs/content/docs/directive/cut.md +1 -1
  44. package/docs/content/docs/directive/dblclick.md +1 -1
  45. package/docs/content/docs/directive/focus.md +1 -1
  46. package/docs/content/docs/directive/get.md +3 -3
  47. package/docs/content/docs/directive/keydown.md +1 -1
  48. package/docs/content/docs/directive/keyup.md +1 -1
  49. package/docs/content/docs/directive/load.md +1 -1
  50. package/docs/content/docs/directive/mousedown.md +1 -1
  51. package/docs/content/docs/directive/mouseenter.md +1 -1
  52. package/docs/content/docs/directive/mouseleave.md +1 -1
  53. package/docs/content/docs/directive/mousemove.md +1 -1
  54. package/docs/content/docs/directive/mouseout.md +1 -1
  55. package/docs/content/docs/directive/mouseover.md +1 -1
  56. package/docs/content/docs/directive/mouseup.md +1 -1
  57. package/docs/content/docs/directive/non-bindable.md +28 -0
  58. package/docs/content/docs/provider/locationProvider.md +26 -0
  59. package/docs/content/docs/provider/templateCacheProvider.md +2 -2
  60. package/docs/content/docs/service/location.md +57 -0
  61. package/docs/content/docs/service/url.md +5 -0
  62. package/docs/layouts/partials/hooks/head-end.html +1 -1
  63. package/docs/layouts/shortcodes/version.html +1 -0
  64. package/docs/static/examples/counter/counter-test.html +0 -4
  65. package/docs/static/examples/eventbus/eventbus-test.html +0 -4
  66. package/docs/static/examples/ng-non-bindable/ng-non-bindable-test.html +13 -0
  67. package/docs/static/examples/ng-non-bindable/ng-non-bindable.html +3 -0
  68. package/docs/static/examples/ng-non-bindable/ng-non-bindable.test.js +11 -0
  69. package/docs/static/typedoc/assets/highlight.css +6 -6
  70. package/docs/static/typedoc/assets/navigation.js +1 -1
  71. package/docs/static/typedoc/assets/search.js +1 -1
  72. package/docs/static/typedoc/classes/Location.html +55 -0
  73. package/docs/static/typedoc/classes/LocationProvider.html +20 -0
  74. package/docs/static/typedoc/index.html +1 -1
  75. package/docs/static/typedoc/interfaces/DefaultPorts.html +5 -0
  76. package/docs/static/typedoc/interfaces/Html5Mode.html +23 -0
  77. package/docs/static/typedoc/interfaces/Provider.html +2 -1
  78. package/docs/static/typedoc/interfaces/UrlParts.html +9 -0
  79. package/docs/static/typedoc/types/AnnotatedFactory.html +1 -1
  80. package/docs/static/typedoc/types/Expression.html +1 -1
  81. package/docs/static/typedoc/types/UrlChangeListener.html +5 -0
  82. package/docs/static/version.js +13 -0
  83. package/docs/test-results/.last-run.json +4 -0
  84. package/docs/test-results/static-examples-counter-counter-counter-example/error-context.md +50 -0
  85. package/package.json +1 -1
  86. package/src/{loader.js → angular.js} +1 -1
  87. package/src/angular.spec.js +189 -21
  88. package/src/animations/animate-css.js +17 -18
  89. package/src/animations/animate.spec.js +1 -1
  90. package/src/animations/shared.js +2 -3
  91. package/src/binding.spec.js +1 -1
  92. package/src/core/compile/compile.js +4 -7
  93. package/src/core/compile/compile.spec.js +1 -1
  94. package/src/core/controller/controller.spec.js +1 -1
  95. package/src/core/controller/controller.test.js +1 -0
  96. package/src/core/di/injector.js +7 -8
  97. package/src/core/di/injector.spec.js +2 -2
  98. package/src/core/di/injector.test.js +2 -2
  99. package/src/core/di/internal-injector.js +3 -6
  100. package/src/core/filter/filter.js +1 -1
  101. package/src/core/filter/filter.spec.js +1 -1
  102. package/src/core/filter/filter.test.js +1 -0
  103. package/src/core/interpolate/interpolate.js +4 -6
  104. package/src/core/interpolate/interpolate.spec.js +1 -1
  105. package/src/core/interpolate/interpolate.test.js +1 -0
  106. package/src/core/parse/ast/ast.spec.js +1 -1
  107. package/src/core/parse/ast/ast.test.js +1 -1
  108. package/src/core/parse/lexer/lexer.spec.js +1 -1
  109. package/src/core/parse/parse.js +150 -146
  110. package/src/core/parse/parse.spec.js +17 -16
  111. package/src/core/prop.spec.js +1 -1
  112. package/src/core/root-element.spec.js +1 -1
  113. package/src/core/scope/scope.js +10 -11
  114. package/src/core/scope/scope.spec.js +3 -4
  115. package/src/directive/aria/aria.spec.js +1 -1
  116. package/src/directive/aria/aria.test.js +1 -0
  117. package/src/directive/attrs/attrs.spec.js +1 -1
  118. package/src/directive/attrs/attrs.test.js +1 -0
  119. package/src/directive/attrs/boolean.spec.js +1 -1
  120. package/src/directive/attrs/boolean.test.js +1 -0
  121. package/src/directive/attrs/element-style.spec.js +1 -1
  122. package/src/directive/attrs/element-style.test.js +1 -0
  123. package/src/directive/attrs/src.spec.js +1 -1
  124. package/src/directive/attrs/src.test.js +1 -0
  125. package/src/directive/bind/bind-html.spec.js +1 -1
  126. package/src/directive/bind/bind.js +1 -0
  127. package/src/directive/bind/bind.spec.js +1 -1
  128. package/src/directive/bind/bind.test.js +1 -0
  129. package/src/directive/channel/channel.spec.js +1 -1
  130. package/src/directive/channel/channel.test.js +1 -0
  131. package/src/directive/class/class.spec.js +1 -1
  132. package/src/directive/class/class.test.js +1 -0
  133. package/src/directive/cloak/cloak.spec.js +1 -1
  134. package/src/directive/cloak/cloak.test.js +1 -0
  135. package/src/directive/controller/controller.spec.js +1 -1
  136. package/src/directive/controller/controller.test.js +1 -0
  137. package/src/directive/events/click.spec.js +1 -1
  138. package/src/directive/events/event.spec.js +1 -1
  139. package/src/directive/events/events.test.js +1 -0
  140. package/src/directive/form/form.js +8 -5
  141. package/src/directive/form/form.spec.js +1 -1
  142. package/src/directive/form/form.test.js +1 -0
  143. package/src/directive/http/delete.spec.js +1 -1
  144. package/src/directive/http/form-test.html +18 -0
  145. package/src/directive/http/get.spec.js +1 -1
  146. package/src/directive/http/http.js +12 -3
  147. package/src/directive/http/post.spec.js +504 -9
  148. package/src/directive/http/put.spec.js +1 -1
  149. package/src/directive/if/if.spec.js +1 -1
  150. package/src/directive/include/include.spec.js +1 -1
  151. package/src/directive/init/init.spec.js +1 -1
  152. package/src/directive/init/init.test.js +1 -0
  153. package/src/directive/input/input.js +13 -15
  154. package/src/directive/input/input.spec.js +1 -2
  155. package/src/directive/input/input.test.js +1 -0
  156. package/src/directive/messages/messages.spec.js +1 -1
  157. package/src/directive/messages/messages.test.js +1 -0
  158. package/src/directive/model/model.js +13 -13
  159. package/src/directive/model/model.spec.js +1 -1
  160. package/src/directive/model/model.test.js +1 -0
  161. package/src/directive/model-options/model-option.test.js +1 -0
  162. package/src/directive/model-options/model-options.js +1 -1
  163. package/src/directive/model-options/model-options.spec.js +1 -1
  164. package/src/directive/non-bindable/non-bindable.spec.js +1 -1
  165. package/src/directive/non-bindable/non-bindable.test.js +1 -0
  166. package/src/directive/observe/observe.spec.js +1 -1
  167. package/src/directive/observe/observe.test.js +1 -0
  168. package/src/directive/on/on.spec.js +1 -1
  169. package/src/directive/on/on.test.js +1 -0
  170. package/src/directive/options/options.spec.js +1 -1
  171. package/src/directive/options/options.test.js +1 -0
  172. package/src/directive/ref/href.spec.js +1 -1
  173. package/src/directive/ref/href.test.js +2 -0
  174. package/src/directive/ref/ref.spec.js +1 -1
  175. package/src/directive/repeat/repeat.spec.js +2 -3
  176. package/src/directive/repeat/repeat.test.js +1 -0
  177. package/src/directive/script/script.spec.js +1 -1
  178. package/src/directive/script/script.test.js +1 -0
  179. package/src/directive/select/select.js +1 -1
  180. package/src/directive/select/select.spec.js +1 -1
  181. package/src/directive/select/select.test.js +1 -0
  182. package/src/directive/setter/setter.spec.js +1 -1
  183. package/src/directive/setter/setter.test.js +1 -0
  184. package/src/directive/show-hide/show-hide.spec.js +1 -1
  185. package/src/directive/show-hide/show-hide.test.js +1 -0
  186. package/src/directive/style/style.spec.js +1 -1
  187. package/src/directive/style/style.test.js +1 -0
  188. package/src/directive/switch/switch.spec.js +1 -1
  189. package/src/directive/switch/switch.test.js +1 -0
  190. package/src/directive/validators/validators.js +82 -84
  191. package/src/directive/validators/validators.spec.js +5 -4
  192. package/src/directive/validators/validators.test.js +1 -0
  193. package/src/filters/filter.spec.js +1 -1
  194. package/src/filters/filters.spec.js +1 -1
  195. package/src/filters/limit-to.js +2 -3
  196. package/src/filters/limit-to.spec.js +1 -1
  197. package/src/filters/order-by.spec.js +1 -1
  198. package/src/index.js +1 -1
  199. package/src/injection-tokens.js +5 -1
  200. package/src/interface.ts +3 -1
  201. package/src/loader.md +0 -155
  202. package/src/{public.js → ng.js} +7 -8
  203. package/src/{public.spec.js → ng.spec.js} +1 -1
  204. package/src/router/directives/state-directives.spec.js +8 -7
  205. package/src/router/directives/view-directive.js +2 -8
  206. package/src/router/directives/view-directive.spec.js +8 -9
  207. package/src/router/{common/common.html → glob/glob.html} +2 -3
  208. package/src/router/{common/common.test.js → glob/glob.test.js} +2 -1
  209. package/src/router/globals.js +1 -1
  210. package/src/router/path/path-utils.js +5 -0
  211. package/src/router/router-test-hashbang.html +45 -0
  212. package/src/router/services.spec.js +5 -6
  213. package/src/router/state/interface.ts +1 -1
  214. package/src/router/state/state-builder.js +3 -3
  215. package/src/router/state/state-builder.spec.js +1 -1
  216. package/src/router/state/state-object.js +1 -1
  217. package/src/router/state/state-registry.js +2 -2
  218. package/src/router/state/state-service.js +13 -10
  219. package/src/router/state/state.spec.js +23 -22
  220. package/src/router/state/state.test.js +1 -0
  221. package/src/router/state/views.js +1 -1
  222. package/src/router/state-filter.spec.js +1 -1
  223. package/src/router/state-filters.js +13 -9
  224. package/src/router/template-factory.js +5 -4
  225. package/src/router/template-factory.spec.js +1 -1
  226. package/src/router/transition/hook-registry.js +1 -1
  227. package/src/router/transition/transition-service.js +6 -5
  228. package/src/router/transition/transition.js +4 -4
  229. package/src/router/url/url-matcher.js +3 -3
  230. package/src/router/url/url-rule.js +1 -0
  231. package/src/router/url/url-rules.js +8 -5
  232. package/src/router/url/url-service.js +77 -76
  233. package/src/router/url/url-service.spec.js +55 -39
  234. package/src/router/url/url.test.js +1 -0
  235. package/src/router/view/view.js +4 -5
  236. package/src/router/view/view.spec.js +10 -12
  237. package/src/router/view/view.test.js +1 -0
  238. package/src/router/view-hook.spec.js +1 -1
  239. package/src/router/view-scroll.spec.js +1 -1
  240. package/src/services/anchor-scroll.html +2 -2
  241. package/src/services/anchor-scroll.js +5 -4
  242. package/src/services/http/http.js +9 -4
  243. package/src/services/http/http.spec.js +2 -7
  244. package/src/services/http/template-request.spec.js +1 -1
  245. package/src/services/http-backend/http-backend.js +51 -77
  246. package/src/services/http-backend/http-backend.spec.js +1 -2
  247. package/src/services/http-backend/http-backend.test.js +1 -0
  248. package/src/services/location/interface.ts +62 -0
  249. package/src/services/location/location.js +433 -520
  250. package/src/services/location/location.spec.js +909 -530
  251. package/src/services/location/location.test.js +2 -2
  252. package/src/services/log/log.spec.js +1 -1
  253. package/src/services/log/log.test.js +1 -0
  254. package/src/services/pubsub/pubsub.spec.js +1 -1
  255. package/src/services/sce/sce.js +5 -7
  256. package/src/services/sce/sce.md +2 -2
  257. package/src/services/sce/sce.spec.js +1 -1
  258. package/src/services/template-cache/template-cache.spec.js +1 -1
  259. package/src/services/template-cache/template-cache.test.js +1 -0
  260. package/src/shared/common.js +0 -5
  261. package/src/shared/common.spec.js +1 -1
  262. package/src/shared/interface.ts +0 -4
  263. package/src/{router/common → shared}/queue.js +7 -7
  264. package/src/shared/shared.html +1 -0
  265. package/src/shared/shared.test.js +1 -0
  266. package/src/shared/url-utils/interface.ts +0 -2
  267. package/src/shared/url-utils/url-utils.js +6 -30
  268. package/src/shared/url-utils/url-utils.spec.js +10 -9
  269. package/src/shared/utils.js +32 -9
  270. package/src/shared/utils.spec.js +35 -1
  271. package/src/src.html +1 -2
  272. package/typedoc.json +0 -1
  273. package/utils/express.js +27 -1
  274. package/utils/version.cjs +23 -0
  275. package/@types/router/state-provider.d.ts +0 -123
  276. package/src/directive/non-bindable/non-bindable.md +0 -17
  277. package/src/loader.spec.js +0 -169
  278. package/src/router/state-provider.js +0 -146
  279. package/src/services/location/location.md +0 -114
  280. package/src/shared/url-utils/url-utils.md +0 -46
  281. /package/@types/{loader.d.ts → angular.d.ts} +0 -0
  282. /package/@types/router/{common → glob}/glob.d.ts +0 -0
  283. /package/src/router/{common → glob}/glob.js +0 -0
  284. /package/src/router/{common → glob}/glob.spec.js +0 -0
  285. /package/src/{router/common → shared}/queue.spec.js +0 -0
@@ -1,7 +1,6 @@
1
1
  import { trimEmptyHash, urlResolve } from "../../shared/url-utils/url-utils.js";
2
2
  import {
3
3
  encodeUriSegment,
4
- isBoolean,
5
4
  isDefined,
6
5
  isNumber,
7
6
  isObject,
@@ -9,263 +8,161 @@ import {
9
8
  isUndefined,
10
9
  minErr,
11
10
  parseKeyValue,
12
- toInt,
13
11
  toKeyValue,
14
12
  equals,
13
+ startsWith,
15
14
  } from "../../shared/utils.js";
16
15
  import { getBaseHref } from "../../shared/dom.js";
16
+ import { $injectTokens as $t } from "../../injection-tokens.js";
17
+
18
+ const PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/;
19
+ const $locationMinErr = minErr("$location");
20
+
21
+ let urlUpdatedByLocation = false;
17
22
 
18
23
  /**
19
- * @typedef {Object} DefaultPorts
20
- * @property {number} http
21
- * @property {number} https
22
- * @property {number} ftp
24
+ * @ignore
25
+ * The pathname, beginning with "/"
26
+ * @type {string}
23
27
  */
28
+ let $$path;
24
29
 
25
30
  /**
26
- * Represents the configuration options for HTML5 mode.
27
- *
28
- * @typedef {Object} Html5Mode
29
- * @property {boolean} enabled - (default: false) If true, will rely on `history.pushState` to
30
- * change URLs where supported. Falls back to hash-prefixed paths in browsers that do not
31
- * support `pushState`.
32
- * @property {boolean} requireBase - (default: `true`) When html5Mode is enabled, specifies
33
- * whether or not a `<base>` tag is required to be present. If both `enabled` and `requireBase`
34
- * are true, and a `<base>` tag is not present, an error will be thrown when `$location` is injected.
35
- * See the {@link guide/$location $location guide} for more information.
36
- * @property {boolean|string} rewriteLinks - (default: `true`) When html5Mode is enabled, enables or
37
- * disables URL rewriting for relative links. If set to a string, URL rewriting will only apply to links
38
- * with an attribute that matches the given string. For example, if set to `'internal-link'`, URL rewriting
39
- * will only occur for `<a internal-link>` links. Note that [attribute name normalization](guide/directive#normalization)
40
- * does not apply here, so `'internalLink'` will **not** match `'internal-link'`.
31
+ * @type {Object.<string,boolean|Array>}
41
32
  */
42
-
43
- /** @type {DefaultPorts} */
44
- const DEFAULT_PORTS = { http: 80, https: 443, ftp: 21 };
45
- const PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/;
46
- const $locationMinErr = minErr("$location");
33
+ let $$search;
47
34
 
48
35
  /**
49
- * @abstract
36
+ * @ignore
37
+ * The hash string, minus the hash symbol
38
+ * @type {string}
50
39
  */
40
+ let $$hash;
41
+
51
42
  export class Location {
52
43
  /**
53
44
  * @param {string} appBase application base URL
54
45
  * @param {string} appBaseNoFile application base URL stripped of any filename
46
+ * @param {boolean} [html5] Defaults to true
47
+ * @param {string} [prefix] URL path prefix for html5 mode or hash prefix for hashbang mode
55
48
  */
56
- constructor(appBase, appBaseNoFile) {
57
- const parsedUrl = urlResolve(appBase);
58
-
49
+ constructor(appBase, appBaseNoFile, html5 = true, prefix) {
59
50
  /** @type {string} */
60
51
  this.appBase = appBase;
61
52
 
62
53
  /** @type {string} */
63
54
  this.appBaseNoFile = appBaseNoFile;
64
55
 
65
- /**
66
- * An absolute URL is the full URL, including protocol (http/https ), the optional subdomain (e.g. www ), domain (example.com), and path (which includes the directory and slug).
67
- * @type {string}
68
- */
69
- this.$$absUrl = "";
56
+ /** @type {boolean} */
57
+ this.html5 = html5;
70
58
 
71
- /**
72
- * If html5 mode is enabled
73
- * @type {boolean}
74
- */
75
- this.$$html5 = false;
59
+ /** @type {string | undefined} */
60
+ this.basePrefix = html5 ? prefix || "" : undefined;
76
61
 
77
- /**
78
- * Has any change been replacing?
79
- * @type {boolean}
80
- */
81
- this.$$replace = false;
82
-
83
- /** @type {string} */
84
- this.$$protocol = parsedUrl.protocol;
85
-
86
- /** @type {string} */
87
- this.$$host = parsedUrl.hostname;
88
-
89
- /**
90
- * The port, without ":"
91
- * @type {number}
92
- */
93
- this.$$port =
94
- toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
62
+ /** @type {string | undefined} */
63
+ this.hashPrefix = html5 ? undefined : prefix;
95
64
 
96
65
  /**
97
- * The pathname, beginning with "/"
66
+ * An absolute URL is the full URL, including protocol (http/https ), the optional subdomain (e.g. www ), domain (example.com), and path (which includes the directory and slug)
67
+ * with all segments encoded according to rules specified in [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
98
68
  * @type {string}
99
69
  */
100
- this.$$path = undefined;
70
+ this.absUrl = "";
101
71
 
102
72
  /**
103
- * The hash string, minus the hash symbol
73
+ * @ignore
74
+ * Current url
104
75
  * @type {string}
105
76
  */
106
- this.$$hash = undefined;
77
+ this.$$url = undefined;
107
78
 
108
79
  /**
109
- * Helper property for scope watch changes
110
- * @type {boolean}
80
+ * @ignore
81
+ * Callback to update browser url
82
+ * @type {Function}
111
83
  */
112
- this.$$urlUpdatedByLocation = false;
84
+ this.$$updateBrowser = undefined;
113
85
  }
114
86
 
115
87
  /**
116
- * Return full URL representation with all segments encoded according to rules specified in
117
- * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
118
- *
119
- * @return {string} full URL
120
- */
121
- absUrl() {
122
- return this.$$absUrl;
123
- }
124
-
125
- /**
126
- * This method is getter / setter.
127
- *
128
- * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
129
88
  * Change path, search and hash, when called with parameter and return `$location`.
130
89
  *
131
- * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`)
132
- * @return {Location|string} url
90
+ * @param {string} url New URL without base prefix (e.g. `/path?a=b#hash`)
91
+ * @return {Location} url
133
92
  */
134
- url(url) {
135
- if (isUndefined(url)) {
136
- return this.$$url;
137
- }
138
-
93
+ setUrl(url) {
139
94
  const match = PATH_MATCH.exec(url);
140
- if (match[1] || url === "") this.path(decodeURIComponent(match[1]));
141
- if (match[2] || match[1] || url === "") this.search(match[3] || "");
142
- this.hash(match[5] || "");
95
+ if (match[1] || url === "") this.setPath(decodeURIComponent(match[1]));
96
+ if (match[2] || match[1] || url === "") this.setSearch(match[3] || "");
97
+ this.setHash(match[5] || "");
143
98
 
144
99
  return this;
145
100
  }
146
101
 
147
102
  /**
103
+ * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
148
104
  *
149
- * Return protocol of current URL.
150
- * @return {string} protocol of current URL
151
- */
152
- protocol() {
153
- return this.$$protocol;
154
- }
155
-
156
- /**
157
- * This method is getter only.
158
- *
159
- * Return host of current URL.
160
- *
161
- * Note: compared to the non-AngularTS version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
162
- *
163
- *
164
- * @return {string} host of current URL.
105
+ * @return {string} url
165
106
  */
166
- host() {
167
- return this.$$host;
107
+ getUrl() {
108
+ return this.$$url;
168
109
  }
169
110
 
170
111
  /**
171
- * This method is getter only.
172
- *
173
- * Return port of current URL.
174
- *
175
- *
176
- * ```js
177
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
178
- * let port = $location.port();
179
- * // => 80
180
- * ```
112
+ * Change path parameter and return `$location`.
181
113
  *
182
- * @return {number} port
114
+ * @param {(string|number)} path New path
115
+ * @return {Location}
183
116
  */
184
- port() {
185
- return this.$$port;
117
+ setPath(path) {
118
+ let newPath = path !== null ? path.toString() : "";
119
+ $$path = newPath.charAt(0) === "/" ? newPath : `/${newPath}`;
120
+ this.$$compose();
121
+ return this;
186
122
  }
187
123
 
188
124
  /**
189
- * This method is getter / setter.
190
- *
191
- * Return path of current URL when called without any parameter.
192
125
  *
193
- * Change path when called with parameter and return `$location`.
126
+ * Return path of current URL
194
127
  *
195
- * Note: Path should always begin with forward slash (/), this method will add the forward slash
196
- * if it is missing.
197
- *
198
- *
199
- * ```js
200
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
201
- * let path = $location.path();
202
- * // => "/some/path"
203
- * ```
204
- *
205
- * @param {(string|number)=} path New path
206
- * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
128
+ * @return {string}
207
129
  */
208
- path(path) {
209
- if (isUndefined(path)) {
210
- return this.$$path;
211
- }
212
- let newPath = path !== null ? path.toString() : "";
213
- this.$$path = newPath.charAt(0) === "/" ? newPath : `/${newPath}`;
214
- this.$$compose();
215
- return this;
130
+ getPath() {
131
+ return $$path;
216
132
  }
217
133
 
218
134
  /**
219
- * This method is getter / setter.
220
- *
221
- * Returns the hash fragment when called without any parameters.
222
- *
223
135
  * Changes the hash fragment when called with a parameter and returns `$location`.
224
- *
225
- *
226
- * ```js
227
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
228
- * let hash = $location.hash();
229
- * // => "hashValue"
230
- * ```
231
- *
232
- * @param {(string|number)=} hash New hash fragment
233
- * @return {string|Location} hash
136
+ * @param {(string|number)} hash New hash fragment
137
+ * @return {Location} hash
234
138
  */
235
- hash(hash) {
236
- if (isUndefined(hash)) {
237
- return this.$$hash;
238
- }
239
-
240
- this.$$hash = hash !== null ? hash.toString() : "";
139
+ setHash(hash) {
140
+ $$hash = hash !== null ? hash.toString() : "";
241
141
  this.$$compose();
242
142
  return this;
243
143
  }
244
144
 
245
145
  /**
246
- * If called, all changes to $location during the current `$digest` will replace the current history
247
- * record, instead of adding a new one.
146
+ * Returns the hash fragment when called without any parameters.
147
+ * @return {string} hash
248
148
  */
249
- replace() {
250
- this.$$replace = true;
251
- return this;
149
+ getHash() {
150
+ return $$hash;
252
151
  }
253
152
 
254
153
  /**
255
- * Returns or sets the search part (as object) of current URL when called without any parameter
154
+ * Sets the search part (as object) of current URL
256
155
  *
257
- * @param {string|Object=} search New search params - string or hash object.
156
+ * @param {string|Object} search New search params - string or hash object.
258
157
  * @param {(string|number|Array<string>|boolean)=} paramValue If search is a string or number, then paramValue will override only a single search property.
259
- * @returns {Object|Location} Search object or Location object
158
+ * @returns {Object} Search object or Location object
260
159
  */
261
- search(search, paramValue) {
160
+ setSearch(search, paramValue) {
262
161
  switch (arguments.length) {
263
- case 0:
264
- return this.$$search;
265
162
  case 1:
266
163
  if (isString(search) || isNumber(search)) {
267
164
  search = search.toString();
268
- this.$$search = parseKeyValue(search);
165
+ $$search = parseKeyValue(search);
269
166
  } else if (isObject(search)) {
270
167
  search = structuredClone(search, {});
271
168
  // remove object undefined or null properties
@@ -273,7 +170,7 @@ export class Location {
273
170
  if (value == null) delete search[key];
274
171
  });
275
172
 
276
- this.$$search = search;
173
+ $$search = search;
277
174
  } else {
278
175
  throw $locationMinErr(
279
176
  "isrcharg",
@@ -283,9 +180,10 @@ export class Location {
283
180
  break;
284
181
  default:
285
182
  if (isUndefined(paramValue) || paramValue === null) {
286
- delete this.$$search[search];
183
+ delete $$search[search];
287
184
  } else {
288
- this.$$search[search] = paramValue;
185
+ // @ts-ignore
186
+ $$search[search] = paramValue;
289
187
  }
290
188
  }
291
189
 
@@ -294,28 +192,28 @@ export class Location {
294
192
  }
295
193
 
296
194
  /**
297
- * Compose url and update `url` and `absUrl` property
298
- * @returns {void}
195
+ * Returns the search part (as object) of current URL
196
+ *
197
+ * @returns {Object} Search object or Location object
299
198
  */
300
- $$compose() {
301
- this.$$url = normalizePath(this.$$path, this.$$search, this.$$hash);
302
- this.$$absUrl = this.$$normalizeUrl(this.$$url);
303
- this.$$urlUpdatedByLocation = true;
199
+ getSearch() {
200
+ return $$search;
304
201
  }
305
202
 
306
203
  /**
307
- * @param {string} _url
308
- * @returns {string}
204
+ * @private
205
+ * Compose url and update `url` and `absUrl` property
309
206
  */
310
- $$normalizeUrl(_url) {
311
- throw new Error(`Method not implemented ${_url}`);
207
+ $$compose() {
208
+ this.$$url = normalizePath($$path, $$search, $$hash);
209
+ this.absUrl = this.html5
210
+ ? this.appBaseNoFile + this.$$url.substring(1)
211
+ : this.appBase + (this.$$url ? this.hashPrefix + this.$$url : "");
212
+ urlUpdatedByLocation = true;
213
+ setTimeout(() => this.$$updateBrowser && this.$$updateBrowser());
312
214
  }
313
215
 
314
216
  /**
315
- * This method is getter / setter.
316
- *
317
- * Return the history state object when called without any parameter.
318
- *
319
217
  * Change the history state object when called with one parameter and return `$location`.
320
218
  * The state object is later passed to `pushState` or `replaceState`.
321
219
  * See {@link https://developer.mozilla.org/en-US/docs/Web/API/History/pushState#state|History.state}
@@ -323,85 +221,30 @@ export class Location {
323
221
  * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
324
222
  * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
325
223
  * older browsers (like IE9 or Android < 4.0), don't use this method.
326
- *
327
- * @param {any} state State object for pushState or replaceState
328
- * @return {any} state
224
+ * @param {any} state
225
+ * @returns {Location}
329
226
  */
330
- state(state) {
331
- if (!arguments.length) {
332
- return this.$$state;
333
- }
334
-
335
- if (!(this instanceof LocationHtml5Url) || !this.$$html5) {
227
+ setState(state) {
228
+ if (!this.html5) {
336
229
  throw $locationMinErr(
337
230
  "nostate",
338
- "History API state support is available only " +
339
- "in HTML5 mode and only in browsers supporting HTML5 History API",
231
+ "History API state support is available only in HTML5 mode",
340
232
  );
341
233
  }
342
- // The user might modify `stateObject` after invoking `$location.state(stateObject)`
234
+ // The user might modify `stateObject` after invoking `$location.setState(stateObject)`
343
235
  // but we're changing the $$state reference to $browser.state() during the $digest
344
236
  // so the modification window is narrow.
345
237
  this.$$state = isUndefined(state) ? null : state;
346
- this.$$urlUpdatedByLocation = true;
238
+ urlUpdatedByLocation = true;
347
239
  return this;
348
240
  }
349
241
 
350
242
  /**
351
- * @param {string} _url
352
- * @param {string} _url2
353
- * @returns {boolean}
354
- */
355
- $$parseLinkUrl(_url, _url2) {
356
- throw new Error(`Method not implemented ${_url} ${_url2}`);
357
- }
358
-
359
- $$parse(_url) {
360
- throw new Error(`Method not implemented ${_url}`);
361
- }
362
- }
363
-
364
- /**
365
- * This object is exposed as $location service when HTML5 mode is enabled and supported
366
- */
367
- export class LocationHtml5Url extends Location {
368
- /**
369
- * @param {string} appBase application base URL
370
- * @param {string} appBaseNoFile application base URL stripped of any filename
371
- * @param {string} basePrefix URL path prefix
372
- */
373
- constructor(appBase, appBaseNoFile, basePrefix) {
374
- super(appBase, appBaseNoFile);
375
- this.$$html5 = true;
376
- this.basePrefix = basePrefix || "";
377
- }
378
-
379
- /**
380
- * Parse given HTML5 (regular) URL string into properties
381
- * @param {string} url HTML5 URL
243
+ * Return the history state object
244
+ * @returns {any}
382
245
  */
383
- $$parse(url) {
384
- const pathUrl = stripBaseUrl(this.appBaseNoFile, url);
385
- if (!isString(pathUrl)) {
386
- throw $locationMinErr(
387
- "ipthprfx",
388
- 'Invalid url "{0}", missing path prefix "{1}".',
389
- url,
390
- this.appBaseNoFile,
391
- );
392
- }
393
-
394
- parseAppUrl(pathUrl, this, true);
395
-
396
- if (!this.$$path) {
397
- this.$$path = "/";
398
- }
399
-
400
- this.$$compose();
401
- }
402
-
403
- $$normalizeUrl(url) {
404
- return this.appBaseNoFile + url.substring(1); // first char is always '/'
246
+ getState() {
247
+ return this.$$state;
405
248
  }
406
249
 
407
250
  /**
@@ -409,149 +252,139 @@ export class LocationHtml5Url extends Location {
409
252
  * @param {string} relHref
410
253
  * @returns {boolean}
411
254
  */
412
- $$parseLinkUrl(url, relHref) {
413
- if (relHref && relHref[0] === "#") {
414
- // special case for links to hash fragments:
415
- // keep the old url and only replace the hash fragment
416
- this.hash(relHref.slice(1));
417
- return true;
418
- }
419
- let appUrl;
420
- let prevAppUrl;
421
- let rewrittenUrl;
422
-
423
- if (isDefined((appUrl = stripBaseUrl(this.appBase, url)))) {
424
- prevAppUrl = appUrl;
425
- if (
426
- this.basePrefix &&
427
- isDefined((appUrl = stripBaseUrl(this.basePrefix, appUrl)))
428
- ) {
429
- rewrittenUrl =
430
- this.appBaseNoFile + (stripBaseUrl("/", appUrl) || appUrl);
431
- } else {
432
- rewrittenUrl = this.appBase + prevAppUrl;
255
+ parseLinkUrl(url, relHref) {
256
+ if (this.html5) {
257
+ if (relHref && relHref[0] === "#") {
258
+ // special case for links to hash fragments:
259
+ // keep the old url and only replace the hash fragment
260
+ this.setHash(relHref.slice(1));
261
+ return true;
433
262
  }
434
- } else if (isDefined((appUrl = stripBaseUrl(this.appBaseNoFile, url)))) {
435
- rewrittenUrl = this.appBaseNoFile + appUrl;
436
- } else if (this.appBaseNoFile === `${url}/`) {
437
- rewrittenUrl = this.appBaseNoFile;
438
- }
439
- if (rewrittenUrl) {
440
- this.$$parse(rewrittenUrl);
263
+ let appUrl;
264
+ let prevAppUrl;
265
+ let rewrittenUrl;
266
+
267
+ if (isDefined((appUrl = stripBaseUrl(this.appBase, url)))) {
268
+ prevAppUrl = appUrl;
269
+ if (
270
+ this.basePrefix &&
271
+ isDefined((appUrl = stripBaseUrl(this.basePrefix, appUrl)))
272
+ ) {
273
+ rewrittenUrl =
274
+ this.appBaseNoFile + (stripBaseUrl("/", appUrl) || appUrl);
275
+ } else {
276
+ rewrittenUrl = this.appBase + prevAppUrl;
277
+ }
278
+ } else if (isDefined((appUrl = stripBaseUrl(this.appBaseNoFile, url)))) {
279
+ rewrittenUrl = this.appBaseNoFile + appUrl;
280
+ } else if (this.appBaseNoFile === `${url}/`) {
281
+ rewrittenUrl = this.appBaseNoFile;
282
+ }
283
+ if (rewrittenUrl) {
284
+ this.parse(rewrittenUrl);
285
+ }
286
+ return !!rewrittenUrl;
287
+ } else {
288
+ if (stripHash(this.appBase) === stripHash(url)) {
289
+ this.parse(url);
290
+ return true;
291
+ }
292
+ return false;
441
293
  }
442
- return !!rewrittenUrl;
443
294
  }
444
- }
445
295
 
446
- /**
447
- * LocationHashbangUrl represents URL
448
- * This object is exposed as $location service when developer doesn't opt into html5 mode.
449
- * It also serves as the base class for html5 mode fallback on legacy browsers.
450
- *
451
- */
452
- export class LocationHashbangUrl extends Location {
453
296
  /**
454
- * @param {string} appBase application base URL
455
- * @param {string} appBaseNoFile application base URL stripped of any filename
456
- * @param {string} hashPrefix hashbang prefix
297
+ * Parse given HTML5 (regular) URL string into properties
298
+ * @param {string} url HTML5 URL
457
299
  */
458
- constructor(appBase, appBaseNoFile, hashPrefix) {
459
- super(appBase, appBaseNoFile);
460
- this.hashPrefix = hashPrefix;
461
- }
300
+ parse(url) {
301
+ if (this.html5) {
302
+ const pathUrl = stripBaseUrl(this.appBaseNoFile, url);
303
+ if (!isString(pathUrl)) {
304
+ throw $locationMinErr(
305
+ "ipthprfx",
306
+ 'Invalid url "{0}", missing path prefix "{1}".',
307
+ url,
308
+ this.appBaseNoFile,
309
+ );
310
+ }
462
311
 
463
- /**
464
- * Parse given hashbang URL into properties
465
- * @param {string} url Hashbang URL
466
- */
467
- $$parse(url) {
468
- const withoutBaseUrl =
469
- stripBaseUrl(this.appBase, url) || stripBaseUrl(this.appBaseNoFile, url);
470
- let withoutHashUrl;
471
-
472
- if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === "#") {
473
- // The rest of the URL starts with a hash so we have
474
- // got either a hashbang path or a plain hash fragment
475
- withoutHashUrl = stripBaseUrl(this.hashPrefix, withoutBaseUrl);
476
- if (isUndefined(withoutHashUrl)) {
477
- // There was no hashbang prefix so we just have a hash fragment
478
- withoutHashUrl = withoutBaseUrl;
312
+ parseAppUrl(pathUrl, true);
313
+
314
+ if (!$$path) {
315
+ $$path = "/";
479
316
  }
317
+
318
+ this.$$compose();
480
319
  } else {
481
- // There was no hashbang path nor hash fragment:
482
- // If we are in HTML5 mode we use what is left as the path;
483
- // Otherwise we ignore what is left
484
- if (this.$$html5) {
485
- withoutHashUrl = withoutBaseUrl;
320
+ const withoutBaseUrl =
321
+ stripBaseUrl(this.appBase, url) ||
322
+ stripBaseUrl(this.appBaseNoFile, url);
323
+ let withoutHashUrl;
324
+
325
+ if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === "#") {
326
+ // The rest of the URL starts with a hash so we have
327
+ // got either a hashbang path or a plain hash fragment
328
+ withoutHashUrl = stripBaseUrl(this.hashPrefix, withoutBaseUrl);
329
+ if (isUndefined(withoutHashUrl)) {
330
+ // There was no hashbang prefix so we just have a hash fragment
331
+ withoutHashUrl = withoutBaseUrl;
332
+ }
486
333
  } else {
487
- withoutHashUrl = "";
488
- if (isUndefined(withoutBaseUrl)) {
489
- this.appBase = url;
490
- /** @type {?} */ (this).replace();
334
+ // There was no hashbang path nor hash fragment:
335
+ // If we are in HTML5 mode we use what is left as the path;
336
+ // Otherwise we ignore what is left
337
+ if (this.html5) {
338
+ withoutHashUrl = withoutBaseUrl;
339
+ } else {
340
+ withoutHashUrl = "";
341
+ if (isUndefined(withoutBaseUrl)) {
342
+ this.appBase = url;
343
+ }
491
344
  }
492
345
  }
493
- }
494
346
 
495
- parseAppUrl(withoutHashUrl, this, false);
347
+ parseAppUrl(withoutHashUrl, false);
496
348
 
497
- this.$$path = removeWindowsDriveName(
498
- this.$$path,
499
- withoutHashUrl,
500
- this.appBase,
501
- );
349
+ $$path = removeWindowsDriveName($$path, withoutHashUrl, this.appBase);
502
350
 
503
- this.$$compose();
351
+ this.$$compose();
504
352
 
505
- /*
506
- * In Windows, on an anchor node on documents loaded from
507
- * the filesystem, the browser will return a pathname
508
- * prefixed with the drive name ('/C:/path') when a
509
- * pathname without a drive is set:
510
- * * a.setAttribute('href', '/foo')
511
- * * a.pathname === '/C:/foo' //true
512
- *
513
- * Inside of AngularTS, we're always using pathnames that
514
- * do not include drive names for routing.
515
- */
516
- function removeWindowsDriveName(path, url, base) {
517
353
  /*
518
- Matches paths for file protocol on windows,
519
- such as /C:/foo/bar, and captures only /foo/bar.
520
- */
521
- const windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
522
-
523
- let firstPathSegmentMatch;
354
+ * In Windows, on an anchor node on documents loaded from
355
+ * the filesystem, the browser will return a pathname
356
+ * prefixed with the drive name ('/C:/path') when a
357
+ * pathname without a drive is set:
358
+ * * a.setAttribute('href', '/foo')
359
+ * * a.pathname === '/C:/foo' //true
360
+ *
361
+ * Inside of AngularTS, we're always using pathnames that
362
+ * do not include drive names for routing.
363
+ */
364
+ function removeWindowsDriveName(path, url, base) {
365
+ /*
366
+ Matches paths for file protocol on windows,
367
+ such as /C:/foo/bar, and captures only /foo/bar.
368
+ */
369
+ const windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
370
+
371
+ let firstPathSegmentMatch;
372
+
373
+ // Get the relative path from the input URL.
374
+ if (startsWith(url, base)) {
375
+ url = url.replace(base, "");
376
+ }
524
377
 
525
- // Get the relative path from the input URL.
526
- if (startsWith(url, base)) {
527
- url = url.replace(base, "");
528
- }
378
+ // The input URL intentionally contains a first path segment that ends with a colon.
379
+ if (windowsFilePathExp.exec(url)) {
380
+ return path;
381
+ }
529
382
 
530
- // The input URL intentionally contains a first path segment that ends with a colon.
531
- if (windowsFilePathExp.exec(url)) {
532
- return path;
383
+ firstPathSegmentMatch = windowsFilePathExp.exec(path);
384
+ return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
533
385
  }
534
-
535
- firstPathSegmentMatch = windowsFilePathExp.exec(path);
536
- return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
537
386
  }
538
387
  }
539
-
540
- $$normalizeUrl(url) {
541
- return this.appBase + (url ? this.hashPrefix + url : "");
542
- }
543
-
544
- /**
545
- * @param {string} url
546
- * @returns {boolean}
547
- */
548
- $$parseLinkUrl(url) {
549
- if (stripHash(this.appBase) === stripHash(url)) {
550
- this.$$parse(url);
551
- return true;
552
- }
553
- return false;
554
- }
555
388
  }
556
389
 
557
390
  export class LocationProvider {
@@ -559,20 +392,20 @@ export class LocationProvider {
559
392
  /** @type {string} */
560
393
  this.hashPrefixConf = "!";
561
394
 
562
- /** @type {Html5Mode} */
395
+ /** @type {import("./interface.ts").Html5Mode} */
563
396
  this.html5ModeConf = {
564
- enabled: false,
565
- requireBase: true,
397
+ enabled: true,
398
+ requireBase: false,
566
399
  rewriteLinks: true,
567
400
  };
568
401
 
569
- /** @type {Array<import("./interface.js").UrlChangeListener>} */
402
+ /** @type {Array<import("./interface.ts").UrlChangeListener>} */
570
403
  this.urlChangeListeners = [];
571
404
  this.urlChangeInit = false;
572
405
 
573
406
  /** @type {History['state']} */
574
407
  this.cachedState = null;
575
- /** @typeof {History.state} */
408
+ /** @type {History['state']} */
576
409
  this.lastHistoryState = null;
577
410
  /** @type {string} */
578
411
  this.lastBrowserUrl = window.location.href;
@@ -583,14 +416,19 @@ export class LocationProvider {
583
416
  // URL API
584
417
  /// ///////////////////////////////////////////////////////////
585
418
 
419
+ /**
420
+ * Updates the browser's current URL and history state.
421
+ *
422
+ * @param {string|undefined} url - The target URL to navigate to.
423
+ * @param {*} [state=null] - Optional history state object to associate with the new URL.
424
+ * @returns {LocationProvider}
425
+ */
586
426
  setUrl(url, state) {
587
427
  if (state === undefined) {
588
428
  state = null;
589
429
  }
590
-
591
- // setter
592
430
  if (url) {
593
- url = urlResolve(url).href;
431
+ url = new URL(url).href;
594
432
 
595
433
  if (this.lastBrowserUrl === url && this.lastHistoryState === state) {
596
434
  return this;
@@ -607,7 +445,7 @@ export class LocationProvider {
607
445
  * Returns the current URL with any empty hash (`#`) removed.
608
446
  * @return {string}
609
447
  */
610
- getUrl() {
448
+ getBrowserUrl() {
611
449
  return trimEmptyHash(window.location.href);
612
450
  }
613
451
 
@@ -635,19 +473,17 @@ export class LocationProvider {
635
473
 
636
474
  /**
637
475
  * Fires the state or URL change event.
638
- *
639
- * @private
640
476
  */
641
- fireStateOrUrlChange() {
477
+ #fireStateOrUrlChange() {
642
478
  const prevLastHistoryState = this.lastHistoryState;
643
479
  this.cacheState();
644
480
  if (
645
- this.lastBrowserUrl === this.getUrl() &&
481
+ this.lastBrowserUrl === this.getBrowserUrl() &&
646
482
  prevLastHistoryState === this.cachedState
647
483
  ) {
648
484
  return;
649
485
  }
650
- this.lastBrowserUrl = this.getUrl();
486
+ this.lastBrowserUrl = this.getBrowserUrl();
651
487
  this.lastHistoryState = this.cachedState;
652
488
  this.urlChangeListeners.forEach((listener) => {
653
489
  listener(trimEmptyHash(window.location.href), this.cachedState);
@@ -660,131 +496,75 @@ export class LocationProvider {
660
496
  * @param {import("./interface.js").UrlChangeListener} callback - The callback function to register.
661
497
  * @returns void
662
498
  */
663
- onUrlChange(callback) {
499
+ #onUrlChange(callback) {
664
500
  if (!this.urlChangeInit) {
665
- window.addEventListener("popstate", this.fireStateOrUrlChange.bind(this));
501
+ window.addEventListener(
502
+ "popstate",
503
+ this.#fireStateOrUrlChange.bind(this),
504
+ );
666
505
  window.addEventListener(
667
506
  "hashchange",
668
- this.fireStateOrUrlChange.bind(this),
507
+ this.#fireStateOrUrlChange.bind(this),
669
508
  );
670
509
  this.urlChangeInit = true;
671
510
  }
672
511
  this.urlChangeListeners.push(callback);
673
512
  }
674
513
 
675
- /**
676
- * The default value for the prefix is `'!'`.
677
- * @param {string=} prefix Prefix for hash part (containing path and search)
678
- * @returns {void}
679
- */
680
- setHashPrefix(prefix) {
681
- this.hashPrefixConf = prefix;
682
- }
683
-
684
- /**
685
- * Current hash prefix
686
- * @returns {string}
687
- */
688
- getHashPrefix() {
689
- return this.hashPrefixConf;
690
- }
691
-
692
- /**
693
- * Configures html5 mode
694
- * @param {(boolean|Html5Mode)=} mode If boolean, sets `html5Mode.enabled` to value. Otherwise, accepts html5Mode object
695
- *
696
- * @returns {void}
697
- */
698
- setHtml5Mode(mode) {
699
- if (isBoolean(mode)) {
700
- this.html5ModeConf.enabled = /** @type {boolean} */ (mode);
701
- }
702
- if (isObject(mode)) {
703
- const html5Mode = /** @type {Html5Mode} */ (mode);
704
- if (isDefined(html5Mode.enabled) && isBoolean(html5Mode.enabled)) {
705
- this.html5ModeConf.enabled = html5Mode.enabled;
706
- }
707
-
708
- if (
709
- isDefined(html5Mode.requireBase) &&
710
- isBoolean(html5Mode.requireBase)
711
- ) {
712
- this.html5ModeConf.requireBase = html5Mode.requireBase;
713
- }
714
-
715
- if (
716
- isDefined(html5Mode.rewriteLinks) &&
717
- (isBoolean(html5Mode.rewriteLinks) || isString(html5Mode.rewriteLinks))
718
- ) {
719
- this.html5ModeConf.rewriteLinks = html5Mode.rewriteLinks;
720
- }
721
- }
722
- }
723
-
724
- /**
725
- * Returns html5 mode cofiguration
726
- * @returns {Html5Mode}
727
- */
728
- getHtml5Mode() {
729
- return this.html5ModeConf;
730
- }
731
-
732
514
  $get = [
733
- "$rootScope",
734
- "$rootElement",
515
+ $t.$rootScope,
516
+ $t.$rootElement,
735
517
  /**
736
518
  *
737
519
  * @param {import('../../core/scope/scope.js').Scope} $rootScope
738
520
  * @param {Element} $rootElement
739
- * @returns
521
+ * @returns {Location}
740
522
  */
741
523
  ($rootScope, $rootElement) => {
742
524
  /** @type {Location} */
743
525
  let $location;
744
- let LocationMode;
745
526
  const baseHref = getBaseHref(); // if base[href] is undefined, it defaults to ''
746
527
  const initialUrl = trimEmptyHash(window.location.href);
747
528
  let appBase;
748
529
 
749
- if (this.getHtml5Mode().enabled) {
750
- if (!baseHref && this.getHtml5Mode().requireBase) {
530
+ if (this.html5ModeConf.enabled) {
531
+ if (!baseHref && this.html5ModeConf.requireBase) {
751
532
  throw $locationMinErr(
752
533
  "nobase",
753
534
  "$location in HTML5 mode requires a <base> tag to be present!",
754
535
  );
755
536
  }
756
537
  appBase = serverBase(initialUrl) + (baseHref || "/");
757
- LocationMode = LocationHtml5Url;
758
538
  } else {
759
539
  appBase = stripHash(initialUrl);
760
- LocationMode = LocationHashbangUrl;
761
540
  }
762
541
  const appBaseNoFile = stripFile(appBase);
763
542
 
764
- $location = new LocationMode(
543
+ $location = new Location(
765
544
  appBase,
766
545
  appBaseNoFile,
767
- `#${this.getHashPrefix()}`,
546
+ this.html5ModeConf.enabled,
547
+ `#${this.hashPrefixConf}`,
768
548
  );
769
- $location.$$parseLinkUrl(initialUrl, initialUrl);
549
+ $location.parseLinkUrl(initialUrl, initialUrl);
770
550
 
771
551
  $location.$$state = this.state();
772
552
 
773
553
  const IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
774
554
 
775
555
  const setBrowserUrlWithFallback = (url, state) => {
776
- const oldUrl = $location.url();
556
+ const oldUrl = $location.getUrl();
777
557
  const oldState = $location.$$state;
778
558
  try {
779
559
  this.setUrl(url, state);
780
560
 
781
- // Make sure $location.state() returns referentially identical (not just deeply equal)
561
+ // Make sure $location.getState() returns referentially identical (not just deeply equal)
782
562
  // state object; this makes possible quick checking if the state changed in the digest
783
563
  // loop. Checking deep equality would be too expensive.
784
564
  $location.$$state = this.state();
785
565
  } catch (e) {
786
566
  // Restore old values if pushState fails
787
- $location.url(/** @type {string} */ (oldUrl));
567
+ $location.setUrl(/** @type {string} */ (oldUrl));
788
568
  $location.$$state = oldState;
789
569
 
790
570
  throw e;
@@ -795,7 +575,7 @@ export class LocationProvider {
795
575
  "click",
796
576
  /** @param {MouseEvent} event */
797
577
  (event) => {
798
- const rewriteLinks = this.getHtml5Mode().rewriteLinks;
578
+ const rewriteLinks = this.html5ModeConf.rewriteLinks;
799
579
  // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
800
580
  // currently we open nice url link and redirect then
801
581
 
@@ -804,7 +584,6 @@ export class LocationProvider {
804
584
  event.ctrlKey ||
805
585
  event.metaKey ||
806
586
  event.shiftKey ||
807
- event.which === 2 ||
808
587
  event.button === 2
809
588
  ) {
810
589
  return;
@@ -839,7 +618,7 @@ export class LocationProvider {
839
618
  // an animation.
840
619
 
841
620
  const scvAnimatedString = /** @type {unknown} */ (absHref);
842
- absHref = urlResolve(
621
+ absHref = new URL(
843
622
  /** @type {SVGAnimatedString } */ (scvAnimatedString).animVal,
844
623
  ).href;
845
624
  }
@@ -852,7 +631,7 @@ export class LocationProvider {
852
631
  !elm.getAttribute("target") &&
853
632
  !event.defaultPrevented
854
633
  ) {
855
- if ($location.$$parseLinkUrl(absHref, relHref)) {
634
+ if ($location.parseLinkUrl(absHref, relHref)) {
856
635
  // We do a preventDefault for all urls that are part of the AngularTS application,
857
636
  // in html5mode and also without, so that we are able to abort navigation without
858
637
  // getting double entries in the location history.
@@ -863,14 +642,14 @@ export class LocationProvider {
863
642
  );
864
643
 
865
644
  // rewrite hashbang url <> html5 url
866
- if ($location.absUrl() !== initialUrl) {
867
- this.setUrl($location.absUrl(), true);
645
+ if ($location.absUrl !== initialUrl) {
646
+ this.setUrl($location.absUrl, true);
868
647
  }
869
648
 
870
649
  let initializing = true;
871
650
 
872
651
  // update $location when $browser url changes
873
- this.onUrlChange((newUrl, newState) => {
652
+ this.#onUrlChange((newUrl, newState) => {
874
653
  if (!startsWith(newUrl, appBaseNoFile)) {
875
654
  // If we are navigating outside of the app then force a reload
876
655
  window.location.href = newUrl;
@@ -878,10 +657,10 @@ export class LocationProvider {
878
657
  }
879
658
 
880
659
  Promise.resolve().then(() => {
881
- const oldUrl = $location.absUrl();
660
+ const oldUrl = $location.absUrl;
882
661
  const oldState = $location.$$state;
883
662
  let defaultPrevented;
884
- $location.$$parse(newUrl);
663
+ $location.parse(newUrl);
885
664
  $location.$$state = newState;
886
665
 
887
666
  defaultPrevented = $rootScope.$broadcast(
@@ -894,10 +673,10 @@ export class LocationProvider {
894
673
 
895
674
  // if the location was changed by a `$locationChangeStart` handler then stop
896
675
  // processing this location change
897
- if ($location.absUrl() !== newUrl) return;
676
+ if ($location.absUrl !== newUrl) return;
898
677
 
899
678
  if (defaultPrevented) {
900
- $location.$$parse(oldUrl);
679
+ $location.parse(oldUrl);
901
680
  $location.$$state = oldState;
902
681
  setBrowserUrlWithFallback(oldUrl, oldState);
903
682
  } else {
@@ -909,21 +688,21 @@ export class LocationProvider {
909
688
 
910
689
  // update browser
911
690
  const updateBrowser = () => {
912
- if (initializing || $location.$$urlUpdatedByLocation) {
913
- $location.$$urlUpdatedByLocation = false;
691
+ if (initializing || urlUpdatedByLocation) {
692
+ urlUpdatedByLocation = false;
914
693
 
915
- const oldUrl = /** @type {string} */ (this.getUrl());
916
- const newUrl = $location.absUrl();
694
+ const oldUrl = /** @type {string} */ (this.getBrowserUrl());
695
+ const newUrl = $location.absUrl;
917
696
  const oldState = this.state();
918
697
  const urlOrStateChanged =
919
698
  !urlsEqual(oldUrl, newUrl) ||
920
- ($location.$$html5 && oldState !== $location.$$state);
699
+ ($location.html5 && oldState !== $location.$$state);
921
700
 
922
701
  if (initializing || urlOrStateChanged) {
923
702
  initializing = false;
924
703
 
925
704
  setTimeout(() => {
926
- const newUrl = $location.absUrl();
705
+ const newUrl = $location.absUrl;
927
706
  const { defaultPrevented } = $rootScope.$broadcast(
928
707
  "$locationChangeStart",
929
708
  newUrl,
@@ -934,10 +713,10 @@ export class LocationProvider {
934
713
 
935
714
  // if the location was changed by a `$locationChangeStart` handler then stop
936
715
  // processing this location change
937
- if ($location.absUrl() !== newUrl) return;
716
+ if ($location.absUrl !== newUrl) return;
938
717
 
939
718
  if (defaultPrevented) {
940
- $location.$$parse(oldUrl);
719
+ $location.parse(oldUrl);
941
720
  $location.$$state = oldState;
942
721
  } else {
943
722
  if (urlOrStateChanged) {
@@ -951,13 +730,8 @@ export class LocationProvider {
951
730
  });
952
731
  }
953
732
  }
954
-
955
- $location.$$replace = false;
956
-
957
- // we don't need to return anything because $evalAsync will make the digest loop dirty when
958
- // there is a change
959
733
  };
960
-
734
+ $location.$$updateBrowser = updateBrowser;
961
735
  updateBrowser();
962
736
  $rootScope.$on("$updateBrowser", updateBrowser);
963
737
 
@@ -966,7 +740,7 @@ export class LocationProvider {
966
740
  function afterLocationChange(oldUrl, oldState) {
967
741
  $rootScope.$broadcast(
968
742
  "$locationChangeSuccess",
969
- $location.absUrl(),
743
+ $location.absUrl,
970
744
  oldUrl,
971
745
  $location.$$state,
972
746
  oldState,
@@ -978,29 +752,64 @@ export class LocationProvider {
978
752
 
979
753
  /**
980
754
  * ///////////////////////////
981
- * HELPERS
755
+ * PRIVATE HELPERS
982
756
  * ///////////////////////////
983
757
  */
984
758
 
985
759
  /**
986
- * Encode path using encodeUriSegment, ignoring forward slashes
760
+ * @ignore
761
+ * Encodes a URL path by encoding each path segment individually using `encodeUriSegment`,
762
+ * while preserving forward slashes (`/`) as segment separators.
987
763
  *
988
- * @param {string} path Path to encode
989
- * @returns {string}
764
+ * This function first decodes any existing percent-encodings (such as `%20` or `%2F`)
765
+ * in each segment to prevent double encoding, except for encoded forward slashes (`%2F`),
766
+ * which are replaced with literal slashes before decoding to keep path boundaries intact.
767
+ *
768
+ * After decoding, each segment is re-encoded with `encodeUriSegment` according to RFC 3986,
769
+ * encoding only characters that must be encoded in a path segment.
770
+ *
771
+ * The encoded segments are then rejoined with `/` to form the encoded path.
772
+ *
773
+ * @param {string} path - The URL path string to encode. May contain multiple segments separated by `/`.
774
+ * @returns {string} The encoded path, where each segment is encoded, but forward slashes are preserved.
775
+ *
776
+ * @example
777
+ * encodePath("user profile/images/pic 1.jpg")
778
+ * // returns "user%20profile/images/pic%201.jpg"
779
+ *
780
+ * @example
781
+ * encodePath("folder1%2Fsub/folder2")
782
+ * // returns "folder1%2Fsub/folder2"
990
783
  */
991
- function encodePath(path) {
784
+ export function encodePath(path) {
992
785
  const segments = path.split("/");
993
786
  let i = segments.length;
994
787
 
995
788
  while (i--) {
996
- // decode forward slashes to prevent them from being double encoded
997
- segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, "/"));
789
+ // Decode any existing encodings (e.g. %20, %2F) to prevent double-encoding
790
+ // But keep slashes intact (they were split on)
791
+ const decodedSegment = decodeURIComponent(
792
+ segments[i].replace(/%2F/gi, "/"),
793
+ );
794
+ segments[i] = encodeUriSegment(decodedSegment);
998
795
  }
999
796
 
1000
797
  return segments.join("/");
1001
798
  }
1002
799
 
1003
- function decodePath(path, html5Mode) {
800
+ /**
801
+ * @ignore
802
+ * Decodes each segment of a URL path.
803
+ *
804
+ * Splits the input path by "/", decodes each segment using decodeURIComponent,
805
+ * and if html5Mode is enabled, re-encodes any forward slashes inside segments
806
+ * as "%2F" to avoid confusion with path separators.
807
+ *
808
+ * @param {string} path - The URL path to decode.
809
+ * @param {boolean} html5Mode - If true, encodes forward slashes in segments as "%2F".
810
+ * @returns {string} The decoded path with segments optionally encoding slashes.
811
+ */
812
+ export function decodePath(path, html5Mode) {
1004
813
  const segments = path.split("/");
1005
814
  let i = segments.length;
1006
815
 
@@ -1015,7 +824,34 @@ function decodePath(path, html5Mode) {
1015
824
  return segments.join("/");
1016
825
  }
1017
826
 
1018
- function normalizePath(pathValue, searchValue, hashValue) {
827
+ /**
828
+ * @ignore
829
+ * Normalizes a URL path by encoding the path segments, query parameters, and hash fragment.
830
+ *
831
+ * - Path segments are encoded using `encodePath`, which encodes each segment individually.
832
+ * - Query parameters (`searchValue`) are converted to a query string using `toKeyValue`.
833
+ * - Hash fragment (`hashValue`) is encoded using `encodeUriSegment` and prefixed with `#`.
834
+ *
835
+ * This function returns a fully constructed URL path with optional query and hash components.
836
+ *
837
+ * @param {string} pathValue - The base URL path (e.g., "folder/item name").
838
+ * @param {Object.<string, any> | string | null} searchValue - An object or string representing query parameters.
839
+ * - If an object, it can contain strings, numbers, booleans, or arrays of values.
840
+ * - If a string, it is assumed to be a raw query string.
841
+ * - If null or undefined, no query string is added.
842
+ * @param {string | null} hashValue - The URL fragment (everything after `#`). If null or undefined, no hash is added.
843
+ *
844
+ * @returns {string} The normalized URL path including encoded path, optional query string, and optional hash.
845
+ *
846
+ * @example
847
+ * normalizePath("products/list", { category: "books", page: 2 }, "section1")
848
+ * // returns "products/list?category=books&page=2#section1"
849
+ *
850
+ * @example
851
+ * normalizePath("user profile/images", null, null)
852
+ * // returns "user%20profile/images"
853
+ */
854
+ export function normalizePath(pathValue, searchValue, hashValue) {
1019
855
  const search = toKeyValue(searchValue);
1020
856
  const hash = hashValue ? `#${encodeUriSegment(hashValue)}` : "";
1021
857
  const path = encodePath(pathValue);
@@ -1023,7 +859,15 @@ function normalizePath(pathValue, searchValue, hashValue) {
1023
859
  return path + (search ? `?${search}` : "") + hash;
1024
860
  }
1025
861
 
1026
- function parseAppUrl(url, locationObj, html5Mode) {
862
+ /**
863
+ * @ignore
864
+ * Parses the application URL and updates the location object with path, search, and hash.
865
+ *
866
+ * @param {string} url - The URL string to parse.
867
+ * @param {boolean} html5Mode - Whether HTML5 mode is enabled (affects decoding).
868
+ * @throws Will throw an error if the URL starts with invalid slashes.
869
+ */
870
+ export function parseAppUrl(url, html5Mode) {
1027
871
  if (/^\s*[\\/]{2,}/.test(url)) {
1028
872
  throw $locationMinErr("badpath", 'Invalid url "{0}".', url);
1029
873
  }
@@ -1037,22 +881,20 @@ function parseAppUrl(url, locationObj, html5Mode) {
1037
881
  prefixed && match.pathname.charAt(0) === "/"
1038
882
  ? match.pathname.substring(1)
1039
883
  : match.pathname;
1040
- locationObj.$$path = decodePath(path, html5Mode);
1041
- locationObj.$$search = parseKeyValue(match.search);
1042
- locationObj.$$hash = decodeURIComponent(match.hash);
884
+ $$path = decodePath(path, html5Mode);
885
+ $$search = parseKeyValue(match.search);
886
+ $$hash = decodeURIComponent(match.hash);
1043
887
 
1044
888
  // make sure path starts with '/';
1045
- if (locationObj.$$path && locationObj.$$path.charAt(0) !== "/") {
1046
- locationObj.$$path = `/${locationObj.$$path}`;
889
+ if ($$path && $$path.charAt(0) !== "/") {
890
+ $$path = `/${$$path}`;
1047
891
  }
1048
892
  }
1049
893
 
1050
- function startsWith(str, search) {
1051
- return str.slice(0, search.length) === search;
1052
- }
1053
-
1054
894
  /**
1055
- *
895
+ * @ignore
896
+ * Returns the substring of `url` after the `base` string if `url` starts with `base`.
897
+ * Returns `undefined` if `url` does not start with `base`.
1056
898
  * @param {string} base
1057
899
  * @param {string} url
1058
900
  * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
@@ -1064,23 +906,94 @@ export function stripBaseUrl(base, url) {
1064
906
  }
1065
907
  }
1066
908
 
909
+ /**
910
+ * @ignore
911
+ * Removes the hash fragment (including the '#') from the given URL string.
912
+ *
913
+ * @param {string} url - The URL string to process.
914
+ * @returns {string} The URL without the hash fragment.
915
+ */
1067
916
  export function stripHash(url) {
1068
917
  const index = url.indexOf("#");
1069
918
  return index === -1 ? url : url.substring(0, index);
1070
919
  }
1071
920
 
921
+ /**
922
+ * @ignore
923
+ * Removes the file name (and any hash) from a URL, returning the base directory path.
924
+ *
925
+ * For example:
926
+ * - Input: "https://example.com/path/to/file.js"
927
+ * Output: "https://example.com/path/to/"
928
+ *
929
+ * - Input: "https://example.com/path/to/file.js#section"
930
+ * Output: "https://example.com/path/to/"
931
+ *
932
+ * @param {string} url - The URL from which to strip the file name and hash.
933
+ * @returns {string} The base path of the URL, ending with a slash.
934
+ */
1072
935
  export function stripFile(url) {
1073
936
  return url.substring(0, stripHash(url).lastIndexOf("/") + 1);
1074
937
  }
1075
938
 
1076
- /* return the server only (scheme://host:port) */
939
+ /**
940
+ * @ignore
941
+ * Extracts the base server URL (scheme, host, and optional port) from a full URL.
942
+ *
943
+ * If no path is present, returns the full URL.
944
+ *
945
+ * For example:
946
+ * - Input: "https://example.com/path/to/file"
947
+ * Output: "https://example.com"
948
+ *
949
+ * - Input: "http://localhost:3000/api/data"
950
+ * Output: "http://localhost:3000"
951
+ *
952
+ * @param {string} url - The full URL to extract the server base from.
953
+ * @returns {string} The server base, including scheme and host (and port if present).
954
+ */
1077
955
  export function serverBase(url) {
1078
- return url.substring(0, url.indexOf("/", url.indexOf("//") + 2));
956
+ const start = url.indexOf("//") + 2;
957
+ const slashIndex = url.indexOf("/", start);
958
+ return slashIndex === -1 ? url : url.substring(0, slashIndex);
959
+ }
960
+
961
+ /**
962
+ * @ignore
963
+ * Determine if two URLs are equal despite potential differences in encoding,
964
+ * trailing slashes, or empty hash fragments, such as between $location.absUrl() and $browser.url().
965
+ *
966
+ * @param {string} a - First URL to compare.
967
+ * @param {string} b - Second URL to compare.
968
+ * @returns {boolean} True if URLs are equivalent after normalization.
969
+ */
970
+ export function urlsEqual(a, b) {
971
+ return normalizeUrl(a) === normalizeUrl(b);
1079
972
  }
1080
973
 
1081
- // Determine if two URLs are equal despite potentially having different encoding/normalizing
1082
- // such as $location.absUrl() vs $browser.url()
1083
- // See https://github.com/angular/angular.js/issues/16592
1084
- function urlsEqual(a, b) {
1085
- return a === b || urlResolve(a).href === urlResolve(b).href;
974
+ /**
975
+ * @ignore
976
+ * Normalize a URL by resolving it via a DOM anchor element,
977
+ * removing trailing slashes (except root), and trimming empty hashes.
978
+ *
979
+ * @param {string} url - URL to normalize.
980
+ * @returns {string} Normalized URL string.
981
+ */
982
+ function normalizeUrl(url) {
983
+ const anchor = document.createElement("a");
984
+ anchor.href = url;
985
+
986
+ let normalized = anchor.href;
987
+
988
+ // Remove trailing slash unless it's root (e.g., https://example.com/)
989
+ if (normalized.endsWith("/") && !/^https?:\/\/[^/]+\/$/.test(normalized)) {
990
+ normalized = normalized.slice(0, -1);
991
+ }
992
+
993
+ // Remove empty hash (e.g., https://example.com/foo# -> https://example.com/foo)
994
+ if (normalized.endsWith("#")) {
995
+ normalized = normalized.slice(0, -1);
996
+ }
997
+
998
+ return normalized;
1086
999
  }