@@ -7,21 +7,26 @@ const queryStringRE = /\?.*$/
7
7
// HTML5 history mode
8
8
export default function ( Vue ) {
9
9
10
- const _ = Vue . util
11
10
const urlParser = document . createElement ( 'a' )
11
+ const {
12
+ bind,
13
+ isObject,
14
+ addClass,
15
+ removeClass
16
+ } = Vue . util
12
17
13
18
Vue . directive ( 'link' , {
14
19
15
20
bind ( ) {
16
- let vm = this . vm
21
+ const vm = this . vm
17
22
/* istanbul ignore if */
18
23
if ( ! vm . $route ) {
19
- warn (
20
- 'v-link can only be used inside a ' +
21
- 'router-enabled app.'
22
- )
24
+ warn ( 'v-link can only be used inside a router-enabled app.' )
23
25
return
24
26
}
27
+ this . router = vm . $route . router
28
+ // update things when the route changes
29
+ this . unwatch = vm . $watch ( '$route' , bind ( this . onRouteUpdate , this ) )
25
30
// no need to handle click if link expects to be opened
26
31
// in a new window/tab.
27
32
/* istanbul ignore if */
@@ -30,83 +35,99 @@ export default function (Vue) {
30
35
return
31
36
}
32
37
// handle click
33
- let router = vm . $route . router
34
- this . handler = ( e ) => {
35
- // don't redirect with control keys
36
- if ( e . metaKey || e . ctrlKey || e . shiftKey ) return
37
- // don't redirect when preventDefault called
38
- if ( e . defaultPrevented ) return
39
- // don't redirect on right click
40
- if ( e . button !== 0 ) return
38
+ this . el . addEventListener ( 'click' , bind ( this . onClick , this ) )
39
+ } ,
40
+
41
+ update ( target ) {
42
+ this . target = target
43
+ if ( isObject ( target ) ) {
44
+ this . append = target . append
45
+ this . exact = target . exact
46
+ this . prevActiveClass = this . activeClass
47
+ this . activeClass = target . activeClass
48
+ }
49
+ this . onRouteUpdate ( this . vm . $route )
50
+ } ,
41
51
42
- const target = this . target
43
- const go = ( target ) => {
44
- e . preventDefault ( )
45
- if ( target != null ) {
46
- router . go ( target )
47
- }
52
+ onClick ( e ) {
53
+ // don't redirect with control keys
54
+ if ( e . metaKey || e . ctrlKey || e . shiftKey ) return
55
+ // don't redirect when preventDefault called
56
+ if ( e . defaultPrevented ) return
57
+ // don't redirect on right click
58
+ if ( e . button !== 0 ) return
59
+
60
+ const target = this . target
61
+ const go = ( target ) => {
62
+ e . preventDefault ( )
63
+ if ( target != null ) {
64
+ this . router . go ( target )
48
65
}
66
+ }
49
67
50
- if ( this . el . tagName === 'A' || e . target === this . el ) {
51
- // v-link on <a v-link="'path'">
52
- if ( sameOrigin ( this . el , target ) ) {
53
- go ( target )
54
- } else if ( typeof target === 'string' ) {
55
- window . location . href = target
56
- }
57
- } else {
58
- // v-link delegate on <div v-link>
59
- var el = e . target
60
- while ( el && el . tagName !== 'A' && el !== this . el ) {
61
- el = el . parentNode
62
- }
63
- if ( ! el ) return
64
- if ( ! sameOrigin ( el , target ) && typeof target === 'string' ) {
65
- window . location . href = target
66
- }
67
- if ( el . tagName !== 'A' || ! el . href ) {
68
- // allow not anchor
69
- go ( target )
70
- } else if ( sameOrigin ( el , target ) ) {
71
- go ( {
72
- path : el . pathname ,
73
- replace : target && target . replace ,
74
- append : target && target . append
75
- } )
76
- }
68
+ if ( this . el . tagName === 'A' || e . target === this . el ) {
69
+ // v-link on <a v-link="'path'">
70
+ if ( sameOrigin ( this . el , target ) ) {
71
+ go ( target )
72
+ } else if ( typeof target === 'string' ) {
73
+ window . location . href = target
74
+ }
75
+ } else {
76
+ // v-link delegate on <div v-link>
77
+ var el = e . target
78
+ while ( el && el . tagName !== 'A' && el !== this . el ) {
79
+ el = el . parentNode
80
+ }
81
+ if ( ! el ) return
82
+ if ( ! sameOrigin ( el , target ) && typeof target === 'string' ) {
83
+ window . location . href = target
84
+ }
85
+ if ( el . tagName !== 'A' || ! el . href ) {
86
+ // allow not anchor
87
+ go ( target )
88
+ } else if ( sameOrigin ( el , target ) ) {
89
+ go ( {
90
+ path : el . pathname ,
91
+ replace : target && target . replace ,
92
+ append : target && target . append
93
+ } )
77
94
}
78
95
}
79
- this . el . addEventListener ( 'click' , this . handler )
80
- // manage active link class
81
- this . unwatch = vm . $watch (
82
- '$route.path' ,
83
- _ . bind ( this . updateClasses , this )
84
- )
85
96
} ,
86
97
87
- update ( path ) {
88
- let router = this . vm . $route . router
89
- let append
90
- this . target = path
91
- if ( _ . isObject ( path ) ) {
92
- append = path . append
93
- this . exact = path . exact
94
- this . prevActiveClass = this . activeClass
95
- this . activeClass = path . activeClass
98
+ onRouteUpdate ( route ) {
99
+ // router._stringifyPath is dependent on current route
100
+ // and needs to be called again whenver route changes.
101
+ var newPath = this . router . _stringifyPath ( this . target )
102
+ if ( this . path !== newPath ) {
103
+ this . path = newPath
104
+ this . updateActiveMatch ( )
105
+ this . updateHref ( )
96
106
}
97
- path = this . path = router . _stringifyPath ( path )
98
- this . activeRE = path && ! this . exact
107
+ this . updateClasses ( route . path )
108
+ } ,
109
+
110
+ updateActiveMatch ( ) {
111
+ this . activeRE = this . path && ! this . exact
99
112
? new RegExp (
100
113
'^' +
101
- path . replace ( / \/ $ / , '' ) . replace ( regexEscapeRE , '\\$&' ) +
114
+ this . path . replace ( / \/ $ / , '' ) . replace ( regexEscapeRE , '\\$&' ) +
102
115
'(\\/|$)'
103
116
)
104
117
: null
105
- this . updateClasses ( this . vm . $route . path )
106
- let isAbsolute = path . charAt ( 0 ) === '/'
118
+ } ,
119
+
120
+ updateHref ( ) {
121
+ if ( this . target && this . target . name ) {
122
+ this . el . href = '#' + this . target . name
123
+ return
124
+ }
125
+ const path = this . path
126
+ const router = this . router
127
+ const isAbsolute = path . charAt ( 0 ) === '/'
107
128
// do not format non-hash relative paths
108
- let href = path && ( router . mode === 'hash' || isAbsolute )
109
- ? router . history . formatPath ( path , append )
129
+ const href = path && ( router . mode === 'hash' || isAbsolute )
130
+ ? router . history . formatPath ( path , this . append )
110
131
: path
111
132
if ( this . el . tagName === 'A' ) {
112
133
if ( href ) {
@@ -118,36 +139,31 @@ export default function (Vue) {
118
139
} ,
119
140
120
141
updateClasses ( path ) {
121
- let el = this . el
122
- let router = this . vm . $route . router
123
- let activeClass = this . activeClass || router . _linkActiveClass
142
+ const el = this . el
143
+ const activeClass = this . activeClass || this . router . _linkActiveClass
124
144
// clear old class
125
145
if ( this . prevActiveClass !== activeClass ) {
126
- _ . removeClass ( el , this . prevActiveClass )
146
+ removeClass ( el , this . prevActiveClass )
127
147
}
128
148
// remove query string before matching
129
- let dest = this . path . replace ( queryStringRE , '' )
149
+ const dest = this . path . replace ( queryStringRE , '' )
130
150
path = path . replace ( queryStringRE , '' )
131
151
// add new class
132
152
if ( this . exact ) {
133
- if (
134
- dest === path ||
135
- (
136
- // also allow additional trailing slash
137
- dest . charAt ( dest . length - 1 ) !== '/' &&
138
- dest === path . replace ( trailingSlashRE , '' )
139
- )
140
- ) {
141
- _ . addClass ( el , activeClass )
153
+ if ( dest === path || (
154
+ // also allow additional trailing slash
155
+ dest . charAt ( dest . length - 1 ) !== '/' &&
156
+ dest === path . replace ( trailingSlashRE , '' )
157
+ ) ) {
158
+ addClass ( el , activeClass )
142
159
} else {
143
- _ . removeClass ( el , activeClass )
160
+ removeClass ( el , activeClass )
144
161
}
145
162
} else {
146
- if ( this . activeRE &&
147
- this . activeRE . test ( path ) ) {
148
- _ . addClass ( el , activeClass )
163
+ if ( this . activeRE && this . activeRE . test ( path ) ) {
164
+ addClass ( el , activeClass )
149
165
} else {
150
- _ . removeClass ( el , activeClass )
166
+ removeClass ( el , activeClass )
151
167
}
152
168
}
153
169
} ,
0 commit comments