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