@angular-wave/angular.ts 0.0.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.
Files changed (231) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.cjs +29 -0
  3. package/.github/workflows/playwright.yml +27 -0
  4. package/CHANGELOG.md +17974 -0
  5. package/CODE_OF_CONDUCT.md +3 -0
  6. package/CONTRIBUTING.md +246 -0
  7. package/DEVELOPERS.md +488 -0
  8. package/LICENSE +22 -0
  9. package/Makefile +31 -0
  10. package/README.md +115 -0
  11. package/RELEASE.md +98 -0
  12. package/SECURITY.md +16 -0
  13. package/TRIAGING.md +135 -0
  14. package/css/angular.css +22 -0
  15. package/dist/angular-ts.cjs.js +36843 -0
  16. package/dist/angular-ts.esm.js +36841 -0
  17. package/dist/angular-ts.umd.js +36848 -0
  18. package/dist/build/angular-animate.js +4272 -0
  19. package/dist/build/angular-aria.js +426 -0
  20. package/dist/build/angular-message-format.js +1072 -0
  21. package/dist/build/angular-messages.js +829 -0
  22. package/dist/build/angular-mocks.js +3757 -0
  23. package/dist/build/angular-parse-ext.js +1275 -0
  24. package/dist/build/angular-resource.js +911 -0
  25. package/dist/build/angular-route.js +1266 -0
  26. package/dist/build/angular-sanitize.js +891 -0
  27. package/dist/build/angular-touch.js +368 -0
  28. package/dist/build/angular.js +36600 -0
  29. package/e2e/unit.spec.ts +15 -0
  30. package/images/android-chrome-192x192.png +0 -0
  31. package/images/android-chrome-512x512.png +0 -0
  32. package/images/apple-touch-icon.png +0 -0
  33. package/images/favicon-16x16.png +0 -0
  34. package/images/favicon-32x32.png +0 -0
  35. package/images/favicon.ico +0 -0
  36. package/images/site.webmanifest +1 -0
  37. package/index.html +104 -0
  38. package/package.json +47 -0
  39. package/playwright.config.ts +78 -0
  40. package/public/circle.html +1 -0
  41. package/public/my_child_directive.html +1 -0
  42. package/public/my_directive.html +1 -0
  43. package/public/my_other_directive.html +1 -0
  44. package/public/test.html +1 -0
  45. package/rollup.config.js +31 -0
  46. package/src/animations/animateCache.js +55 -0
  47. package/src/animations/animateChildrenDirective.js +105 -0
  48. package/src/animations/animateCss.js +1139 -0
  49. package/src/animations/animateCssDriver.js +291 -0
  50. package/src/animations/animateJs.js +367 -0
  51. package/src/animations/animateJsDriver.js +67 -0
  52. package/src/animations/animateQueue.js +851 -0
  53. package/src/animations/animation.js +506 -0
  54. package/src/animations/module.js +779 -0
  55. package/src/animations/ngAnimateSwap.js +119 -0
  56. package/src/animations/rafScheduler.js +50 -0
  57. package/src/animations/shared.js +378 -0
  58. package/src/constants.js +20 -0
  59. package/src/core/animate.js +845 -0
  60. package/src/core/animateCss.js +73 -0
  61. package/src/core/animateRunner.js +195 -0
  62. package/src/core/attributes.js +199 -0
  63. package/src/core/cache.js +45 -0
  64. package/src/core/compile.js +4727 -0
  65. package/src/core/controller.js +225 -0
  66. package/src/core/exceptionHandler.js +63 -0
  67. package/src/core/filter.js +146 -0
  68. package/src/core/interpolate.js +442 -0
  69. package/src/core/interval.js +188 -0
  70. package/src/core/intervalFactory.js +57 -0
  71. package/src/core/location.js +1086 -0
  72. package/src/core/parser/parse.js +2562 -0
  73. package/src/core/parser/parse.md +13 -0
  74. package/src/core/q.js +746 -0
  75. package/src/core/rootScope.js +1596 -0
  76. package/src/core/sanitizeUri.js +85 -0
  77. package/src/core/sce.js +1161 -0
  78. package/src/core/taskTrackerFactory.js +125 -0
  79. package/src/core/timeout.js +121 -0
  80. package/src/core/urlUtils.js +187 -0
  81. package/src/core/utils.js +1349 -0
  82. package/src/directive/a.js +37 -0
  83. package/src/directive/attrs.js +283 -0
  84. package/src/directive/bind.js +51 -0
  85. package/src/directive/bind.md +142 -0
  86. package/src/directive/change.js +12 -0
  87. package/src/directive/change.md +25 -0
  88. package/src/directive/cloak.js +12 -0
  89. package/src/directive/cloak.md +24 -0
  90. package/src/directive/events.js +75 -0
  91. package/src/directive/events.md +166 -0
  92. package/src/directive/form.js +725 -0
  93. package/src/directive/init.js +15 -0
  94. package/src/directive/init.md +41 -0
  95. package/src/directive/input.js +1783 -0
  96. package/src/directive/list.js +46 -0
  97. package/src/directive/list.md +22 -0
  98. package/src/directive/ngClass.js +249 -0
  99. package/src/directive/ngController.js +64 -0
  100. package/src/directive/ngCsp.js +82 -0
  101. package/src/directive/ngIf.js +134 -0
  102. package/src/directive/ngInclude.js +217 -0
  103. package/src/directive/ngModel.js +1356 -0
  104. package/src/directive/ngModelOptions.js +509 -0
  105. package/src/directive/ngOptions.js +670 -0
  106. package/src/directive/ngRef.js +90 -0
  107. package/src/directive/ngRepeat.js +650 -0
  108. package/src/directive/ngShowHide.js +255 -0
  109. package/src/directive/ngSwitch.js +178 -0
  110. package/src/directive/ngTransclude.js +98 -0
  111. package/src/directive/non-bindable.js +11 -0
  112. package/src/directive/non-bindable.md +17 -0
  113. package/src/directive/script.js +30 -0
  114. package/src/directive/select.js +624 -0
  115. package/src/directive/style.js +25 -0
  116. package/src/directive/style.md +23 -0
  117. package/src/directive/validators.js +329 -0
  118. package/src/exts/aria.js +544 -0
  119. package/src/exts/messages.js +852 -0
  120. package/src/filters/filter.js +207 -0
  121. package/src/filters/filter.md +69 -0
  122. package/src/filters/filters.js +239 -0
  123. package/src/filters/json.md +16 -0
  124. package/src/filters/limit-to.js +43 -0
  125. package/src/filters/limit-to.md +19 -0
  126. package/src/filters/order-by.js +183 -0
  127. package/src/filters/order-by.md +83 -0
  128. package/src/index.js +13 -0
  129. package/src/injector.js +1034 -0
  130. package/src/jqLite.js +1117 -0
  131. package/src/loader.js +1320 -0
  132. package/src/public.js +215 -0
  133. package/src/routeToRegExp.js +41 -0
  134. package/src/services/anchorScroll.js +135 -0
  135. package/src/services/browser.js +321 -0
  136. package/src/services/cacheFactory.js +398 -0
  137. package/src/services/cookieReader.js +72 -0
  138. package/src/services/document.js +64 -0
  139. package/src/services/http.js +1537 -0
  140. package/src/services/httpBackend.js +206 -0
  141. package/src/services/log.js +160 -0
  142. package/src/services/templateRequest.js +139 -0
  143. package/test/angular.spec.js +2153 -0
  144. package/test/aria/aria.spec.js +1245 -0
  145. package/test/binding.spec.js +504 -0
  146. package/test/build-test.html +14 -0
  147. package/test/injector.spec.js +2327 -0
  148. package/test/jasmine/jasmine-5.1.2/boot0.js +65 -0
  149. package/test/jasmine/jasmine-5.1.2/boot1.js +133 -0
  150. package/test/jasmine/jasmine-5.1.2/jasmine-html.js +963 -0
  151. package/test/jasmine/jasmine-5.1.2/jasmine.css +320 -0
  152. package/test/jasmine/jasmine-5.1.2/jasmine.js +10824 -0
  153. package/test/jasmine/jasmine-5.1.2/jasmine_favicon.png +0 -0
  154. package/test/jasmine/jasmine-browser.json +17 -0
  155. package/test/jasmine/jasmine.json +9 -0
  156. package/test/jqlite.spec.js +2133 -0
  157. package/test/loader.spec.js +219 -0
  158. package/test/messages/messages.spec.js +1146 -0
  159. package/test/min-err.spec.js +174 -0
  160. package/test/mock-test.html +13 -0
  161. package/test/module-test.html +15 -0
  162. package/test/ng/anomate.spec.js +606 -0
  163. package/test/ng/cache-factor.spec.js +334 -0
  164. package/test/ng/compile.spec.js +17956 -0
  165. package/test/ng/controller-provider.spec.js +227 -0
  166. package/test/ng/cookie-reader.spec.js +98 -0
  167. package/test/ng/directive/a.spec.js +192 -0
  168. package/test/ng/directive/bind.spec.js +334 -0
  169. package/test/ng/directive/boolean.spec.js +136 -0
  170. package/test/ng/directive/change.spec.js +71 -0
  171. package/test/ng/directive/class.spec.js +858 -0
  172. package/test/ng/directive/click.spec.js +38 -0
  173. package/test/ng/directive/cloak.spec.js +44 -0
  174. package/test/ng/directive/constoller.spec.js +194 -0
  175. package/test/ng/directive/element-style.spec.js +92 -0
  176. package/test/ng/directive/event.spec.js +282 -0
  177. package/test/ng/directive/form.spec.js +1518 -0
  178. package/test/ng/directive/href.spec.js +143 -0
  179. package/test/ng/directive/if.spec.js +402 -0
  180. package/test/ng/directive/include.spec.js +828 -0
  181. package/test/ng/directive/init.spec.js +68 -0
  182. package/test/ng/directive/input.spec.js +3810 -0
  183. package/test/ng/directive/list.spec.js +170 -0
  184. package/test/ng/directive/model-options.spec.js +1008 -0
  185. package/test/ng/directive/model.spec.js +1905 -0
  186. package/test/ng/directive/non-bindable.spec.js +55 -0
  187. package/test/ng/directive/options.spec.js +3583 -0
  188. package/test/ng/directive/ref.spec.js +575 -0
  189. package/test/ng/directive/repeat.spec.js +1675 -0
  190. package/test/ng/directive/script.spec.js +52 -0
  191. package/test/ng/directive/scrset.spec.js +67 -0
  192. package/test/ng/directive/select.spec.js +2541 -0
  193. package/test/ng/directive/show-hide.spec.js +253 -0
  194. package/test/ng/directive/src.spec.js +157 -0
  195. package/test/ng/directive/style.spec.js +178 -0
  196. package/test/ng/directive/switch.spec.js +647 -0
  197. package/test/ng/directive/validators.spec.js +717 -0
  198. package/test/ng/document.spec.js +52 -0
  199. package/test/ng/filter/filter.spec.js +714 -0
  200. package/test/ng/filter/filters.spec.js +35 -0
  201. package/test/ng/filter/limit-to.spec.js +251 -0
  202. package/test/ng/filter/order-by.spec.js +891 -0
  203. package/test/ng/filter.spec.js +149 -0
  204. package/test/ng/http-backend.spec.js +398 -0
  205. package/test/ng/http.spec.js +4071 -0
  206. package/test/ng/interpolate.spec.js +642 -0
  207. package/test/ng/interval.spec.js +343 -0
  208. package/test/ng/location.spec.js +3488 -0
  209. package/test/ng/on.spec.js +229 -0
  210. package/test/ng/parse.spec.js +4655 -0
  211. package/test/ng/prop.spec.js +805 -0
  212. package/test/ng/q.spec.js +2904 -0
  213. package/test/ng/root-element.spec.js +16 -0
  214. package/test/ng/sanitize-uri.spec.js +249 -0
  215. package/test/ng/sce.spec.js +660 -0
  216. package/test/ng/scope.spec.js +3442 -0
  217. package/test/ng/template-request.spec.js +236 -0
  218. package/test/ng/timeout.spec.js +351 -0
  219. package/test/ng/url-utils.spec.js +156 -0
  220. package/test/ng/utils.spec.js +144 -0
  221. package/test/original-test.html +21 -0
  222. package/test/public.spec.js +34 -0
  223. package/test/sanitize/bing-html.spec.js +36 -0
  224. package/test/server/express.js +158 -0
  225. package/test/test-utils.js +11 -0
  226. package/tsconfig.json +17 -0
  227. package/types/angular.d.ts +138 -0
  228. package/types/global.d.ts +9 -0
  229. package/types/index.d.ts +2357 -0
  230. package/types/jqlite.d.ts +558 -0
  231. package/vite.config.js +14 -0
