1
1
/**
2
2
* State-based routing for AngularJS
3
- * @version v0.2.5
3
+ * @version v0.2.6
4
4
* @link http://angular-ui.github.com/
5
5
* @license MIT License, http://www.opensource.org/licenses/MIT
6
6
*/
@@ -57,6 +57,24 @@ function ancestors(first, second) {
57
57
return path ;
58
58
}
59
59
60
+ /**
61
+ * IE8-safe wrapper for `Object.keys()`.
62
+ *
63
+ * @param {Object } object A JavaScript object.
64
+ * @return {Array } Returns the keys of the object as an array.
65
+ */
66
+ function keys ( object ) {
67
+ if ( Object . keys ) {
68
+ return Object . keys ( object ) ;
69
+ }
70
+ var result = [ ] ;
71
+
72
+ angular . forEach ( object , function ( val , key ) {
73
+ result . push ( key ) ;
74
+ } ) ;
75
+ return result ;
76
+ }
77
+
60
78
/**
61
79
* IE8-safe wrapper for `Array.prototype.indexOf()`.
62
80
*
@@ -104,6 +122,61 @@ function inheritParams(currentParams, newParams, $current, $to) {
104
122
return extend ( { } , inherited , newParams ) ;
105
123
}
106
124
125
+ /**
126
+ * Normalizes a set of values to string or `null`, filtering them by a list of keys.
127
+ *
128
+ * @param {Array } keys The list of keys to normalize/return.
129
+ * @param {Object } values An object hash of values to normalize.
130
+ * @return {Object } Returns an object hash of normalized string values.
131
+ */
132
+ function normalize ( keys , values ) {
133
+ var normalized = { } ;
134
+
135
+ forEach ( keys , function ( name ) {
136
+ var value = values [ name ] ;
137
+ normalized [ name ] = ( value != null ) ? String ( value ) : null ;
138
+ } ) ;
139
+ return normalized ;
140
+ }
141
+
142
+ /**
143
+ * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
144
+ *
145
+ * @param {Object } a The first object.
146
+ * @param {Object } b The second object.
147
+ * @param {Array } keys The list of keys within each object to compare. If the list is empty or not specified,
148
+ * it defaults to the list of keys in `a`.
149
+ * @return {Boolean } Returns `true` if the keys match, otherwise `false`.
150
+ */
151
+ function equalForKeys ( a , b , keys ) {
152
+ if ( ! keys ) {
153
+ keys = [ ] ;
154
+ for ( var n in a ) keys . push ( n ) ; // Used instead of Object.keys() for IE8 compatibility
155
+ }
156
+
157
+ for ( var i = 0 ; i < keys . length ; i ++ ) {
158
+ var k = keys [ i ] ;
159
+ if ( a [ k ] != b [ k ] ) return false ; // Not '===', values aren't necessarily normalized
160
+ }
161
+ return true ;
162
+ }
163
+
164
+ /**
165
+ * Returns the subset of an object, based on a list of keys.
166
+ *
167
+ * @param {Array } keys
168
+ * @param {Object } values
169
+ * @return {Boolean } Returns a subset of `values`.
170
+ */
171
+ function filterByKeys ( keys , values ) {
172
+ var filtered = { } ;
173
+
174
+ forEach ( keys , function ( name ) {
175
+ filtered [ name ] = values [ name ] ;
176
+ } ) ;
177
+ return filtered ;
178
+ }
179
+
107
180
angular . module ( 'ui.router.util' , [ 'ng' ] ) ;
108
181
angular . module ( 'ui.router.router' , [ 'ui.router.util' ] ) ;
109
182
angular . module ( 'ui.router.state' , [ 'ui.router.router' , 'ui.router.util' ] ) ;
@@ -799,7 +872,7 @@ angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
799
872
$StateProvider . $inject = [ '$urlRouterProvider' , '$urlMatcherFactoryProvider' , '$locationProvider' ] ;
800
873
function $StateProvider ( $urlRouterProvider , $urlMatcherFactory , $locationProvider ) {
801
874
802
- var root , states = { } , $state , queue = { } ;
875
+ var root , states = { } , $state , queue = { } , abstractKey = 'abstract' ;
803
876
804
877
// Builds state properties from definition passed to registerState()
805
878
var stateBuilder = {
@@ -976,10 +1049,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
976
1049
states [ name ] = state ;
977
1050
978
1051
// Register the state in the global state list and with $urlRouter if necessary.
979
- if ( ! state [ 'abstract' ] && state . url ) {
1052
+ if ( ! state [ abstractKey ] && state . url ) {
980
1053
$urlRouterProvider . when ( state . url , [ '$match' , '$stateParams' , function ( $match , $stateParams ) {
981
1054
if ( $state . $current . navigable != state || ! equalForKeys ( $match , $stateParams ) ) {
982
- $state . transitionTo ( state , $match , false ) ;
1055
+ $state . transitionTo ( state , $match , { location : false } ) ;
983
1056
}
984
1057
} ] ) ;
985
1058
}
@@ -1116,7 +1189,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
1116
1189
throw new Error ( "No such state '" + to + "'" ) ;
1117
1190
}
1118
1191
}
1119
- if ( toState [ 'abstract' ] ) throw new Error ( "Cannot transition to abstract state '" + to + "'" ) ;
1192
+ if ( toState [ abstractKey ] ) throw new Error ( "Cannot transition to abstract state '" + to + "'" ) ;
1120
1193
if ( options . inherit ) toParams = inheritParams ( $stateParams , toParams || { } , $state . $current , toState ) ;
1121
1194
to = toState ;
1122
1195
@@ -1286,13 +1359,13 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
1286
1359
return url ;
1287
1360
} ;
1288
1361
1289
- $state . get = function ( stateOrName ) {
1362
+ $state . get = function ( stateOrName , context ) {
1290
1363
if ( ! isDefined ( stateOrName ) ) {
1291
1364
var list = [ ] ;
1292
1365
forEach ( states , function ( state ) { list . push ( state . self ) ; } ) ;
1293
1366
return list ;
1294
1367
}
1295
- var state = findState ( stateOrName ) ;
1368
+ var state = findState ( stateOrName , context ) ;
1296
1369
return ( state && state . self ) ? state . self : null ;
1297
1370
} ;
1298
1371
@@ -1344,39 +1417,6 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
1344
1417
return $state ;
1345
1418
}
1346
1419
1347
- function normalize ( keys , values ) {
1348
- var normalized = { } ;
1349
-
1350
- forEach ( keys , function ( name ) {
1351
- var value = values [ name ] ;
1352
- normalized [ name ] = ( value != null ) ? String ( value ) : null ;
1353
- } ) ;
1354
- return normalized ;
1355
- }
1356
-
1357
- function equalForKeys ( a , b , keys ) {
1358
- // If keys not provided, assume keys from object 'a'
1359
- if ( ! keys ) {
1360
- keys = [ ] ;
1361
- for ( var n in a ) keys . push ( n ) ; // Used instead of Object.keys() for IE8 compatibility
1362
- }
1363
-
1364
- for ( var i = 0 ; i < keys . length ; i ++ ) {
1365
- var k = keys [ i ] ;
1366
- if ( a [ k ] != b [ k ] ) return false ; // Not '===', values aren't necessarily normalized
1367
- }
1368
- return true ;
1369
- }
1370
-
1371
- function filterByKeys ( keys , values ) {
1372
- var filtered = { } ;
1373
-
1374
- forEach ( keys , function ( name ) {
1375
- filtered [ name ] = values [ name ] ;
1376
- } ) ;
1377
- return filtered ;
1378
- }
1379
-
1380
1420
function shouldTriggerReload ( to , from , locals , options ) {
1381
1421
if ( to === from && ( ( locals === from . locals && ! options . reload ) || ( to . self . reloadOnSearch === false ) ) ) {
1382
1422
return true ;
@@ -1420,9 +1460,7 @@ angular.module('ui.router.state').provider('$view', $ViewProvider);
1420
1460
1421
1461
$ViewDirective . $inject = [ '$state' , '$compile' , '$controller' , '$injector' , '$anchorScroll' ] ;
1422
1462
function $ViewDirective ( $state , $compile , $controller , $injector , $anchorScroll ) {
1423
- // TODO: Change to $injector.has() when we version bump to Angular 1.1.5.
1424
- // See: https://github.com/angular/angular.js/blob/master/CHANGELOG.md#115-triangle-squarification-2013-05-22
1425
- var $animator ; try { $animator = $injector . get ( '$animator' ) ; } catch ( e ) { /* do nothing */ }
1463
+ var $animator = $injector . has ( '$animator' ) ? $injector . get ( '$animator' ) : null ;
1426
1464
var viewIsUpdating = false ;
1427
1465
1428
1466
var directive = {
@@ -1435,7 +1473,7 @@ function $ViewDirective( $state, $compile, $controller, $injector, $an
1435
1473
var viewScope , viewLocals ,
1436
1474
name = attr [ directive . name ] || attr . name || '' ,
1437
1475
onloadExp = attr . onload || '' ,
1438
- animate = isDefined ( $animator ) && $animator ( scope , attr ) ,
1476
+ animate = $animator && $animator ( scope , attr ) ,
1439
1477
initialView = transclude ( scope ) ;
1440
1478
1441
1479
// Returns a set of DOM manipulation functions based on whether animation
@@ -1543,22 +1581,25 @@ function parseStateRef(ref) {
1543
1581
return { state : parsed [ 1 ] , paramExpr : parsed [ 3 ] || null } ;
1544
1582
}
1545
1583
1546
- $StateRefDirective . $inject = [ '$state' ] ;
1547
- function $StateRefDirective ( $state ) {
1584
+ function stateContext ( el ) {
1585
+ var stateData = el . parent ( ) . inheritedData ( '$uiView' ) ;
1586
+
1587
+ if ( stateData && stateData . state && stateData . state . name ) {
1588
+ return stateData . state ;
1589
+ }
1590
+ }
1591
+
1592
+ $StateRefDirective . $inject = [ '$state' , '$timeout' ] ;
1593
+ function $StateRefDirective ( $state , $timeout ) {
1548
1594
return {
1549
1595
restrict : 'A' ,
1550
- link : function ( scope , element , attrs ) {
1596
+ require : '?^uiSrefActive' ,
1597
+ link : function ( scope , element , attrs , uiSrefActive ) {
1551
1598
var ref = parseStateRef ( attrs . uiSref ) ;
1552
- var params = null , url = null , base = $state . $current ;
1599
+ var params = null , url = null , base = stateContext ( element ) || $state . $current ;
1553
1600
var isForm = element [ 0 ] . nodeName === "FORM" ;
1554
1601
var attr = isForm ? "action" : "href" , nav = true ;
1555
1602
1556
- var stateData = element . parent ( ) . inheritedData ( '$uiView' ) ;
1557
-
1558
- if ( stateData && stateData . state && stateData . state . name ) {
1559
- base = stateData . state ;
1560
- }
1561
-
1562
1603
var update = function ( newVal ) {
1563
1604
if ( newVal ) params = newVal ;
1564
1605
if ( ! nav ) return ;
@@ -1570,6 +1611,9 @@ function $StateRefDirective($state) {
1570
1611
return false ;
1571
1612
}
1572
1613
element [ 0 ] [ attr ] = newHref ;
1614
+ if ( uiSrefActive ) {
1615
+ uiSrefActive . $$setStateInfo ( ref . state , params ) ;
1616
+ }
1573
1617
} ;
1574
1618
1575
1619
if ( ref . paramExpr ) {
@@ -1586,8 +1630,11 @@ function $StateRefDirective($state) {
1586
1630
var button = e . which || e . button ;
1587
1631
1588
1632
if ( ( button === 0 || button == 1 ) && ! e . ctrlKey && ! e . metaKey && ! e . shiftKey ) {
1589
- scope . $evalAsync ( function ( ) {
1590
- $state . go ( ref . state , params , { relative : base } ) ;
1633
+ // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
1634
+ $timeout ( function ( ) {
1635
+ scope . $apply ( function ( ) {
1636
+ $state . go ( ref . state , params , { relative : base } ) ;
1637
+ } ) ;
1591
1638
} ) ;
1592
1639
e . preventDefault ( ) ;
1593
1640
}
@@ -1596,7 +1643,44 @@ function $StateRefDirective($state) {
1596
1643
} ;
1597
1644
}
1598
1645
1599
- angular . module ( 'ui.router.state' ) . directive ( 'uiSref' , $StateRefDirective ) ;
1646
+ $StateActiveDirective . $inject = [ '$state' , '$stateParams' , '$interpolate' ] ;
1647
+ function $StateActiveDirective ( $state , $stateParams , $interpolate ) {
1648
+ return {
1649
+ restrict : "A" ,
1650
+ controller : function ( $scope , $element , $attrs ) {
1651
+ var state , params , activeClass ;
1652
+
1653
+ // There probably isn't much point in $observing this
1654
+ activeClass = $interpolate ( $attrs . uiSrefActive || '' , false ) ( $scope ) ;
1655
+
1656
+ // Allow uiSref to communicate with uiSrefActive
1657
+ this . $$setStateInfo = function ( newState , newParams ) {
1658
+ state = $state . get ( newState , stateContext ( $element ) ) ;
1659
+ params = newParams ;
1660
+ update ( ) ;
1661
+ } ;
1662
+
1663
+ $scope . $on ( '$stateChangeSuccess' , update ) ;
1664
+
1665
+ // Update route state
1666
+ function update ( ) {
1667
+ if ( $state . $current . self === state && matchesParams ( ) ) {
1668
+ $element . addClass ( activeClass ) ;
1669
+ } else {
1670
+ $element . removeClass ( activeClass ) ;
1671
+ }
1672
+ }
1673
+
1674
+ function matchesParams ( ) {
1675
+ return ! params || equalForKeys ( params , $stateParams ) ;
1676
+ }
1677
+ }
1678
+ } ;
1679
+ }
1680
+
1681
+ angular . module ( 'ui.router.state' )
1682
+ . directive ( 'uiSref' , $StateRefDirective )
1683
+ . directive ( 'uiSrefActive' , $StateActiveDirective ) ;
1600
1684
1601
1685
$RouteProvider . $inject = [ '$stateProvider' , '$urlRouterProvider' ] ;
1602
1686
function $RouteProvider ( $stateProvider , $urlRouterProvider ) {
0 commit comments