1
1
'use strict' ;
2
2
3
- var URL_MATCH = / ^ ( [ ^ : ] + ) : \/ \/ ( \w + : { 0 , 1 } \w * @ ) ? ( [ \w \. - ] * ) ( : ( [ 0 - 9 ] + ) ) ? ( \/ [ ^ \? # ] * ) ? ( \? ( [ ^ # ] * ) ) ? ( # ( .* ) ) ? $ / ,
4
- PATH_MATCH = / ^ ( [ ^ \? # ] * ) ? ( \? ( [ ^ # ] * ) ) ? ( # ( .* ) ) ? $ / ,
5
- HASH_MATCH = PATH_MATCH ,
3
+ var SERVER_MATCH = / ^ ( [ ^ : ] + ) : \/ \/ ( \w + : { 0 , 1 } \w * @ ) ? ( [ \w \. - ] * ) ( : ( [ 0 - 9 ] + ) ) ? / ,
4
+ PATH_MATCH = / ^ ( [ ^ \? # ] * ) ( \? ( [ ^ # ] * ) ) ? ( # ( .* ) ) ? $ / ,
6
5
DEFAULT_PORTS = { 'http' : 80 , 'https' : 443 , 'ftp' : 21 } ;
7
6
8
7
@@ -23,104 +22,73 @@ function encodePath(path) {
23
22
return segments . join ( '/' ) ;
24
23
}
25
24
26
-
27
25
function matchUrl ( url , obj ) {
28
- var match = URL_MATCH . exec ( url ) ;
29
-
30
- match = {
31
- protocol : match [ 1 ] ,
32
- host : match [ 3 ] ,
33
- port : int ( match [ 5 ] ) || DEFAULT_PORTS [ match [ 1 ] ] || null ,
34
- path : match [ 6 ] || '/' ,
35
- search : match [ 8 ] ,
36
- hash : match [ 10 ]
37
- } ;
38
-
39
- if ( obj ) {
40
- obj . $$protocol = match . protocol ;
41
- obj . $$host = match . host ;
42
- obj . $$port = match . port ;
43
- }
26
+ var match = SERVER_MATCH . exec ( url ) ;
44
27
45
- return match ;
28
+ obj . $$protocol = match [ 1 ] ;
29
+ obj . $$host = match [ 3 ] ;
30
+ obj . $$port = int ( match [ 5 ] ) || DEFAULT_PORTS [ match [ 1 ] ] || null ;
46
31
}
47
32
33
+ function matchAppUrl ( url , obj ) {
34
+ var match = PATH_MATCH . exec ( url ) ;
48
35
49
- function composeProtocolHostPort ( protocol , host , port ) {
50
- return protocol + '://' + host + ( port == DEFAULT_PORTS [ protocol ] ? '' : ':' + port ) ;
51
- }
52
-
36
+ obj . $$path = decodeURIComponent ( match [ 1 ] ) ;
37
+ obj . $$search = parseKeyValue ( match [ 3 ] ) ;
38
+ obj . $$hash = decodeURIComponent ( match [ 5 ] || '' ) ;
53
39
54
- function pathPrefixFromBase ( basePath ) {
55
- return basePath . substr ( 0 , basePath . lastIndexOf ( '/' ) ) ;
40
+ // make sure path starts with '/';
41
+ if ( obj . $$path && obj . $$path . charAt ( 0 ) != '/' ) obj . $$path = '/' + obj . $$path ;
56
42
}
57
43
58
44
59
- function convertToHtml5Url ( url , basePath , hashPrefix ) {
60
- var match = matchUrl ( url ) ;
45
+ function composeProtocolHostPort ( protocol , host , port ) {
46
+ return protocol + '://' + host + ( port == DEFAULT_PORTS [ protocol ] ? '' : ':' + port ) ;
47
+ }
61
48
62
- // already html5 url
63
- if ( decodeURIComponent ( match . path ) != basePath || isUndefined ( match . hash ) ||
64
- match . hash . indexOf ( hashPrefix ) !== 0 ) {
65
- return url ;
66
- // convert hashbang url -> html5 url
67
- } else {
68
- return composeProtocolHostPort ( match . protocol , match . host , match . port ) +
69
- pathPrefixFromBase ( basePath ) + match . hash . substr ( hashPrefix . length ) ;
70
- }
49
+ function beginsWith ( begin , whole , otherwise ) {
50
+ return whole . indexOf ( begin ) == 0 ? whole . substr ( begin . length ) : otherwise ;
71
51
}
72
52
73
53
74
- function convertToHashbangUrl ( url , basePath , hashPrefix ) {
75
- var match = matchUrl ( url ) ;
54
+ function stripHash ( url ) {
55
+ var index = url . indexOf ( '#' ) ;
56
+ return index == - 1 ? url : url . substr ( 0 , index ) ;
57
+ }
76
58
77
- // already hashbang url
78
- if ( decodeURIComponent ( match . path ) == basePath ) {
79
- return url ;
80
- // convert html5 url -> hashbang url
81
- } else {
82
- var search = match . search && '?' + match . search || '' ,
83
- hash = match . hash && '#' + match . hash || '' ,
84
- pathPrefix = pathPrefixFromBase ( basePath ) ,
85
- path = match . path . substr ( pathPrefix . length ) ;
86
59
87
- if ( match . path . indexOf ( pathPrefix ) !== 0 ) {
88
- throw Error ( 'Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !' ) ;
89
- }
60
+ function stripFile ( url ) {
61
+ return url . substr ( 0 , stripHash ( url ) . lastIndexOf ( '/' ) + 1 ) ;
62
+ }
90
63
91
- return composeProtocolHostPort ( match . protocol , match . host , match . port ) + basePath +
92
- '#' + hashPrefix + path + search + hash ;
93
- }
64
+ /* return the server only */
65
+ function serverBase ( url ) {
66
+ return url . substring ( 0 , url . indexOf ( '/' , url . indexOf ( '//' ) + 2 ) ) ;
94
67
}
95
68
96
69
97
70
/**
98
- * LocationUrl represents an url
71
+ * LocationHtml5Url represents an url
99
72
* This object is exposed as $location service when HTML5 mode is enabled and supported
100
73
*
101
74
* @constructor
102
- * @param {string } url HTML5 url
103
- * @param {string } pathPrefix
75
+ * @param {string } appBase application base URL
76
+ * @param {string } hashPrefix hasbang prefix
104
77
*/
105
- function LocationUrl ( url , pathPrefix ) {
106
- pathPrefix = pathPrefix || '' ;
107
-
78
+ function LocationHtml5Url ( appBase , hashPrefix ) {
79
+ var appBaseNoFile = stripFile ( appBase ) ;
108
80
/**
109
81
* Parse given html5 (regular) url string into properties
110
82
* @param {string } url HTML5 url
111
83
* @private
112
84
*/
113
85
this . $$parse = function ( url ) {
114
- var match = matchUrl ( url , this ) ;
115
-
116
- if ( match . path . indexOf ( pathPrefix ) !== 0 ) {
117
- throw Error ( 'Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !' ) ;
86
+ matchUrl ( url , this ) ;
87
+ matchAppUrl ( url . substr ( appBaseNoFile . length ) , this ) ;
88
+ if ( ! this . $$ path) {
89
+ this . $$ path = '/' ;
118
90
}
119
91
120
- this . $$path = decodeURIComponent ( match . path . substr ( pathPrefix . length ) ) ;
121
- this . $$search = parseKeyValue ( match . search ) ;
122
- this . $$hash = match . hash && decodeURIComponent ( match . hash ) || '' ;
123
-
124
92
this . $$compose ( ) ;
125
93
} ;
126
94
@@ -133,11 +101,24 @@ function LocationUrl(url, pathPrefix) {
133
101
hash = this . $$hash ? '#' + encodeUriSegment ( this . $$hash ) : '' ;
134
102
135
103
this . $$url = encodePath ( this . $$path ) + ( search ? '?' + search : '' ) + hash ;
136
- this . $$absUrl = composeProtocolHostPort ( this . $$protocol , this . $$host , this . $$port ) +
137
- pathPrefix + this . $$url ;
104
+ this . $$absUrl = appBaseNoFile + this . $$url . substr ( 1 ) ; // first char is always '/'
138
105
} ;
139
106
140
- this . $$parse ( url ) ;
107
+ this . $$rewrite = function ( url ) {
108
+ var appUrl ;
109
+
110
+ if ( ( appUrl = beginsWith ( appBase , url ) ) !== undefined ) {
111
+ if ( ( appUrl = beginsWith ( hashPrefix , appUrl ) ) !== undefined ) {
112
+ return appBaseNoFile + ( beginsWith ( '/' , appUrl ) || appUrl ) ;
113
+ } else {
114
+ return appBase ;
115
+ }
116
+ } else if ( ( appUrl = beginsWith ( appBaseNoFile , url ) ) ) {
117
+ return appBaseNoFile + appUrl ;
118
+ } else if ( appBaseNoFile == url + '/' ) {
119
+ return appBaseNoFile ;
120
+ }
121
+ }
141
122
}
142
123
143
124
@@ -146,36 +127,22 @@ function LocationUrl(url, pathPrefix) {
146
127
* This object is exposed as $location service when html5 history api is disabled or not supported
147
128
*
148
129
* @constructor
149
- * @param {string } url Legacy url
150
- * @param {string } hashPrefix Prefix for hash part (containing path and search)
130
+ * @param {string } appBase application base URL
131
+ * @param {string } hashPrefix hasbang prefix
151
132
*/
152
- function LocationHashbangUrl ( url , hashPrefix ) {
153
- var basePath ;
133
+ function LocationHashbangUrl ( appBase , hashPrefix ) {
134
+ var appBaseNoFile = stripFile ( appBase ) ;
154
135
155
136
/**
156
137
* Parse given hashbang url into properties
157
138
* @param {string } url Hashbang url
158
139
* @private
159
140
*/
160
141
this . $$parse = function ( url ) {
161
- var match = matchUrl ( url , this ) ;
162
-
163
-
164
- if ( match . hash && match . hash . indexOf ( hashPrefix ) !== 0 ) {
165
- throw Error ( 'Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !' ) ;
166
- }
167
-
168
- basePath = match . path + ( match . search ? '?' + match . search : '' ) ;
169
- match = HASH_MATCH . exec ( ( match . hash || '' ) . substr ( hashPrefix . length ) ) ;
170
- if ( match [ 1 ] ) {
171
- this . $$path = ( match [ 1 ] . charAt ( 0 ) == '/' ? '' : '/' ) + decodeURIComponent ( match [ 1 ] ) ;
172
- } else {
173
- this . $$path = '' ;
174
- }
175
-
176
- this . $$search = parseKeyValue ( match [ 3 ] ) ;
177
- this . $$hash = match [ 5 ] && decodeURIComponent ( match [ 5 ] ) || '' ;
178
-
142
+ matchUrl ( url , this ) ;
143
+ url = beginsWith ( appBase , url ) || beginsWith ( appBaseNoFile , url ) ;
144
+ url = beginsWith ( hashPrefix , url , url ) ;
145
+ matchAppUrl ( url , this ) ;
179
146
this . $$compose ( ) ;
180
147
} ;
181
148
@@ -188,15 +155,48 @@ function LocationHashbangUrl(url, hashPrefix) {
188
155
hash = this . $$hash ? '#' + encodeUriSegment ( this . $$hash ) : '' ;
189
156
190
157
this . $$url = encodePath ( this . $$path ) + ( search ? '?' + search : '' ) + hash ;
191
- this . $$absUrl = composeProtocolHostPort ( this . $$protocol , this . $$host , this . $$port ) +
192
- basePath + ( this . $$url ? '#' + hashPrefix + this . $$url : '' ) ;
158
+ this . $$absUrl = appBase + ( this . $$url ? hashPrefix + this . $$url : '' ) ;
193
159
} ;
194
160
195
- this . $$parse ( url ) ;
161
+ this . $$rewrite = function ( url ) {
162
+ if ( stripHash ( appBase ) == stripHash ( url ) ) {
163
+ return url ;
164
+ }
165
+ }
196
166
}
197
167
198
168
199
- LocationUrl . prototype = {
169
+ /**
170
+ * LocationHashbangUrl represents url
171
+ * This object is exposed as $location service when html5 history api is enabled but the browser
172
+ * does not support it.
173
+ *
174
+ * @constructor
175
+ * @param {string } appBase application base URL
176
+ * @param {string } hashPrefix hasbang prefix
177
+ */
178
+ function LocationHashbangInHtml5Url ( appBase , hashPrefix ) {
179
+ LocationHashbangUrl . apply ( this , arguments ) ;
180
+
181
+ var appBaseNoFile = stripFile ( appBase ) ;
182
+
183
+ this . $$rewrite = function ( url ) {
184
+ var appUrl ;
185
+
186
+ if ( appBase == stripHash ( url ) ) {
187
+ return url ;
188
+ } else if ( ( appUrl = beginsWith ( appBaseNoFile , url ) ) ) {
189
+ return appBase + hashPrefix + appUrl ;
190
+ } else if ( appBaseNoFile === url + '/' ) {
191
+ return appBaseNoFile ;
192
+ }
193
+ }
194
+ }
195
+
196
+
197
+ LocationHashbangInHtml5Url . prototype =
198
+ LocationHashbangUrl . prototype =
199
+ LocationHtml5Url . prototype = {
200
200
201
201
/**
202
202
* Has any change been replacing ?
@@ -378,8 +378,6 @@ LocationUrl.prototype = {
378
378
}
379
379
} ;
380
380
381
- LocationHashbangUrl . prototype = inherit ( LocationUrl . prototype ) ;
382
-
383
381
function locationGetter ( property ) {
384
382
return function ( ) {
385
383
return this [ property ] ;
@@ -476,30 +474,20 @@ function $LocationProvider(){
476
474
this . $get = [ '$rootScope' , '$browser' , '$sniffer' , '$rootElement' ,
477
475
function ( $rootScope , $browser , $sniffer , $rootElement ) {
478
476
var $location ,
479
- basePath ,
480
- pathPrefix ,
481
- initUrl = $browser . url ( ) ,
482
- absUrlPrefix ;
477
+ LocationMode ,
478
+ baseHref = $browser . baseHref ( ) ,
479
+ initialUrl = $browser . url ( ) ,
480
+ appBase ;
483
481
484
482
if ( html5Mode ) {
485
- basePath = $browser . baseHref ( ) || '/' ;
486
- pathPrefix = pathPrefixFromBase ( basePath ) ;
487
- if ( $sniffer . history ) {
488
- $location = new LocationUrl (
489
- convertToHtml5Url ( initUrl , basePath , hashPrefix ) ,
490
- pathPrefix ) ;
491
- } else {
492
- $location = new LocationHashbangUrl (
493
- convertToHashbangUrl ( initUrl , basePath , hashPrefix ) ,
494
- hashPrefix ) ;
495
- }
496
- // link rewriting
497
- absUrlPrefix = composeProtocolHostPort (
498
- $location . protocol ( ) , $location . host ( ) , $location . port ( ) ) + pathPrefix ;
483
+ appBase = baseHref ? serverBase ( initialUrl ) + baseHref : initialUrl ;
484
+ LocationMode = $sniffer . history ? LocationHtml5Url : LocationHashbangInHtml5Url ;
499
485
} else {
500
- $location = new LocationHashbangUrl ( initUrl , hashPrefix ) ;
501
- absUrlPrefix = $location . absUrl ( ) . split ( '#' ) [ 0 ] ;
486
+ appBase = stripHash ( initialUrl ) ;
487
+ LocationMode = LocationHashbangUrl ;
502
488
}
489
+ $location = new LocationMode ( appBase , '#' + hashPrefix ) ;
490
+ $location . $$parse ( $location . $$rewrite ( initialUrl ) ) ;
503
491
504
492
$rootElement . bind ( 'click' , function ( event ) {
505
493
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
@@ -515,27 +503,21 @@ function $LocationProvider(){
515
503
}
516
504
517
505
var absHref = elm . prop ( 'href' ) ,
518
- href ;
519
-
520
- if ( ! absHref ||
521
- elm . attr ( 'target' ) ||
522
- absHref . indexOf ( absUrlPrefix ) !== 0 ) { // link to different domain or base path
523
- return ;
506
+ rewrittenUrl = $location . $$rewrite ( absHref ) ;
507
+
508
+ if ( absHref && ! elm . attr ( 'target' ) && rewrittenUrl ) {
509
+ // update location manually
510
+ $location . $$parse ( rewrittenUrl ) ;
511
+ $rootScope . $apply ( ) ;
512
+ event . preventDefault ( ) ;
513
+ // hack to work around FF6 bug 684208 when scenario runner clicks on links
514
+ window . angular [ 'ff-684208-preventDefault' ] = true ;
524
515
}
525
-
526
- // update location with href without the prefix
527
- href = absHref . substr ( absUrlPrefix . length ) ;
528
- if ( href . indexOf ( '#' + hashPrefix ) == 0 ) href = href . substr ( hashPrefix . length + 1 ) ;
529
- $location . url ( href ) ;
530
- $rootScope . $apply ( ) ;
531
- event . preventDefault ( ) ;
532
- // hack to work around FF6 bug 684208 when scenario runner clicks on links
533
- window . angular [ 'ff-684208-preventDefault' ] = true ;
534
516
} ) ;
535
517
536
518
537
519
// rewrite hashbang url <> html5 url
538
- if ( $location . absUrl ( ) != initUrl ) {
520
+ if ( $location . absUrl ( ) != initialUrl ) {
539
521
$browser . url ( $location . absUrl ( ) , true ) ;
540
522
}
541
523
0 commit comments