@@ -0,0 +1,1086 @@
1
+ import { jqLite } from "../jqLite";
2
+ import { urlResolve } from "./urlUtils";
3
+ import {
4
+ encodeUriSegment,
5
+ forEach,
6
+ isBoolean,
7
+ isDefined,
8
+ isNumber,
9
+ isObject,
10
+ isString,
11
+ isUndefined,
12
+ minErr,
13
+ nodeName_,
14
+ parseKeyValue,
15
+ toInt,
16
+ toKeyValue,
17
+ } from "./utils";
18
+
19
+ export const PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/;
20
+ const DEFAULT_PORTS = { http: 80, https: 443, ftp: 21 };
21
+ const $locationMinErr = minErr("$location");
22
+
23
+ /**
24
+ * Encode path using encodeUriSegment, ignoring forward slashes
25
+ *
26
+ * @param {string} path Path to encode
27
+ * @returns {string}
28
+ */
29
+ function encodePath(path) {
30
+ const segments = path.split("/");
31
+ let i = segments.length;
32
+
33
+ while (i--) {
34
+ // decode forward slashes to prevent them from being double encoded
35
+ segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, "/"));
36
+ }
37
+
38
+ return segments.join("/");
39
+ }
40
+
41
+ function decodePath(path, html5Mode) {
42
+ const segments = path.split("/");
43
+ let i = segments.length;
44
+
45
+ while (i--) {
46
+ segments[i] = decodeURIComponent(segments[i]);
47
+ if (html5Mode) {
48
+ // encode forward slashes to prevent them from being mistaken for path separators
49
+ segments[i] = segments[i].replace(/\//g, "%2F");
50
+ }
51
+ }
52
+
53
+ return segments.join("/");
54
+ }
55
+
56
+ function normalizePath(pathValue, searchValue, hashValue) {
57
+ const search = toKeyValue(searchValue);
58
+ const hash = hashValue ? `#${encodeUriSegment(hashValue)}` : "";
59
+ const path = encodePath(pathValue);
60
+
61
+ return path + (search ? `?${search}` : "") + hash;
62
+ }
63
+
64
+ function parseAbsoluteUrl(absoluteUrl, locationObj) {
65
+ const parsedUrl = urlResolve(absoluteUrl);
66
+
67
+ locationObj.$$protocol = parsedUrl.protocol;
68
+ locationObj.$$host = parsedUrl.hostname;
69
+ locationObj.$$port =
70
+ toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
71
+ }
72
+
73
+ const DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
74
+ function parseAppUrl(url, locationObj, html5Mode) {
75
+ if (DOUBLE_SLASH_REGEX.test(url)) {
76
+ throw $locationMinErr("badpath", 'Invalid url "{0}".', url);
77
+ }
78
+
79
+ const prefixed = url.charAt(0) !== "/";
80
+ if (prefixed) {
81
+ url = `/${url}`;
82
+ }
83
+ const match = urlResolve(url);
84
+ const path =
85
+ prefixed && match.pathname.charAt(0) === "/"
86
+ ? match.pathname.substring(1)
87
+ : match.pathname;
88
+ locationObj.$$path = decodePath(path, html5Mode);
89
+ locationObj.$$search = parseKeyValue(match.search);
90
+ locationObj.$$hash = decodeURIComponent(match.hash);
91
+
92
+ // make sure path starts with '/';
93
+ if (locationObj.$$path && locationObj.$$path.charAt(0) !== "/") {
94
+ locationObj.$$path = `/${locationObj.$$path}`;
95
+ }
96
+ }
97
+
98
+ function startsWith(str, search) {
99
+ return str.slice(0, search.length) === search;
100
+ }
101
+
102
+ /**
103
+ *
104
+ * @param {string} base
105
+ * @param {string} url
106
+ * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
107
+ * the expected string.
108
+ */
109
+ export function stripBaseUrl(base, url) {
110
+ if (startsWith(url, base)) {
111
+ return url.substr(base.length);
112
+ }
113
+ }
114
+
115
+ export function stripHash(url) {
116
+ const index = url.indexOf("#");
117
+ return index === -1 ? url : url.substr(0, index);
118
+ }
119
+
120
+ export function stripFile(url) {
121
+ return url.substr(0, stripHash(url).lastIndexOf("/") + 1);
122
+ }
123
+
124
+ /* return the server only (scheme://host:port) */
125
+ export function serverBase(url) {
126
+ return url.substring(0, url.indexOf("/", url.indexOf("//") + 2));
127
+ }
128
+
129
+ /**
130
+ * LocationHtml5Url represents a URL
131
+ * This object is exposed as $location service when HTML5 mode is enabled and supported
132
+ *
133
+ * @constructor
134
+ * @param {string} appBase application base URL
135
+ * @param {string} appBaseNoFile application base URL stripped of any filename
136
+ * @param {string} basePrefix URL path prefix
137
+ */
138
+ export function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
139
+ this.$$html5 = true;
140
+ basePrefix = basePrefix || "";
141
+ parseAbsoluteUrl(appBase, this);
142
+
143
+ /**
144
+ * Parse given HTML5 (regular) URL string into properties
145
+ * @param {string} url HTML5 URL
146
+ * @private
147
+ */
148
+ this.$$parse = function (url) {
149
+ const pathUrl = stripBaseUrl(appBaseNoFile, url);
150
+ if (!isString(pathUrl)) {
151
+ throw $locationMinErr(
152
+ "ipthprfx",
153
+ 'Invalid url "{0}", missing path prefix "{1}".',
154
+ url,
155
+ appBaseNoFile,
156
+ );
157
+ }
158
+
159
+ parseAppUrl(pathUrl, this, true);
160
+
161
+ if (!this.$$path) {
162
+ this.$$path = "/";
163
+ }
164
+
165
+ this.$$compose();
166
+ };
167
+
168
+ this.$$normalizeUrl = function (url) {
169
+ return appBaseNoFile + url.substr(1); // first char is always '/'
170
+ };
171
+
172
+ this.$$parseLinkUrl = function (url, relHref) {
173
+ if (relHref && relHref[0] === "#") {
174
+ // special case for links to hash fragments:
175
+ // keep the old url and only replace the hash fragment
176
+ this.hash(relHref.slice(1));
177
+ return true;
178
+ }
179
+ let appUrl;
180
+ let prevAppUrl;
181
+ let rewrittenUrl;
182
+
183
+ if (isDefined((appUrl = stripBaseUrl(appBase, url)))) {
184
+ prevAppUrl = appUrl;
185
+ if (
186
+ basePrefix &&
187
+ isDefined((appUrl = stripBaseUrl(basePrefix, appUrl)))
188
+ ) {
189
+ rewrittenUrl = appBaseNoFile + (stripBaseUrl("/", appUrl) || appUrl);
190
+ } else {
191
+ rewrittenUrl = appBase + prevAppUrl;
192
+ }
193
+ } else if (isDefined((appUrl = stripBaseUrl(appBaseNoFile, url)))) {
194
+ rewrittenUrl = appBaseNoFile + appUrl;
195
+ } else if (appBaseNoFile === `${url}/`) {
196
+ rewrittenUrl = appBaseNoFile;
197
+ }
198
+ if (rewrittenUrl) {
199
+ this.$$parse(rewrittenUrl);
200
+ }
201
+ return !!rewrittenUrl;
202
+ };
203
+ }
204
+
205
+ /**
206
+ * LocationHashbangUrl represents URL
207
+ * This object is exposed as $location service when developer doesn't opt into html5 mode.
208
+ * It also serves as the base class for html5 mode fallback on legacy browsers.
209
+ *
210
+ * @constructor
211
+ * @param {string} appBase application base URL
212
+ * @param {string} appBaseNoFile application base URL stripped of any filename
213
+ * @param {string} hashPrefix hashbang prefix
214
+ */
215
+ export function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
216
+ parseAbsoluteUrl(appBase, this);
217
+
218
+ /**
219
+ * Parse given hashbang URL into properties
220
+ * @param {string} url Hashbang URL
221
+ * @private
222
+ */
223
+ this.$$parse = function (url) {
224
+ const withoutBaseUrl =
225
+ stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
226
+ let withoutHashUrl;
227
+
228
+ if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === "#") {
229
+ // The rest of the URL starts with a hash so we have
230
+ // got either a hashbang path or a plain hash fragment
231
+ withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
232
+ if (isUndefined(withoutHashUrl)) {
233
+ // There was no hashbang prefix so we just have a hash fragment
234
+ withoutHashUrl = withoutBaseUrl;
235
+ }
236
+ } else {
237
+ // There was no hashbang path nor hash fragment:
238
+ // If we are in HTML5 mode we use what is left as the path;
239
+ // Otherwise we ignore what is left
240
+ if (this.$$html5) {
241
+ withoutHashUrl = withoutBaseUrl;
242
+ } else {
243
+ withoutHashUrl = "";
244
+ if (isUndefined(withoutBaseUrl)) {
245
+ appBase = url;
246
+ /** @type {?} */ (this).replace();
247
+ }
248
+ }
249
+ }
250
+
251
+ parseAppUrl(withoutHashUrl, this, false);
252
+
253
+ this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
254
+
255
+ this.$$compose();
256
+
257
+ /*
258
+ * In Windows, on an anchor node on documents loaded from
259
+ * the filesystem, the browser will return a pathname
260
+ * prefixed with the drive name ('/C:/path') when a
261
+ * pathname without a drive is set:
262
+ * * a.setAttribute('href', '/foo')
263
+ * * a.pathname === '/C:/foo' //true
264
+ *
265
+ * Inside of AngularJS, we're always using pathnames that
266
+ * do not include drive names for routing.
267
+ */
268
+ function removeWindowsDriveName(path, url, base) {
269
+ /*
270
+ Matches paths for file protocol on windows,
271
+ such as /C:/foo/bar, and captures only /foo/bar.
272
+ */
273
+ const windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
274
+
275
+ let firstPathSegmentMatch;
276
+
277
+ // Get the relative path from the input URL.
278
+ if (startsWith(url, base)) {
279
+ url = url.replace(base, "");
280
+ }
281
+
282
+ // The input URL intentionally contains a first path segment that ends with a colon.
283
+ if (windowsFilePathExp.exec(url)) {
284
+ return path;
285
+ }
286
+
287
+ firstPathSegmentMatch = windowsFilePathExp.exec(path);
288
+ return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
289
+ }
290
+ };
291
+
292
+ this.$$normalizeUrl = function (url) {
293
+ return appBase + (url ? hashPrefix + url : "");
294
+ };
295
+
296
+ this.$$parseLinkUrl = function (url) {
297
+ if (stripHash(appBase) === stripHash(url)) {
298
+ this.$$parse(url);
299
+ return true;
300
+ }
301
+ return false;
302
+ };
303
+ }
304
+
305
+ /**
306
+ * LocationHashbangUrl represents URL
307
+ * This object is exposed as $location service when html5 history api is enabled but the browser
308
+ * does not support it.
309
+ *
310
+ * @constructor
311
+ * @param {string} appBase application base URL
312
+ * @param {string} appBaseNoFile application base URL stripped of any filename
313
+ * @param {string} hashPrefix hashbang prefix
314
+ */
315
+ export function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
316
+ this.$$html5 = true;
317
+ LocationHashbangUrl.apply(this, arguments);
318
+
319
+ this.$$parseLinkUrl = function (url, relHref) {
320
+ if (relHref && relHref[0] === "#") {
321
+ // special case for links to hash fragments:
322
+ // keep the old url and only replace the hash fragment
323
+ this.hash(relHref.slice(1));
324
+ return true;
325
+ }
326
+
327
+ let rewrittenUrl;
328
+ let appUrl;
329
+
330
+ if (appBase === stripHash(url)) {
331
+ rewrittenUrl = url;
332
+ } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
333
+ rewrittenUrl = appBase + hashPrefix + appUrl;
334
+ } else if (appBaseNoFile === `${url}/`) {
335
+ rewrittenUrl = appBaseNoFile;
336
+ }
337
+ if (rewrittenUrl) {
338
+ this.$$parse(rewrittenUrl);
339
+ }
340
+ return !!rewrittenUrl;
341
+ };
342
+
343
+ this.$$normalizeUrl = function (url) {
344
+ // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
345
+ return appBase + hashPrefix + url;
346
+ };
347
+ }
348
+
349
+ const locationPrototype = {
350
+ /**
351
+ * Ensure absolute URL is initialized.
352
+ * @private
353
+ */
354
+ $$absUrl: "",
355
+
356
+ /**
357
+ * Are we in html5 mode?
358
+ * @private
359
+ */
360
+ $$html5: false,
361
+
362
+ /**
363
+ * Has any change been replacing?
364
+ * @private
365
+ */
366
+ $$replace: false,
367
+
368
+ /**
369
+ * Compose url and update `url` and `absUrl` property
370
+ * @private
371
+ */
372
+ $$compose() {
373
+ this.$$url = normalizePath(this.$$path, this.$$search, this.$$hash);
374
+ this.$$absUrl = this.$$normalizeUrl(this.$$url);
375
+ this.$$urlUpdatedByLocation = true;
376
+ },
377
+
378
+ /**
379
+ * @ngdoc method
380
+ * @name $location#absUrl
381
+ *
382
+ * @description
383
+ * This method is getter only.
384
+ *
385
+ * Return full URL representation with all segments encoded according to rules specified in
386
+ * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
387
+ *
388
+ *
389
+ * ```js
390
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
391
+ * let absUrl = $location.absUrl();
392
+ * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
393
+ * ```
394
+ *
395
+ * @return {string} full URL
396
+ */
397
+ absUrl: locationGetter("$$absUrl"),
398
+
399
+ /**
400
+ * @ngdoc method
401
+ * @name $location#url
402
+ *
403
+ * @description
404
+ * This method is getter / setter.
405
+ *
406
+ * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
407
+ *
408
+ * Change path, search and hash, when called with parameter and return `$location`.
409
+ *
410
+ *
411
+ * ```js
412
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
413
+ * let url = $location.url();
414
+ * // => "/some/path?foo=bar&baz=xoxo"
415
+ * ```
416
+ *
417
+ * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`)
418
+ * @return {string} url
419
+ */
420
+ url(url) {
421
+ if (isUndefined(url)) {
422
+ return this.$$url;
423
+ }
424
+
425
+ const match = PATH_MATCH.exec(url);
426
+ if (match[1] || url === "") this.path(decodeURIComponent(match[1]));
427
+ if (match[2] || match[1] || url === "") this.search(match[3] || "");
428
+ this.hash(match[5] || "");
429
+
430
+ return this;
431
+ },
432
+
433
+ /**
434
+ * @ngdoc method
435
+ * @name $location#protocol
436
+ *
437
+ * @description
438
+ * This method is getter only.
439
+ *
440
+ * Return protocol of current URL.
441
+ *
442
+ *
443
+ * ```js
444
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
445
+ * let protocol = $location.protocol();
446
+ * // => "http"
447
+ * ```
448
+ *
449
+ * @return {string} protocol of current URL
450
+ */
451
+ protocol: locationGetter("$$protocol"),
452
+
453
+ /**
454
+ * @ngdoc method
455
+ * @name $location#host
456
+ *
457
+ * @description
458
+ * This method is getter only.
459
+ *
460
+ * Return host of current URL.
461
+ *
462
+ * Note: compared to the non-AngularJS version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
463
+ *
464
+ *
465
+ * ```js
466
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
467
+ * let host = $location.host();
468
+ * // => "example.com"
469
+ *
470
+ * // given URL http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
471
+ * host = $location.host();
472
+ * // => "example.com"
473
+ * host = location.host;
474
+ * // => "example.com:8080"
475
+ * ```
476
+ *
477
+ * @return {string} host of current URL.
478
+ */
479
+ host: locationGetter("$$host"),
480
+
481
+ /**
482
+ * @ngdoc method
483
+ * @name $location#port
484
+ *
485
+ * @description
486
+ * This method is getter only.
487
+ *
488
+ * Return port of current URL.
489
+ *
490
+ *
491
+ * ```js
492
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
493
+ * let port = $location.port();
494
+ * // => 80
495
+ * ```
496
+ *
497
+ * @return {Number} port
498
+ */
499
+ port: locationGetter("$$port"),
500
+
501
+ /**
502
+ * @ngdoc method
503
+ * @name $location#path
504
+ *
505
+ * @description
506
+ * This method is getter / setter.
507
+ *
508
+ * Return path of current URL when called without any parameter.
509
+ *
510
+ * Change path when called with parameter and return `$location`.
511
+ *
512
+ * Note: Path should always begin with forward slash (/), this method will add the forward slash
513
+ * if it is missing.
514
+ *
515
+ *
516
+ * ```js
517
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
518
+ * let path = $location.path();
519
+ * // => "/some/path"
520
+ * ```
521
+ *
522
+ * @param {(string|number)=} path New path
523
+ * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
524
+ */
525
+ path: locationGetterSetter("$$path", (path) => {
526
+ path = path !== null ? path.toString() : "";
527
+ return path.charAt(0) === "/" ? path : `/${path}`;
528
+ }),
529
+
530
+ /**
531
+ * @ngdoc method
532
+ * @name $location#search
533
+ *
534
+ * @description
535
+ * This method is getter / setter.
536
+ *
537
+ * Return search part (as object) of current URL when called without any parameter.
538
+ *
539
+ * Change search part when called with parameter and return `$location`.
540
+ *
541
+ *
542
+ * ```js
543
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
544
+ * let searchObject = $location.search();
545
+ * // => {foo: 'bar', baz: 'xoxo'}
546
+ *
547
+ * // set foo to 'yipee'
548
+ * $location.search('foo', 'yipee');
549
+ * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
550
+ * ```
551
+ *
552
+ * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
553
+ * hash object.
554
+ *
555
+ * When called with a single argument the method acts as a setter, setting the `search` component
556
+ * of `$location` to the specified value.
557
+ *
558
+ * If the argument is a hash object containing an array of values, these values will be encoded
559
+ * as duplicate search parameters in the URL.
560
+ *
561
+ * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
562
+ * will override only a single search property.
563
+ *
564
+ * If `paramValue` is an array, it will override the property of the `search` component of
565
+ * `$location` specified via the first argument.
566
+ *
567
+ * If `paramValue` is `null`, the property specified via the first argument will be deleted.
568
+ *
569
+ * If `paramValue` is `true`, the property specified via the first argument will be added with no
570
+ * value nor trailing equal sign.
571
+ *
572
+ * @return {Object} If called with no arguments returns the parsed `search` object. If called with
573
+ * one or more arguments returns `$location` object itself.
574
+ */
575
+ search(search, paramValue) {
576
+ switch (arguments.length) {
577
+ case 0:
578
+ return this.$$search;
579
+ case 1:
580
+ if (isString(search) || isNumber(search)) {
581
+ search = search.toString();
582
+ this.$$search = parseKeyValue(search);
583
+ } else if (isObject(search)) {
584
+ search = structuredClone(search, {});
585
+ // remove object undefined or null properties
586
+ forEach(search, (value, key) => {
587
+ if (value == null) delete search[key];
588
+ });
589
+
590
+ this.$$search = search;
591
+ } else {
592
+ throw $locationMinErr(
593
+ "isrcharg",
594
+ "The first argument of the `$location#search()` call must be a string or an object.",
595
+ );
596
+ }
597
+ break;
598
+ default:
599
+ if (isUndefined(paramValue) || paramValue === null) {
600
+ delete this.$$search[search];
601
+ } else {
602
+ this.$$search[search] = paramValue;
603
+ }
604
+ }
605
+
606
+ this.$$compose();
607
+ return this;
608
+ },
609
+
610
+ /**
611
+ * @ngdoc method
612
+ * @name $location#hash
613
+ *
614
+ * @description
615
+ * This method is getter / setter.
616
+ *
617
+ * Returns the hash fragment when called without any parameters.
618
+ *
619
+ * Changes the hash fragment when called with a parameter and returns `$location`.
620
+ *
621
+ *
622
+ * ```js
623
+ * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
624
+ * let hash = $location.hash();
625
+ * // => "hashValue"
626
+ * ```
627
+ *
628
+ * @param {(string|number)=} hash New hash fragment
629
+ * @return {string} hash
630
+ */
631
+ hash: locationGetterSetter("$$hash", (hash) =>
632
+ hash !== null ? hash.toString() : "",
633
+ ),
634
+
635
+ /**
636
+ * @ngdoc method
637
+ * @name $location#replace
638
+ *
639
+ * @description
640
+ * If called, all changes to $location during the current `$digest` will replace the current history
641
+ * record, instead of adding a new one.
642
+ */
643
+ replace() {
644
+ this.$$replace = true;
645
+ return this;
646
+ },
647
+ };
648
+
649
+ forEach(
650
+ [LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url],
651
+ (Location) => {
652
+ Location.prototype = Object.create(locationPrototype);
653
+
654
+ /**
655
+ * @ngdoc method
656
+ * @name $location#state
657
+ *
658
+ * @description
659
+ * This method is getter / setter.
660
+ *
661
+ * Return the history state object when called without any parameter.
662
+ *
663
+ * Change the history state object when called with one parameter and return `$location`.
664
+ * The state object is later passed to `pushState` or `replaceState`.
665
+ *
666
+ * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
667
+ * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
668
+ * older browsers (like IE9 or Android < 4.0), don't use this method.
669
+ *
670
+ * @param {object=} state State object for pushState or replaceState
671
+ * @return {object} state
672
+ */
673
+ Location.prototype.state = function (state) {
674
+ if (!arguments.length) {
675
+ return this.$$state;
676
+ }
677
+
678
+ if (Location !== LocationHtml5Url || !this.$$html5) {
679
+ throw $locationMinErr(
680
+ "nostate",
681
+ "History API state support is available only " +
682
+ "in HTML5 mode and only in browsers supporting HTML5 History API",
683
+ );
684
+ }
685
+ // The user might modify `stateObject` after invoking `$location.state(stateObject)`
686
+ // but we're changing the $$state reference to $browser.state() during the $digest
687
+ // so the modification window is narrow.
688
+ this.$$state = isUndefined(state) ? null : state;
689
+ this.$$urlUpdatedByLocation = true;
690
+
691
+ return this;
692
+ };
693
+ },
694
+ );
695
+
696
+ function locationGetter(property) {
697
+ return function () {
698
+ return this[property];
699
+ };
700
+ }
701
+
702
+ function locationGetterSetter(property, preprocess) {
703
+ return function (value) {
704
+ if (isUndefined(value)) {
705
+ return this[property];
706
+ }
707
+
708
+ this[property] = preprocess(value);
709
+ this.$$compose();
710
+
711
+ return this;
712
+ };
713
+ }
714
+
715
+ /**
716
+ * @ngdoc service
717
+ * @name $location
718
+ *
719
+ * @requires $rootElement
720
+ *
721
+ * @description
722
+ * The $location service parses the URL in the browser address bar (based on the
723
+ * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
724
+ * available to your application. Changes to the URL in the address bar are reflected into
725
+ * $location service and changes to $location are reflected into the browser address bar.
726
+ *
727
+ * **The $location service:**
728
+ *
729
+ * - Exposes the current URL in the browser address bar, so you can
730
+ * - Watch and observe the URL.
731
+ * - Change the URL.
732
+ * - Synchronizes the URL with the browser when the user
733
+ * - Changes the address bar.
734
+ * - Clicks the back or forward button (or clicks a History link).
735
+ * - Clicks on a link.
736
+ * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
737
+ *
738
+ * For more information see {@link guide/$location Developer Guide: Using $location}
739
+ */
740
+
741
+ /**
742
+ * @ngdoc provider
743
+ * @name $locationProvider
744
+ *
745
+ *
746
+ * @description
747
+ * Use the `$locationProvider` to configure how the application deep linking paths are stored.
748
+ */
749
+ export function $LocationProvider() {
750
+ let hashPrefix = "!";
751
+ const html5Mode = {
752
+ enabled: false,
753
+ requireBase: true,
754
+ rewriteLinks: true,
755
+ };
756
+
757
+ /**
758
+ * @ngdoc method
759
+ * @name $locationProvider#hashPrefix
760
+ * @description
761
+ * The default value for the prefix is `'!'`.
762
+ * @param {string=} prefix Prefix for hash part (containing path and search)
763
+ * @returns {*} current value if used as getter or itself (chaining) if used as setter
764
+ */
765
+ this.hashPrefix = function (prefix) {
766
+ if (isDefined(prefix)) {
767
+ hashPrefix = prefix;
768
+ return this;
769
+ }
770
+ return hashPrefix;
771
+ };
772
+
773
+ /**
774
+ * @ngdoc method
775
+ * @name $locationProvider#html5Mode
776
+ * @description
777
+ * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
778
+ * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
779
+ * properties:
780
+ * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
781
+ * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
782
+ * support `pushState`.
783
+ * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
784
+ * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
785
+ * true, and a base tag is not present, an error will be thrown when `$location` is injected.
786
+ * See the {@link guide/$location $location guide for more information}
787
+ * - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled,
788
+ * enables/disables URL rewriting for relative links. If set to a string, URL rewriting will
789
+ * only happen on links with an attribute that matches the given string. For example, if set
790
+ * to `'internal-link'`, then the URL will only be rewritten for `<a internal-link>` links.
791
+ * Note that [attribute name normalization](guide/directive#normalization) does not apply
792
+ * here, so `'internalLink'` will **not** match `'internal-link'`.
793
+ *
794
+ * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
795
+ */
796
+ this.html5Mode = function (mode) {
797
+ if (isBoolean(mode)) {
798
+ html5Mode.enabled = mode;
799
+ return this;
800
+ }
801
+ if (isObject(mode)) {
802
+ if (isBoolean(mode.enabled)) {
803
+ html5Mode.enabled = mode.enabled;
804
+ }
805
+
806
+ if (isBoolean(mode.requireBase)) {
807
+ html5Mode.requireBase = mode.requireBase;
808
+ }
809
+
810
+ if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
811
+ html5Mode.rewriteLinks = mode.rewriteLinks;
812
+ }
813
+
814
+ return this;
815
+ }
816
+ return html5Mode;
817
+ };
818
+
819
+ /**
820
+ * @ngdoc event
821
+ * @name $location#$locationChangeStart
822
+ * @eventType broadcast on root scope
823
+ * @description
824
+ * Broadcasted before a URL will change.
825
+ *
826
+ * This change can be prevented by calling
827
+ * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
828
+ * details about event object. Upon successful change
829
+ * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
830
+ *
831
+ * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
832
+ * the browser supports the HTML5 History API.
833
+ *
834
+ * @param {Object} angularEvent Synthetic event object.
835
+ * @param {string} newUrl New URL
836
+ * @param {string=} oldUrl URL that was before it was changed.
837
+ * @param {string=} newState New history state object
838
+ * @param {string=} oldState History state object that was before it was changed.
839
+ */
840
+
841
+ /**
842
+ * @ngdoc event
843
+ * @name $location#$locationChangeSuccess
844
+ * @eventType broadcast on root scope
845
+ * @description
846
+ * Broadcasted after a URL was changed.
847
+ *
848
+ * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
849
+ * the browser supports the HTML5 History API.
850
+ *
851
+ * @param {Object} angularEvent Synthetic event object.
852
+ * @param {string} newUrl New URL
853
+ * @param {string=} oldUrl URL that was before it was changed.
854
+ * @param {string=} newState New history state object
855
+ * @param {string=} oldState History state object that was before it was changed.
856
+ */
857
+
858
+ this.$get = [
859
+ "$rootScope",
860
+ "$browser",
861
+ "$rootElement",
862
+ function ($rootScope, $browser, $rootElement) {
863
+ let $location;
864
+ let LocationMode;
865
+ const baseHref = $browser.baseHref(); // if base[href] is undefined, it defaults to ''
866
+ const initialUrl = $browser.url();
867
+ let appBase;
868
+
869
+ if (html5Mode.enabled) {
870
+ if (!baseHref && html5Mode.requireBase) {
871
+ throw $locationMinErr(
872
+ "nobase",
873
+ "$location in HTML5 mode requires a <base> tag to be present!",
874
+ );
875
+ }
876
+ appBase = serverBase(initialUrl) + (baseHref || "/");
877
+ LocationMode = LocationHtml5Url;
878
+ } else {
879
+ appBase = stripHash(initialUrl);
880
+ LocationMode = LocationHashbangUrl;
881
+ }
882
+ const appBaseNoFile = stripFile(appBase);
883
+
884
+ $location = new LocationMode(appBase, appBaseNoFile, `#${hashPrefix}`);
885
+ $location.$$parseLinkUrl(initialUrl, initialUrl);
886
+
887
+ $location.$$state = $browser.state();
888
+
889
+ const IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
890
+
891
+ // Determine if two URLs are equal despite potentially having different encoding/normalizing
892
+ // such as $location.absUrl() vs $browser.url()
893
+ // See https://github.com/angular/angular.js/issues/16592
894
+ function urlsEqual(a, b) {
895
+ return a === b || urlResolve(a).href === urlResolve(b).href;
896
+ }
897
+
898
+ function setBrowserUrlWithFallback(url, replace, state) {
899
+ const oldUrl = $location.url();
900
+ const oldState = $location.$$state;
901
+ try {
902
+ $browser.url(url, replace, state);
903
+
904
+ // Make sure $location.state() returns referentially identical (not just deeply equal)
905
+ // state object; this makes possible quick checking if the state changed in the digest
906
+ // loop. Checking deep equality would be too expensive.
907
+ $location.$$state = $browser.state();
908
+ } catch (e) {
909
+ // Restore old values if pushState fails
910
+ $location.url(oldUrl);
911
+ $location.$$state = oldState;
912
+
913
+ throw e;
914
+ }
915
+ }
916
+
917
+ $rootElement.on("click", (event) => {
918
+ const { rewriteLinks } = html5Mode;
919
+ // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
920
+ // currently we open nice url link and redirect then
921
+
922
+ if (
923
+ !rewriteLinks ||
924
+ event.ctrlKey ||
925
+ event.metaKey ||
926
+ event.shiftKey ||
927
+ event.which === 2 ||
928
+ event.button === 2
929
+ )
930
+ return;
931
+
932
+ let elm = jqLite(event.target);
933
+
934
+ // traverse the DOM up to find first A tag
935
+ while (nodeName_(elm[0]) !== "a") {
936
+ // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
937
+ if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
938
+ }
939
+
940
+ if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks)))
941
+ return;
942
+
943
+ let absHref = elm.prop("href");
944
+ // get the actual href attribute - see
945
+ // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
946
+ const relHref = elm.attr("href") || elm.attr("xlink:href");
947
+
948
+ if (
949
+ isObject(absHref) &&
950
+ absHref.toString() === "[object SVGAnimatedString]"
951
+ ) {
952
+ // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
953
+ // an animation.
954
+ absHref = urlResolve(absHref.animVal).href;
955
+ }
956
+
957
+ // Ignore when url is started with javascript: or mailto:
958
+ if (IGNORE_URI_REGEXP.test(absHref)) return;
959
+
960
+ if (absHref && !elm.attr("target") && !event.isDefaultPrevented()) {
961
+ if ($location.$$parseLinkUrl(absHref, relHref)) {
962
+ // We do a preventDefault for all urls that are part of the AngularJS application,
963
+ // in html5mode and also without, so that we are able to abort navigation without
964
+ // getting double entries in the location history.
965
+ event.preventDefault();
966
+ // update location manually
967
+ if ($location.absUrl() !== $browser.url()) {
968
+ $rootScope.$apply();
969
+ }
970
+ }
971
+ }
972
+ });
973
+
974
+ // rewrite hashbang url <> html5 url
975
+ if ($location.absUrl() !== initialUrl) {
976
+ $browser.url($location.absUrl(), true);
977
+ }
978
+
979
+ let initializing = true;
980
+
981
+ // update $location when $browser url changes
982
+ $browser.onUrlChange((newUrl, newState) => {
983
+ if (!startsWith(newUrl, appBaseNoFile)) {
984
+ // If we are navigating outside of the app then force a reload
985
+ window.location.href = newUrl;
986
+ return;
987
+ }
988
+
989
+ $rootScope.$evalAsync(() => {
990
+ const oldUrl = $location.absUrl();
991
+ const oldState = $location.$$state;
992
+ let defaultPrevented;
993
+ $location.$$parse(newUrl);
994
+ $location.$$state = newState;
995
+
996
+ defaultPrevented = $rootScope.$broadcast(
997
+ "$locationChangeStart",
998
+ newUrl,
999
+ oldUrl,
1000
+ newState,
1001
+ oldState,
1002
+ ).defaultPrevented;
1003
+
1004
+ // if the location was changed by a `$locationChangeStart` handler then stop
1005
+ // processing this location change
1006
+ if ($location.absUrl() !== newUrl) return;
1007
+
1008
+ if (defaultPrevented) {
1009
+ $location.$$parse(oldUrl);
1010
+ $location.$$state = oldState;
1011
+ setBrowserUrlWithFallback(oldUrl, false, oldState);
1012
+ } else {
1013
+ initializing = false;
1014
+ afterLocationChange(oldUrl, oldState);
1015
+ }
1016
+ });
1017
+ if (!$rootScope.$$phase) $rootScope.$digest();
1018
+ });
1019
+
1020
+ // update browser
1021
+ $rootScope.$watch(() => {
1022
+ if (initializing || $location.$$urlUpdatedByLocation) {
1023
+ $location.$$urlUpdatedByLocation = false;
1024
+
1025
+ const oldUrl = $browser.url();
1026
+ const newUrl = $location.absUrl();
1027
+ const oldState = $browser.state();
1028
+ const currentReplace = $location.$$replace;
1029
+ const urlOrStateChanged =
1030
+ !urlsEqual(oldUrl, newUrl) ||
1031
+ ($location.$$html5 && oldState !== $location.$$state);
1032
+
1033
+ if (initializing || urlOrStateChanged) {
1034
+ initializing = false;
1035
+
1036
+ $rootScope.$evalAsync(() => {
1037
+ const newUrl = $location.absUrl();
1038
+ const { defaultPrevented } = $rootScope.$broadcast(
1039
+ "$locationChangeStart",
1040
+ newUrl,
1041
+ oldUrl,
1042
+ $location.$$state,
1043
+ oldState,
1044
+ );
1045
+
1046
+ // if the location was changed by a `$locationChangeStart` handler then stop
1047
+ // processing this location change
1048
+ if ($location.absUrl() !== newUrl) return;
1049
+
1050
+ if (defaultPrevented) {
1051
+ $location.$$parse(oldUrl);
1052
+ $location.$$state = oldState;
1053
+ } else {
1054
+ if (urlOrStateChanged) {
1055
+ setBrowserUrlWithFallback(
1056
+ newUrl,
1057
+ currentReplace,
1058
+ oldState === $location.$$state ? null : $location.$$state,
1059
+ );
1060
+ }
1061
+ afterLocationChange(oldUrl, oldState);
1062
+ }
1063
+ });
1064
+ }
1065
+ }
1066
+
1067
+ $location.$$replace = false;
1068
+
1069
+ // we don't need to return anything because $evalAsync will make the digest loop dirty when
1070
+ // there is a change
1071
+ });
1072
+
1073
+ return $location;
1074
+
1075
+ function afterLocationChange(oldUrl, oldState) {
1076
+ $rootScope.$broadcast(
1077
+ "$locationChangeSuccess",
1078
+ $location.absUrl(),
1079
+ oldUrl,
1080
+ $location.$$state,
1081
+ oldState,
1082
+ );
1083
+ }
1084
+ },
1085
+ ];
1086
+ }