Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
todomvc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sven Franck
todomvc
Commits
1239e6a6
Commit
1239e6a6
authored
Dec 31, 2013
by
Addy Osmani
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #751 from passy/angular-routing
AngularJS: Utilize ngRoute for filters
parents
47353c4a
723b0059
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1661 additions
and
498 deletions
+1661
-498
architecture-examples/angularjs/bower.json
architecture-examples/angularjs/bower.json
+3
-2
architecture-examples/angularjs/bower_components/angular-route/angular-route.js
...angularjs/bower_components/angular-route/angular-route.js
+911
-0
architecture-examples/angularjs/bower_components/angular/angular.js
...re-examples/angularjs/bower_components/angular/angular.js
+673
-435
architecture-examples/angularjs/index.html
architecture-examples/angularjs/index.html
+56
-51
architecture-examples/angularjs/js/app.js
architecture-examples/angularjs/js/app.js
+12
-1
architecture-examples/angularjs/js/controllers/todoCtrl.js
architecture-examples/angularjs/js/controllers/todoCtrl.js
+6
-9
No files found.
architecture-examples/angularjs/bower.json
View file @
1239e6a6
...
...
@@ -2,10 +2,11 @@
"name"
:
"todomvc-angular"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"angular"
:
"
~1.2.1
"
,
"angular"
:
"
1.2.5
"
,
"todomvc-common"
:
"~0.1.4"
},
"devDependencies"
:
{
"angular-mocks"
:
"~1.2.1"
"angular-mocks"
:
"1.2.5"
,
"angular-route"
:
"1.2.5"
}
}
architecture-examples/angularjs/bower_components/angular-route/angular-route.js
0 → 100644
View file @
1239e6a6
/**
* @license AngularJS v1.2.5
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(
function
(
window
,
angular
,
undefined
)
{
'
use strict
'
;
/**
* @ngdoc overview
* @name ngRoute
* @description
*
* # ngRoute
*
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* {@installModule route}
*
* <div doc-module-components="ngRoute"></div>
*/
/* global -ngRouteModule */
var
ngRouteModule
=
angular
.
module
(
'
ngRoute
'
,
[
'
ng
'
]).
provider
(
'
$route
'
,
$RouteProvider
);
/**
* @ngdoc object
* @name ngRoute.$routeProvider
* @function
*
* @description
*
* Used for configuring routes.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* ## Dependencies
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*/
function
$RouteProvider
(){
function
inherit
(
parent
,
extra
)
{
return
angular
.
extend
(
new
(
angular
.
extend
(
function
()
{},
{
prototype
:
parent
}))(),
extra
);
}
var
routes
=
{};
/**
* @ngdoc method
* @name ngRoute.$routeProvider#when
* @methodOf ngRoute.$routeProvider
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashs/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashs`.
*
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
*
* Object properties:
*
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
* newly created scope or the name of a {@link angular.Module#controller registered
* controller} if passed as a string.
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
* published to scope under the `controllerAs` name.
* - `template` – `{string=|function()=}` – html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
* be injected into the controller. If any of these dependencies are promises, the router
* will wait for them all to be resolved or one to be rejected before the controller is
* instantiated.
* If all the promises are resolved successfully, the values of the resolved promises are
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
* fired. If any of the promises are rejected the
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
* is:
*
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
* resolved before its value is injected into the controller. Be aware that
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
* functions. Use `$route.current.params` to access the new route parameters, instead.
*
* - `redirectTo` – {(string|function())=} – value to update
* {@link ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
*
* - `{Object.<string>}` - route parameters extracted from the current
* `$location.path()` by applying the current route templateUrl.
* - `{string}` - current `$location.path()`
* - `{Object}` - current `$location.search()`
*
* The custom `redirectTo` function is expected to return a string which will be used
* to update `$location.path()` and `$location.search()`.
*
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
* or `$location.hash()` changes.
*
* If the option is set to `false` and url in the browser changes, then
* `$routeUpdate` event is broadcasted on the root scope.
*
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
*
* If the option is set to `true`, then the particular route can be matched without being
* case sensitive
*
* @returns {Object} self
*
* @description
* Adds a new route definition to the `$route` service.
*/
this
.
when
=
function
(
path
,
route
)
{
routes
[
path
]
=
angular
.
extend
(
{
reloadOnSearch
:
true
},
route
,
path
&&
pathRegExp
(
path
,
route
)
);
// create redirection for trailing slashes
if
(
path
)
{
var
redirectPath
=
(
path
[
path
.
length
-
1
]
==
'
/
'
)
?
path
.
substr
(
0
,
path
.
length
-
1
)
:
path
+
'
/
'
;
routes
[
redirectPath
]
=
angular
.
extend
(
{
redirectTo
:
path
},
pathRegExp
(
redirectPath
,
route
)
);
}
return
this
;
};
/**
* @param path {string} path
* @param opts {Object} options
* @return {?Object}
*
* @description
* Normalizes the given path, returning a regular expression
* and the original path.
*
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
*/
function
pathRegExp
(
path
,
opts
)
{
var
insensitive
=
opts
.
caseInsensitiveMatch
,
ret
=
{
originalPath
:
path
,
regexp
:
path
},
keys
=
ret
.
keys
=
[];
path
=
path
.
replace
(
/
([
().
])
/g
,
'
\\
$1
'
)
.
replace
(
/
(\/)?
:
(\w
+
)([\?
|
\*])?
/g
,
function
(
_
,
slash
,
key
,
option
){
var
optional
=
option
===
'
?
'
?
option
:
null
;
var
star
=
option
===
'
*
'
?
option
:
null
;
keys
.
push
({
name
:
key
,
optional
:
!!
optional
});
slash
=
slash
||
''
;
return
''
+
(
optional
?
''
:
slash
)
+
'
(?:
'
+
(
optional
?
slash
:
''
)
+
(
star
&&
'
(.+?)
'
||
'
([^/]+)
'
)
+
(
optional
||
''
)
+
'
)
'
+
(
optional
||
''
);
})
.
replace
(
/
([\/
$
\*])
/g
,
'
\\
$1
'
);
ret
.
regexp
=
new
RegExp
(
'
^
'
+
path
+
'
$
'
,
insensitive
?
'
i
'
:
''
);
return
ret
;
}
/**
* @ngdoc method
* @name ngRoute.$routeProvider#otherwise
* @methodOf ngRoute.$routeProvider
*
* @description
* Sets route definition that will be used on route change when no other route definition
* is matched.
*
* @param {Object} params Mapping information to be assigned to `$route.current`.
* @returns {Object} self
*/
this
.
otherwise
=
function
(
params
)
{
this
.
when
(
null
,
params
);
return
this
;
};
this
.
$get
=
[
'
$rootScope
'
,
'
$location
'
,
'
$routeParams
'
,
'
$q
'
,
'
$injector
'
,
'
$http
'
,
'
$templateCache
'
,
'
$sce
'
,
function
(
$rootScope
,
$location
,
$routeParams
,
$q
,
$injector
,
$http
,
$templateCache
,
$sce
)
{
/**
* @ngdoc object
* @name ngRoute.$route
* @requires $location
* @requires $routeParams
*
* @property {Object} current Reference to the current route definition.
* The route definition contains:
*
* - `controller`: The controller constructor as define in route definition.
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
* controller instantiation. The `locals` contain
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
*
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Array.<Object>} routes Array of all configured routes.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
* It watches `$location.url()` and tries to map the path to an existing route definition.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
*
* The `$route` service is typically used in conjunction with the
* {@link ngRoute.directive:ngView `ngView`} directive and the
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
This example shows how changing the URL hash causes the `$route` to match a route against the
URL, and the `ngView` pulls in the partial.
Note that this example is using {@link ng.directive:script inlined templates}
to get it working on jsfiddle as well.
<example module="ngViewExample" deps="angular-route.js">
<file name="index.html">
<div ng-controller="MainCntl">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div ng-view></div>
<hr />
<pre>$location.path() = {{$location.path()}}</pre>
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{$route.current.params}}</pre>
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
<pre>$routeParams = {{$routeParams}}</pre>
</div>
</file>
<file name="book.html">
controller: {{name}}<br />
Book Id: {{params.bookId}}<br />
</file>
<file name="chapter.html">
controller: {{name}}<br />
Book Id: {{params.bookId}}<br />
Chapter Id: {{params.chapterId}}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: BookCntl,
resolve: {
// I will cause a 1 second delay
delay: function($q, $timeout) {
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: ChapterCntl
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
function MainCntl($scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
}
function BookCntl($scope, $routeParams) {
$scope.name = "BookCntl";
$scope.params = $routeParams;
}
function ChapterCntl($scope, $routeParams) {
$scope.name = "ChapterCntl";
$scope.params = $routeParams;
}
</file>
<file name="scenario.js">
it('should load and compile correct template', function() {
element('a:contains("Moby: Ch1")').click();
var content = element('.doc-example-live [ng-view]').text();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element('a:contains("Scarlet")').click();
sleep(2); // promises are not part of scenario waiting
content = element('.doc-example-live [ng-view]').text();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeStart
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
* resolving all of the dependencies needed for the route change to occurs.
* Typically this involves fetching the view template as well as any dependencies
* defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} next Future route information.
* @param {Route} current Current route information.
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeSuccess
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
* {@link ngRoute.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} current Current route information.
* @param {Route|Undefined} previous Previous route information, or undefined if current is
* first route entered.
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeChangeError
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
* Broadcasted if any of the resolve promises are rejected.
*
* @param {Object} angularEvent Synthetic event object
* @param {Route} current Current route information.
* @param {Route} previous Previous route information.
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
*/
/**
* @ngdoc event
* @name ngRoute.$route#$routeUpdate
* @eventOf ngRoute.$route
* @eventType broadcast on root scope
* @description
*
* The `reloadOnSearch` property has been set to false, and we are reusing the same
* instance of the Controller.
*/
var
forceReload
=
false
,
$route
=
{
routes
:
routes
,
/**
* @ngdoc method
* @name ngRoute.$route#reload
* @methodOf ngRoute.$route
*
* @description
* Causes `$route` service to reload the current route even if
* {@link ng.$location $location} hasn't changed.
*
* As a result of that, {@link ngRoute.directive:ngView ngView}
* creates new scope, reinstantiates the controller.
*/
reload
:
function
()
{
forceReload
=
true
;
$rootScope
.
$evalAsync
(
updateRoute
);
}
};
$rootScope
.
$on
(
'
$locationChangeSuccess
'
,
updateRoute
);
return
$route
;
/////////////////////////////////////////////////////
/**
* @param on {string} current url
* @param route {Object} route regexp to match the url against
* @return {?Object}
*
* @description
* Check if the route matches the current url.
*
* Inspired by match in
* visionmedia/express/lib/router/router.js.
*/
function
switchRouteMatcher
(
on
,
route
)
{
var
keys
=
route
.
keys
,
params
=
{};
if
(
!
route
.
regexp
)
return
null
;
var
m
=
route
.
regexp
.
exec
(
on
);
if
(
!
m
)
return
null
;
for
(
var
i
=
1
,
len
=
m
.
length
;
i
<
len
;
++
i
)
{
var
key
=
keys
[
i
-
1
];
var
val
=
'
string
'
==
typeof
m
[
i
]
?
decodeURIComponent
(
m
[
i
])
:
m
[
i
];
if
(
key
&&
val
)
{
params
[
key
.
name
]
=
val
;
}
}
return
params
;
}
function
updateRoute
()
{
var
next
=
parseRoute
(),
last
=
$route
.
current
;
if
(
next
&&
last
&&
next
.
$$route
===
last
.
$$route
&&
angular
.
equals
(
next
.
pathParams
,
last
.
pathParams
)
&&
!
next
.
reloadOnSearch
&&
!
forceReload
)
{
last
.
params
=
next
.
params
;
angular
.
copy
(
last
.
params
,
$routeParams
);
$rootScope
.
$broadcast
(
'
$routeUpdate
'
,
last
);
}
else
if
(
next
||
last
)
{
forceReload
=
false
;
$rootScope
.
$broadcast
(
'
$routeChangeStart
'
,
next
,
last
);
$route
.
current
=
next
;
if
(
next
)
{
if
(
next
.
redirectTo
)
{
if
(
angular
.
isString
(
next
.
redirectTo
))
{
$location
.
path
(
interpolate
(
next
.
redirectTo
,
next
.
params
)).
search
(
next
.
params
)
.
replace
();
}
else
{
$location
.
url
(
next
.
redirectTo
(
next
.
pathParams
,
$location
.
path
(),
$location
.
search
()))
.
replace
();
}
}
}
$q
.
when
(
next
).
then
(
function
()
{
if
(
next
)
{
var
locals
=
angular
.
extend
({},
next
.
resolve
),
template
,
templateUrl
;
angular
.
forEach
(
locals
,
function
(
value
,
key
)
{
locals
[
key
]
=
angular
.
isString
(
value
)
?
$injector
.
get
(
value
)
:
$injector
.
invoke
(
value
);
});
if
(
angular
.
isDefined
(
template
=
next
.
template
))
{
if
(
angular
.
isFunction
(
template
))
{
template
=
template
(
next
.
params
);
}
}
else
if
(
angular
.
isDefined
(
templateUrl
=
next
.
templateUrl
))
{
if
(
angular
.
isFunction
(
templateUrl
))
{
templateUrl
=
templateUrl
(
next
.
params
);
}
templateUrl
=
$sce
.
getTrustedResourceUrl
(
templateUrl
);
if
(
angular
.
isDefined
(
templateUrl
))
{
next
.
loadedTemplateUrl
=
templateUrl
;
template
=
$http
.
get
(
templateUrl
,
{
cache
:
$templateCache
}).
then
(
function
(
response
)
{
return
response
.
data
;
});
}
}
if
(
angular
.
isDefined
(
template
))
{
locals
[
'
$template
'
]
=
template
;
}
return
$q
.
all
(
locals
);
}
}).
// after route change
then
(
function
(
locals
)
{
if
(
next
==
$route
.
current
)
{
if
(
next
)
{
next
.
locals
=
locals
;
angular
.
copy
(
next
.
params
,
$routeParams
);
}
$rootScope
.
$broadcast
(
'
$routeChangeSuccess
'
,
next
,
last
);
}
},
function
(
error
)
{
if
(
next
==
$route
.
current
)
{
$rootScope
.
$broadcast
(
'
$routeChangeError
'
,
next
,
last
,
error
);
}
});
}
}
/**
* @returns the current active route, by matching it against the URL
*/
function
parseRoute
()
{
// Match a route
var
params
,
match
;
angular
.
forEach
(
routes
,
function
(
route
,
path
)
{
if
(
!
match
&&
(
params
=
switchRouteMatcher
(
$location
.
path
(),
route
)))
{
match
=
inherit
(
route
,
{
params
:
angular
.
extend
({},
$location
.
search
(),
params
),
pathParams
:
params
});
match
.
$$route
=
route
;
}
});
// No route matched; fallback to "otherwise" route
return
match
||
routes
[
null
]
&&
inherit
(
routes
[
null
],
{
params
:
{},
pathParams
:{}});
}
/**
* @returns interpolation of the redirect path with the parameters
*/
function
interpolate
(
string
,
params
)
{
var
result
=
[];
angular
.
forEach
((
string
||
''
).
split
(
'
:
'
),
function
(
segment
,
i
)
{
if
(
i
===
0
)
{
result
.
push
(
segment
);
}
else
{
var
segmentMatch
=
segment
.
match
(
/
(\w
+
)(
.*
)
/
);
var
key
=
segmentMatch
[
1
];
result
.
push
(
params
[
key
]);
result
.
push
(
segmentMatch
[
2
]
||
''
);
delete
params
[
key
];
}
});
return
result
.
join
(
''
);
}
}];
}
ngRouteModule
.
provider
(
'
$routeParams
'
,
$RouteParamsProvider
);
/**
* @ngdoc object
* @name ngRoute.$routeParams
* @requires $route
*
* @description
* The `$routeParams` service allows you to retrieve the current set of route parameters.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* The route parameters are a combination of {@link ng.$location `$location`}'s
* {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
*
* In case of parameter name collision, `path` params take precedence over `search` params.
*
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
* (but its properties will likely change) even when a route change occurs.
*
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
* Instead you can use `$route.current.params` to access the new route's parameters.
*
* @example
* <pre>
* // Given:
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
* // Route: /Chapter/:chapterId/Section/:sectionId
* //
* // Then
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
* </pre>
*/
function
$RouteParamsProvider
()
{
this
.
$get
=
function
()
{
return
{};
};
}
ngRouteModule
.
directive
(
'
ngView
'
,
ngViewFactory
);
ngRouteModule
.
directive
(
'
ngView
'
,
ngViewFillContentFactory
);
/**
* @ngdoc directive
* @name ngRoute.directive:ngView
* @restrict ECA
*
* @description
* # Overview
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
* including the rendered template of the current route into the main layout (`index.html`) file.
* Every time the current route changes, the included view changes with it according to the
* configuration of the `$route` service.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* @animations
* enter - animation is used to bring new content into the browser.
* leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
* @scope
* @priority 400
* @example
<example module="ngViewExample" deps="angular-route.js" animations="true">
<file name="index.html">
<div ng-controller="MainCntl as main">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div class="view-animate-container">
<div ng-view class="view-animate"></div>
</div>
<hr />
<pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
<file name="book.html">
<div>
controller: {{book.name}}<br />
Book Id: {{book.params.bookId}}<br />
</div>
</file>
<file name="chapter.html">
<div>
controller: {{chapter.name}}<br />
Book Id: {{chapter.params.bookId}}<br />
Chapter Id: {{chapter.params.chapterId}}
</div>
</file>
<file name="animations.css">
.view-animate-container {
position:relative;
height:100px!important;
position:relative;
background:white;
border:1px solid black;
height:40px;
overflow:hidden;
}
.view-animate {
padding:10px;
}
.view-animate.ng-enter, .view-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
display:block;
width:100%;
border-left:1px solid black;
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
padding:10px;
}
.view-animate.ng-enter {
left:100%;
}
.view-animate.ng-enter.ng-enter-active {
left:0;
}
.view-animate.ng-leave.ng-leave-active {
left:-100%;
}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: BookCntl,
controllerAs: 'book'
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: ChapterCntl,
controllerAs: 'chapter'
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
function MainCntl($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}
function BookCntl($routeParams) {
this.name = "BookCntl";
this.params = $routeParams;
}
function ChapterCntl($routeParams) {
this.name = "ChapterCntl";
this.params = $routeParams;
}
</file>
<file name="scenario.js">
it('should load and compile correct template', function() {
element('a:contains("Moby: Ch1")').click();
var content = element('.doc-example-live [ng-view]').text();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element('a:contains("Scarlet")').click();
content = element('.doc-example-live [ng-view]').text();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngRoute.directive:ngView#$viewContentLoaded
* @eventOf ngRoute.directive:ngView
* @eventType emit on the current ngView scope
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory
.
$inject
=
[
'
$route
'
,
'
$anchorScroll
'
,
'
$animate
'
];
function
ngViewFactory
(
$route
,
$anchorScroll
,
$animate
)
{
return
{
restrict
:
'
ECA
'
,
terminal
:
true
,
priority
:
400
,
transclude
:
'
element
'
,
link
:
function
(
scope
,
$element
,
attr
,
ctrl
,
$transclude
)
{
var
currentScope
,
currentElement
,
autoScrollExp
=
attr
.
autoscroll
,
onloadExp
=
attr
.
onload
||
''
;
scope
.
$on
(
'
$routeChangeSuccess
'
,
update
);
update
();
function
cleanupLastView
()
{
if
(
currentScope
)
{
currentScope
.
$destroy
();
currentScope
=
null
;
}
if
(
currentElement
)
{
$animate
.
leave
(
currentElement
);
currentElement
=
null
;
}
}
function
update
()
{
var
locals
=
$route
.
current
&&
$route
.
current
.
locals
,
template
=
locals
&&
locals
.
$template
;
if
(
template
)
{
var
newScope
=
scope
.
$new
();
var
current
=
$route
.
current
;
// Note: This will also link all children of ng-view that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-view on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var
clone
=
$transclude
(
newScope
,
function
(
clone
)
{
$animate
.
enter
(
clone
,
null
,
currentElement
||
$element
,
function
onNgViewEnter
()
{
if
(
angular
.
isDefined
(
autoScrollExp
)
&&
(
!
autoScrollExp
||
scope
.
$eval
(
autoScrollExp
)))
{
$anchorScroll
();
}
});
cleanupLastView
();
});
currentElement
=
clone
;
currentScope
=
current
.
scope
=
newScope
;
currentScope
.
$emit
(
'
$viewContentLoaded
'
);
currentScope
.
$eval
(
onloadExp
);
}
else
{
cleanupLastView
();
}
}
}
};
}
// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory
.
$inject
=
[
'
$compile
'
,
'
$controller
'
,
'
$route
'
];
function
ngViewFillContentFactory
(
$compile
,
$controller
,
$route
)
{
return
{
restrict
:
'
ECA
'
,
priority
:
-
400
,
link
:
function
(
scope
,
$element
)
{
var
current
=
$route
.
current
,
locals
=
current
.
locals
;
$element
.
html
(
locals
.
$template
);
var
link
=
$compile
(
$element
.
contents
());
if
(
current
.
controller
)
{
locals
.
$scope
=
scope
;
var
controller
=
$controller
(
current
.
controller
,
locals
);
if
(
current
.
controllerAs
)
{
scope
[
current
.
controllerAs
]
=
controller
;
}
$element
.
data
(
'
$ngControllerController
'
,
controller
);
$element
.
children
().
data
(
'
$ngControllerController
'
,
controller
);
}
link
(
scope
);
}
};
}
})(
window
,
window
.
angular
);
architecture-examples/angularjs/bower_components/angular/angular.js
View file @
1239e6a6
/**
* @license AngularJS v1.2.
1
* (c) 2010-201
2
Google, Inc. http://angularjs.org
* @license AngularJS v1.2.
5
* (c) 2010-201
4
Google, Inc. http://angularjs.org
* License: MIT
*/
(
function
(
window
,
document
,
undefined
)
{
'
use strict
'
;
...
...
@@ -68,7 +68,7 @@ function minErr(module) {
return
match
;
});
message
=
message
+
'
\n
http://errors.angularjs.org/1.2.
1
/
'
+
message
=
message
+
'
\n
http://errors.angularjs.org/1.2.
5
/
'
+
(
module
?
module
+
'
/
'
:
''
)
+
code
;
for
(
i
=
2
;
i
<
arguments
.
length
;
i
++
)
{
message
=
message
+
(
i
==
2
?
'
?
'
:
'
&
'
)
+
'
p
'
+
(
i
-
2
)
+
'
=
'
+
...
...
@@ -159,7 +159,7 @@ function minErr(module) {
-assertArgFn,
-assertNotHasOwnProperty,
-getter,
-getBlockElements
-getBlockElements
,
*/
...
...
@@ -472,7 +472,7 @@ function valueFn(value) {return function() {return value;};}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is undefined.
*/
function
isUndefined
(
value
){
return
typeof
value
==
'
undefined
'
;}
function
isUndefined
(
value
){
return
typeof
value
==
=
'
undefined
'
;}
/**
...
...
@@ -486,7 +486,7 @@ function isUndefined(value){return typeof value == 'undefined';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is defined.
*/
function
isDefined
(
value
){
return
typeof
value
!=
'
undefined
'
;}
function
isDefined
(
value
){
return
typeof
value
!=
=
'
undefined
'
;}
/**
...
...
@@ -501,7 +501,7 @@ function isDefined(value){return typeof value != 'undefined';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Object` but not `null`.
*/
function
isObject
(
value
){
return
value
!=
null
&&
typeof
value
==
'
object
'
;}
function
isObject
(
value
){
return
value
!=
null
&&
typeof
value
==
=
'
object
'
;}
/**
...
...
@@ -515,7 +515,7 @@ function isObject(value){return value != null && typeof value == 'object';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `String`.
*/
function
isString
(
value
){
return
typeof
value
==
'
string
'
;}
function
isString
(
value
){
return
typeof
value
==
=
'
string
'
;}
/**
...
...
@@ -529,7 +529,7 @@ function isString(value){return typeof value == 'string';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Number`.
*/
function
isNumber
(
value
){
return
typeof
value
==
'
number
'
;}
function
isNumber
(
value
){
return
typeof
value
==
=
'
number
'
;}
/**
...
...
@@ -544,7 +544,7 @@ function isNumber(value){return typeof value == 'number';}
* @returns {boolean} True if `value` is a `Date`.
*/
function
isDate
(
value
){
return
toString
.
apply
(
value
)
==
'
[object Date]
'
;
return
toString
.
call
(
value
)
=
==
'
[object Date]
'
;
}
...
...
@@ -560,7 +560,7 @@ function isDate(value){
* @returns {boolean} True if `value` is an `Array`.
*/
function
isArray
(
value
)
{
return
toString
.
apply
(
value
)
==
'
[object Array]
'
;
return
toString
.
call
(
value
)
=
==
'
[object Array]
'
;
}
...
...
@@ -575,7 +575,7 @@ function isArray(value) {
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Function`.
*/
function
isFunction
(
value
){
return
typeof
value
==
'
function
'
;}
function
isFunction
(
value
){
return
typeof
value
==
=
'
function
'
;}
/**
...
...
@@ -586,7 +586,7 @@ function isFunction(value){return typeof value == 'function';}
* @returns {boolean} True if `value` is a `RegExp`.
*/
function
isRegExp
(
value
)
{
return
toString
.
apply
(
value
)
==
'
[object RegExp]
'
;
return
toString
.
call
(
value
)
=
==
'
[object RegExp]
'
;
}
...
...
@@ -608,12 +608,12 @@ function isScope(obj) {
function
isFile
(
obj
)
{
return
toString
.
apply
(
obj
)
===
'
[object File]
'
;
return
toString
.
call
(
obj
)
===
'
[object File]
'
;
}
function
isBoolean
(
value
)
{
return
typeof
value
==
'
boolean
'
;
return
typeof
value
==
=
'
boolean
'
;
}
...
...
@@ -623,7 +623,7 @@ var trim = (function() {
// TODO: we should move this into IE/ES5 polyfill
if
(
!
String
.
prototype
.
trim
)
{
return
function
(
value
)
{
return
isString
(
value
)
?
value
.
replace
(
/^
\s
*/
,
''
).
replace
(
/
\s
*$/
,
''
)
:
value
;
return
isString
(
value
)
?
value
.
replace
(
/^
\s
\s
*/
,
''
).
replace
(
/
\s
\s
*$/
,
''
)
:
value
;
};
}
return
function
(
value
)
{
...
...
@@ -644,9 +644,9 @@ var trim = (function() {
* @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
*/
function
isElement
(
node
)
{
return
node
&&
return
!!
(
node
&&
(
node
.
nodeName
// we are a direct element
||
(
node
.
on
&&
node
.
find
));
// we have an on and find method part of jQuery API
||
(
node
.
on
&&
node
.
find
))
)
;
// we have an on and find method part of jQuery API
}
/**
...
...
@@ -717,7 +717,7 @@ function includes(array, obj) {
function
indexOf
(
array
,
obj
)
{
if
(
array
.
indexOf
)
return
array
.
indexOf
(
obj
);
for
(
var
i
=
0
;
i
<
array
.
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
array
.
length
;
i
++
)
{
if
(
obj
===
array
[
i
])
return
i
;
}
return
-
1
;
...
...
@@ -847,7 +847,7 @@ function shallowCopy(src, dst) {
for
(
var
key
in
src
)
{
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
// so we don't need to worry hasOwnProperty here
// so we don't need to worry
about using our custom
hasOwnProperty here
if
(
src
.
hasOwnProperty
(
key
)
&&
key
.
substr
(
0
,
2
)
!==
'
$$
'
)
{
dst
[
key
]
=
src
[
key
];
}
...
...
@@ -1053,7 +1053,7 @@ function startingTag(element) {
try
{
// turns out IE does not let you set .html() on elements which
// are not allowed to have children. So we just ignore it.
element
.
html
(
''
);
element
.
empty
(
);
}
catch
(
e
)
{}
// As Per DOM Standards
var
TEXT_NODE
=
3
;
...
...
@@ -1181,26 +1181,38 @@ function encodeUriQuery(val, pctEncodeSpaces) {
*
* @description
*
* Use this directive to auto-bootstrap an application. Only
* one ngApp directive can be used per HTML document. The directive
* designates the root of the application and is typically placed
* at the root of the page.
*
* The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in
* an HTML document you must manually bootstrap them using {@link angular.bootstrap}.
* Applications cannot be nested.
*
* In the example below if the `ngApp` directive were not placed
* on the `html` element then the document would not be compiled
* and the `{{ 1+2 }}` would not be resolved to `3`.
*
* `ngApp` is the easiest way to bootstrap an application.
*
<doc:example>
<doc:source>
I can add: 1 + 2 = {{ 1+2 }}
</doc:source>
</doc:example>
* Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
* designates the **root element** of the application and is typically placed near the root element
* of the page - e.g. on the `<body>` or `<html>` tags.
*
* Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
* found in the document will be used to define the root element to auto-bootstrap as an
* application. To run multiple applications in an HTML document you must manually bootstrap them using
* {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
*
* You can specify an **AngularJS module** to be used as the root module for the application. This
* module will be loaded into the {@link AUTO.$injector} when the application is bootstrapped and
* should contain the application code needed or have dependencies on other modules that will
* contain the code. See {@link angular.module} for more information.
*
* In the example below if the `ngApp` directive were not placed on the `html` element then the
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
* would not be resolved to `3`.
*
* `ngApp` is the easiest, and most common, way to bootstrap an application.
*
<example module="ngAppDemo">
<file name="index.html">
<div ng-controller="ngAppDemoController">
I can add: {{a}} + {{b}} = {{ a+b }}
</file>
<file name="script.js">
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
$scope.a = 1;
$scope.b = 2;
});
</file>
</example>
*
*/
function
angularInit
(
element
,
bootstrap
)
{
...
...
@@ -1397,23 +1409,25 @@ function getter(obj, path, bindFnToScope) {
}
/**
* Return the
siblings between `startNode` and `endNode`, inclusive
* @param {
Object} object with `startNode` and `endNode` properties
* Return the
DOM siblings between the first and last node in the given array.
* @param {
Array} array like object
* @returns jQlite object containing the elements
*/
function
getBlockElements
(
block
)
{
if
(
block
.
startNode
===
block
.
endNode
)
{
return
jqLite
(
block
.
startNode
);
function
getBlockElements
(
nodes
)
{
var
startNode
=
nodes
[
0
],
endNode
=
nodes
[
nodes
.
length
-
1
];
if
(
startNode
===
endNode
)
{
return
jqLite
(
startNode
);
}
var
element
=
block
.
startNode
;
var
element
=
startNode
;
var
elements
=
[
element
];
do
{
element
=
element
.
nextSibling
;
if
(
!
element
)
break
;
elements
.
push
(
element
);
}
while
(
element
!==
block
.
endNode
);
}
while
(
element
!==
endNode
);
return
jqLite
(
elements
);
}
...
...
@@ -1435,7 +1449,12 @@ function setupModuleLoader(window) {
return
obj
[
name
]
||
(
obj
[
name
]
=
factory
());
}
return
ensure
(
ensure
(
window
,
'
angular
'
,
Object
),
'
module
'
,
function
()
{
var
angular
=
ensure
(
window
,
'
angular
'
,
Object
);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular
.
$$minErr
=
angular
.
$$minErr
||
minErr
;
return
ensure
(
angular
,
'
module
'
,
function
()
{
/** @type {Object.<string, angular.Module>} */
var
modules
=
{};
...
...
@@ -1748,6 +1767,7 @@ function setupModuleLoader(window) {
ngHideDirective,
ngIfDirective,
ngIncludeDirective,
ngIncludeFillContentDirective,
ngInitDirective,
ngNonBindableDirective,
ngPluralizeDirective,
...
...
@@ -1785,6 +1805,7 @@ function setupModuleLoader(window) {
$ParseProvider,
$RootScopeProvider,
$QProvider,
$$SanitizeUriProvider,
$SceProvider,
$SceDelegateProvider,
$SnifferProvider,
...
...
@@ -1808,11 +1829,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var
version
=
{
full
:
'
1.2.
1
'
,
// all of these placeholder strings will be replaced by grunt's
full
:
'
1.2.
5
'
,
// all of these placeholder strings will be replaced by grunt's
major
:
1
,
// package task
minor
:
2
,
dot
:
1
,
codeName
:
'
underscore-empathy
'
dot
:
5
,
codeName
:
'
singularity-expansion
'
};
...
...
@@ -1856,6 +1877,10 @@ function publishExternalAPI(angular){
angularModule
(
'
ng
'
,
[
'
ngLocale
'
],
[
'
$provide
'
,
function
ngModule
(
$provide
)
{
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
$provide
.
provider
({
$
$sanitizeUri
:
$$SanitizeUriProvider
});
$provide
.
provider
(
'
$compile
'
,
$CompileProvider
).
directive
({
a
:
htmlAnchorDirective
,
...
...
@@ -1896,6 +1921,9 @@ function publishExternalAPI(angular){
ngRequired
:
requiredDirective
,
ngValue
:
ngValueDirective
}).
directive
({
ngInclude
:
ngIncludeFillContentDirective
}).
directive
(
ngAttributeAliasDirectives
).
directive
(
ngEventDirectives
);
$provide
.
provider
({
...
...
@@ -1973,6 +2001,7 @@ function publishExternalAPI(angular){
* - [`contents()`](http://api.jquery.com/contents/)
* - [`css()`](http://api.jquery.com/css/)
* - [`data()`](http://api.jquery.com/data/)
* - [`empty()`](http://api.jquery.com/empty/)
* - [`eq()`](http://api.jquery.com/eq/)
* - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [`hasClass()`](http://api.jquery.com/hasClass/)
...
...
@@ -2285,6 +2314,15 @@ function jqLiteInheritedData(element, name, value) {
}
}
function
jqLiteEmpty
(
element
)
{
for
(
var
i
=
0
,
childNodes
=
element
.
childNodes
;
i
<
childNodes
.
length
;
i
++
)
{
jqLiteDealoc
(
childNodes
[
i
]);
}
while
(
element
.
firstChild
)
{
element
.
removeChild
(
element
.
firstChild
);
}
}
//////////////////////////////////////////
// Functions which are declared directly.
//////////////////////////////////////////
...
...
@@ -2479,7 +2517,9 @@ forEach({
jqLiteDealoc
(
childNodes
[
i
]);
}
element
.
innerHTML
=
value
;
}
},
empty
:
jqLiteEmpty
},
function
(
fn
,
name
){
/**
* Properties: writes return selection, reads return first value
...
...
@@ -2489,11 +2529,13 @@ forEach({
// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
// in a way that survives minification.
if
(((
fn
.
length
==
2
&&
(
fn
!==
jqLiteHasClass
&&
fn
!==
jqLiteController
))
?
arg1
:
arg2
)
===
undefined
)
{
// jqLiteEmpty takes no arguments but is a setter.
if
(
fn
!==
jqLiteEmpty
&&
(((
fn
.
length
==
2
&&
(
fn
!==
jqLiteHasClass
&&
fn
!==
jqLiteController
))
?
arg1
:
arg2
)
===
undefined
))
{
if
(
isObject
(
arg1
))
{
// we are a write, but the object properties are the key/values
for
(
i
=
0
;
i
<
this
.
length
;
i
++
)
{
for
(
i
=
0
;
i
<
this
.
length
;
i
++
)
{
if
(
fn
===
jqLiteData
)
{
// data() takes the whole object in jQuery
fn
(
this
[
i
],
arg1
);
...
...
@@ -2518,7 +2560,7 @@ forEach({
}
}
else
{
// we are a write, so apply to all children
for
(
i
=
0
;
i
<
this
.
length
;
i
++
)
{
for
(
i
=
0
;
i
<
this
.
length
;
i
++
)
{
fn
(
this
[
i
],
arg1
,
arg2
);
}
// return self for chaining
...
...
@@ -2749,7 +2791,11 @@ forEach({
},
find
:
function
(
element
,
selector
)
{
return
element
.
getElementsByTagName
(
selector
);
if
(
element
.
getElementsByTagName
)
{
return
element
.
getElementsByTagName
(
selector
);
}
else
{
return
[];
}
},
clone
:
jqLiteClone
,
...
...
@@ -2885,6 +2931,28 @@ HashMap.prototype = {
* $rootScope.$digest();
* });
* </pre>
*
* Sometimes you want to get access to the injector of a currently running Angular app
* from outside Angular. Perhaps, you want to inject and compile some markup after the
* application has been bootstrapped. You can do this using extra `injector()` added
* to JQuery/jqLite elements. See {@link angular.element}.
*
* *This is fairly rare but could be the case if a third party library is injecting the
* markup.*
*
* In the following example a new block of HTML containing a `ng-controller`
* directive is added to the end of the document body by JQuery. We then compile and link
* it into the current AngularJS scope.
*
* <pre>
* var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
* $(document.body).append($div);
*
* angular.element(document).injector().invoke(function($compile) {
* var scope = angular.element($div).scope();
* $compile($div)(scope);
* });
* </pre>
*/
...
...
@@ -3079,7 +3147,7 @@ function annotate(fn) {
* // ...
* }
* // Define function dependencies
* MyController
.$inject
= ['$scope', '$route'];
* MyController
['$inject']
= ['$scope', '$route'];
*
* // Then
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
...
...
@@ -3359,11 +3427,11 @@ function annotate(fn) {
* @example
* Here are some examples of creating value services.
* <pre>
* $provide.
constant
('ADMIN_USER', 'admin');
* $provide.
value
('ADMIN_USER', 'admin');
*
* $provide.
constant
('RoleLookup', { admin: 0, writer: 1, reader: 2 });
* $provide.
value
('RoleLookup', { admin: 0, writer: 1, reader: 2 });
*
* $provide.
constant
('halfOf', function(value) {
* $provide.
value
('halfOf', function(value) {
* return value / 2;
* });
* </pre>
...
...
@@ -3605,24 +3673,9 @@ function createInjector(modulesToLoad) {
fn
=
fn
[
length
];
}
// Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
switch
(
self
?
-
1
:
args
.
length
)
{
case
0
:
return
fn
();
case
1
:
return
fn
(
args
[
0
]);
case
2
:
return
fn
(
args
[
0
],
args
[
1
]);
case
3
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
]);
case
4
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
]);
case
5
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
],
args
[
4
]);
case
6
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
],
args
[
4
],
args
[
5
]);
case
7
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
],
args
[
4
],
args
[
5
],
args
[
6
]);
case
8
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
],
args
[
4
],
args
[
5
],
args
[
6
],
args
[
7
]);
case
9
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
],
args
[
4
],
args
[
5
],
args
[
6
],
args
[
7
],
args
[
8
]);
case
10
:
return
fn
(
args
[
0
],
args
[
1
],
args
[
2
],
args
[
3
],
args
[
4
],
args
[
5
],
args
[
6
],
args
[
7
],
args
[
8
],
args
[
9
]);
default
:
return
fn
.
apply
(
self
,
args
);
}
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return
fn
.
apply
(
self
,
args
);
}
function
instantiate
(
Type
,
locals
)
{
...
...
@@ -3849,13 +3902,14 @@ var $AnimateProvider = ['$provide', function($provide) {
* inserted into the DOM
*/
enter
:
function
(
element
,
parent
,
after
,
done
)
{
var
afterNode
=
after
&&
after
[
after
.
length
-
1
];
var
parentNode
=
parent
&&
parent
[
0
]
||
afterNode
&&
afterNode
.
parentNode
;
// IE does not like undefined so we have to pass null.
var
afterNextSibling
=
(
afterNode
&&
afterNode
.
nextSibling
)
||
null
;
forEach
(
element
,
function
(
node
)
{
parentNode
.
insertBefore
(
node
,
afterNextSibling
);
});
if
(
after
)
{
after
.
after
(
element
);
}
else
{
if
(
!
parent
||
!
parent
[
0
])
{
parent
=
after
.
parent
();
}
parent
.
append
(
element
);
}
done
&&
$timeout
(
done
,
0
,
false
);
},
...
...
@@ -4780,7 +4834,7 @@ function $TemplateCacheProvider() {
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
* * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
* * `?^` - Attempt to locate the required controller by searching the element's parentsor pass `null` to the
* * `?^` - Attempt to locate the required controller by searching the element's parents
or pass `null` to the
* `link` fn if not found.
*
*
...
...
@@ -4871,7 +4925,7 @@ function $TemplateCacheProvider() {
* </div>
*
* <div class="alert alert-error">
* **Note:** The `transclude` function that is passed to the compile function is dep
e
recated, as it
* **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
* e.g. does not know about the right outer scope. Please use the transclude function that is passed
* to the link function instead.
* </div>
...
...
@@ -5081,14 +5135,12 @@ var $compileMinErr = minErr('$compile');
*
* @description
*/
$CompileProvider
.
$inject
=
[
'
$provide
'
];
function
$CompileProvider
(
$provide
)
{
$CompileProvider
.
$inject
=
[
'
$provide
'
,
'
$$sanitizeUriProvider
'
];
function
$CompileProvider
(
$provide
,
$$sanitizeUriProvider
)
{
var
hasDirectives
=
{},
Suffix
=
'
Directive
'
,
COMMENT_DIRECTIVE_REGEXP
=
/^
\s
*directive
\:\s
*
([\d\w\-
_
]
+
)\s
+
(
.*
)
$/
,
CLASS_DIRECTIVE_REGEXP
=
/
(([\d\w\-
_
]
+
)(?:\:([^
;
]
+
))?
;
?)
/
,
aHrefSanitizationWhitelist
=
/^
\s
*
(
https
?
|ftp|mailto|tel|file
)
:/
,
imgSrcSanitizationWhitelist
=
/^
\s
*
(
https
?
|ftp|file
)
:|data:image
\/
/
;
CLASS_DIRECTIVE_REGEXP
=
/
(([\d\w\-
_
]
+
)(?:\:([^
;
]
+
))?
;
?)
/
;
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// The assumption is that future DOM event attribute names will begin with
...
...
@@ -5172,10 +5224,11 @@ function $CompileProvider($provide) {
*/
this
.
aHrefSanitizationWhitelist
=
function
(
regexp
)
{
if
(
isDefined
(
regexp
))
{
aHrefSanitizationWhitelist
=
regexp
;
$$sanitizeUriProvider
.
aHrefSanitizationWhitelist
(
regexp
)
;
return
this
;
}
else
{
return
$$sanitizeUriProvider
.
aHrefSanitizationWhitelist
();
}
return
aHrefSanitizationWhitelist
;
};
...
...
@@ -5202,18 +5255,18 @@ function $CompileProvider($provide) {
*/
this
.
imgSrcSanitizationWhitelist
=
function
(
regexp
)
{
if
(
isDefined
(
regexp
))
{
imgSrcSanitizationWhitelist
=
regexp
;
$$sanitizeUriProvider
.
imgSrcSanitizationWhitelist
(
regexp
)
;
return
this
;
}
else
{
return
$$sanitizeUriProvider
.
imgSrcSanitizationWhitelist
();
}
return
imgSrcSanitizationWhitelist
;
};
this
.
$get
=
[
'
$injector
'
,
'
$interpolate
'
,
'
$exceptionHandler
'
,
'
$http
'
,
'
$templateCache
'
,
'
$parse
'
,
'
$controller
'
,
'
$rootScope
'
,
'
$document
'
,
'
$sce
'
,
'
$animate
'
,
'
$controller
'
,
'
$rootScope
'
,
'
$document
'
,
'
$sce
'
,
'
$animate
'
,
'
$$sanitizeUri
'
,
function
(
$injector
,
$interpolate
,
$exceptionHandler
,
$http
,
$templateCache
,
$parse
,
$controller
,
$rootScope
,
$document
,
$sce
,
$animate
)
{
$controller
,
$rootScope
,
$document
,
$sce
,
$animate
,
$$sanitizeUri
)
{
var
Attributes
=
function
(
element
,
attr
)
{
this
.
$$element
=
element
;
...
...
@@ -5260,6 +5313,24 @@ function $CompileProvider($provide) {
}
},
/**
* @ngdoc function
* @name ng.$compile.directive.Attributes#$updateClass
* @methodOf ng.$compile.directive.Attributes
* @function
*
* @description
* Adds and removes the appropriate CSS class values to the element based on the difference
* between the new and old CSS class values (specified as newClasses and oldClasses).
*
* @param {string} newClasses The current CSS className value
* @param {string} oldClasses The former CSS className value
*/
$updateClass
:
function
(
newClasses
,
oldClasses
)
{
this
.
$removeClass
(
tokenDifference
(
oldClasses
,
newClasses
));
this
.
$addClass
(
tokenDifference
(
newClasses
,
oldClasses
));
},
/**
* Set a normalized attribute on the element in a way such that all directives
* can share the attribute. This function properly handles boolean attributes.
...
...
@@ -5270,59 +5341,44 @@ function $CompileProvider($provide) {
* @param {string=} attrName Optional none normalized name. Defaults to key.
*/
$set
:
function
(
key
,
value
,
writeAttr
,
attrName
)
{
//special case for class attribute addition + removal
//so that class changes can tap into the animation
//hooks provided by the $animate service
if
(
key
==
'
class
'
)
{
value
=
value
||
''
;
var
current
=
this
.
$$element
.
attr
(
'
class
'
)
||
''
;
this
.
$removeClass
(
tokenDifference
(
current
,
value
).
join
(
'
'
));
this
.
$addClass
(
tokenDifference
(
value
,
current
).
join
(
'
'
));
}
else
{
var
booleanKey
=
getBooleanAttrName
(
this
.
$$element
[
0
],
key
),
normalizedVal
,
nodeName
;
// TODO: decide whether or not to throw an error if "class"
//is set through this function since it may cause $updateClass to
//become unstable.
if
(
booleanKey
)
{
this
.
$$element
.
prop
(
key
,
value
);
attrName
=
booleanKey
;
}
var
booleanKey
=
getBooleanAttrName
(
this
.
$$element
[
0
],
key
),
normalizedVal
,
nodeName
;
this
[
key
]
=
value
;
if
(
booleanKey
)
{
this
.
$$element
.
prop
(
key
,
value
);
attrName
=
booleanKey
;
}
// translate normalized key to actual key
if
(
attrName
)
{
this
.
$attr
[
key
]
=
attrName
;
}
else
{
attrName
=
this
.
$attr
[
key
];
if
(
!
attrName
)
{
this
.
$attr
[
key
]
=
attrName
=
snake_case
(
key
,
'
-
'
);
}
}
this
[
key
]
=
value
;
nodeName
=
nodeName_
(
this
.
$$element
);
// sanitize a[href] and img[src] values
if
((
nodeName
===
'
A
'
&&
key
===
'
href
'
)
||
(
nodeName
===
'
IMG
'
&&
key
===
'
src
'
))
{
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
if
(
!
msie
||
msie
>=
8
)
{
normalizedVal
=
urlResolve
(
value
).
href
;
if
(
normalizedVal
!==
''
)
{
if
((
key
===
'
href
'
&&
!
normalizedVal
.
match
(
aHrefSanitizationWhitelist
))
||
(
key
===
'
src
'
&&
!
normalizedVal
.
match
(
imgSrcSanitizationWhitelist
)))
{
this
[
key
]
=
value
=
'
unsafe:
'
+
normalizedVal
;
}
}
}
// translate normalized key to actual key
if
(
attrName
)
{
this
.
$attr
[
key
]
=
attrName
;
}
else
{
attrName
=
this
.
$attr
[
key
];
if
(
!
attrName
)
{
this
.
$attr
[
key
]
=
attrName
=
snake_case
(
key
,
'
-
'
);
}
}
if
(
writeAttr
!==
false
)
{
if
(
value
===
null
||
value
===
undefined
)
{
this
.
$$element
.
removeAttr
(
attrName
);
}
else
{
this
.
$$element
.
attr
(
attrName
,
value
);
}
nodeName
=
nodeName_
(
this
.
$$element
);
// sanitize a[href] and img[src] values
if
((
nodeName
===
'
A
'
&&
key
===
'
href
'
)
||
(
nodeName
===
'
IMG
'
&&
key
===
'
src
'
))
{
this
[
key
]
=
value
=
$$sanitizeUri
(
value
,
key
===
'
src
'
);
}
if
(
writeAttr
!==
false
)
{
if
(
value
===
null
||
value
===
undefined
)
{
this
.
$$element
.
removeAttr
(
attrName
);
}
else
{
this
.
$$element
.
attr
(
attrName
,
value
);
}
}
...
...
@@ -5335,22 +5391,6 @@ function $CompileProvider($provide) {
$exceptionHandler
(
e
);
}
});
function
tokenDifference
(
str1
,
str2
)
{
var
values
=
[],
tokens1
=
str1
.
split
(
/
\s
+/
),
tokens2
=
str2
.
split
(
/
\s
+/
);
outer
:
for
(
var
i
=
0
;
i
<
tokens1
.
length
;
i
++
)
{
var
token
=
tokens1
[
i
];
for
(
var
j
=
0
;
j
<
tokens2
.
length
;
j
++
)
{
if
(
token
==
tokens2
[
j
])
continue
outer
;
}
values
.
push
(
token
);
}
return
values
;
}
},
...
...
@@ -5533,7 +5573,7 @@ function $CompileProvider($provide) {
createBoundTranscludeFn
(
scope
,
childTranscludeFn
||
transcludeFn
)
);
}
else
{
nodeLinkFn
(
childLinkFn
,
childScope
,
node
,
undefined
,
boundTranscludeFn
);
nodeLinkFn
(
childLinkFn
,
childScope
,
node
,
$rootElement
,
boundTranscludeFn
);
}
}
else
if
(
childLinkFn
)
{
childLinkFn
(
scope
,
node
.
childNodes
,
undefined
,
boundTranscludeFn
);
...
...
@@ -5821,7 +5861,7 @@ function $CompileProvider($provide) {
});
}
else
{
$template
=
jqLite
(
jqLiteClone
(
compileNode
)).
contents
();
$compileNode
.
html
(
''
);
// clear contents
$compileNode
.
empty
(
);
// clear contents
childTranscludeFn
=
compile
(
$template
,
transcludeFn
);
}
}
...
...
@@ -6002,7 +6042,7 @@ function $CompileProvider($provide) {
optional
=
(
match
[
2
]
==
'
?
'
),
mode
=
match
[
1
],
// @, =, or &
lastValue
,
parentGet
,
parentSet
;
parentGet
,
parentSet
,
compare
;
isolateScope
.
$$isolateBindings
[
scopeName
]
=
mode
+
attrName
;
...
...
@@ -6025,6 +6065,11 @@ function $CompileProvider($provide) {
return
;
}
parentGet
=
$parse
(
attrs
[
attrName
]);
if
(
parentGet
.
literal
)
{
compare
=
equals
;
}
else
{
compare
=
function
(
a
,
b
)
{
return
a
===
b
;
};
}
parentSet
=
parentGet
.
assign
||
function
()
{
// reset the change, or we will throw this exception on every $digest
lastValue
=
isolateScope
[
scopeName
]
=
parentGet
(
scope
);
...
...
@@ -6035,19 +6080,18 @@ function $CompileProvider($provide) {
lastValue
=
isolateScope
[
scopeName
]
=
parentGet
(
scope
);
isolateScope
.
$watch
(
function
parentValueWatch
()
{
var
parentValue
=
parentGet
(
scope
);
if
(
parentValue
!==
isolateScope
[
scopeName
])
{
if
(
!
compare
(
parentValue
,
isolateScope
[
scopeName
]))
{
// we are out of sync and need to copy
if
(
parentValue
!==
lastValue
)
{
if
(
!
compare
(
parentValue
,
lastValue
)
)
{
// parent changed and it has precedence
lastValue
=
isolateScope
[
scopeName
]
=
parentValue
;
isolateScope
[
scopeName
]
=
parentValue
;
}
else
{
// if the parent can be assigned then do so
parentSet
(
scope
,
parentValue
=
lastValue
=
isolateScope
[
scopeName
]);
parentSet
(
scope
,
parentValue
=
isolateScope
[
scopeName
]);
}
}
return
parentValue
;
});
return
lastValue
=
parentValue
;
}
,
null
,
parentGet
.
literal
);
break
;
case
'
&
'
:
...
...
@@ -6249,7 +6293,7 @@ function $CompileProvider($provide) {
?
origAsyncDirective
.
templateUrl
(
$compileNode
,
tAttrs
)
:
origAsyncDirective
.
templateUrl
;
$compileNode
.
html
(
''
);
$compileNode
.
empty
(
);
$http
.
get
(
$sce
.
getTrustedResourceUrl
(
templateUrl
),
{
cache
:
$templateCache
}).
success
(
function
(
content
)
{
...
...
@@ -6372,10 +6416,15 @@ function $CompileProvider($provide) {
function
getTrustedContext
(
node
,
attrNormalizedName
)
{
if
(
attrNormalizedName
==
"
srcdoc
"
)
{
return
$sce
.
HTML
;
}
var
tag
=
nodeName_
(
node
);
// maction[xlink:href] can source SVG. It's not limited to <maction>.
if
(
attrNormalizedName
==
"
xlinkHref
"
||
(
nodeName_
(
node
)
!=
"
IMG
"
&&
(
attrNormalizedName
==
"
src
"
||
attrNormalizedName
==
"
ngSrc
"
)))
{
(
tag
==
"
FORM
"
&&
attrNormalizedName
==
"
action
"
)
||
(
tag
!=
"
IMG
"
&&
(
attrNormalizedName
==
"
src
"
||
attrNormalizedName
==
"
ngSrc
"
)))
{
return
$sce
.
RESOURCE_URL
;
}
}
...
...
@@ -6420,9 +6469,19 @@ function $CompileProvider($provide) {
attr
[
name
]
=
interpolateFn
(
scope
);
(
$$observers
[
name
]
||
(
$$observers
[
name
]
=
[])).
$$inter
=
true
;
(
attr
.
$$observers
&&
attr
.
$$observers
[
name
].
$$scope
||
scope
).
$watch
(
interpolateFn
,
function
interpolateFnWatchAction
(
value
)
{
attr
.
$set
(
name
,
value
);
});
$watch
(
interpolateFn
,
function
interpolateFnWatchAction
(
newValue
,
oldValue
)
{
//special case for class attribute addition + removal
//so that class changes can tap into the animation
//hooks provided by the $animate service. Be sure to
//skip animations when the first digest occurs (when
//both the new and the old values are the same) since
//the CSS classes are the non-interpolated values
if
(
name
===
'
class
'
&&
newValue
!=
oldValue
)
{
attr
.
$updateClass
(
newValue
,
oldValue
);
}
else
{
attr
.
$set
(
name
,
newValue
);
}
});
}
};
}
...
...
@@ -6563,6 +6622,22 @@ function directiveLinkingFn(
/* function(Function) */
boundTranscludeFn
){}
function
tokenDifference
(
str1
,
str2
)
{
var
values
=
''
,
tokens1
=
str1
.
split
(
/
\s
+/
),
tokens2
=
str2
.
split
(
/
\s
+/
);
outer
:
for
(
var
i
=
0
;
i
<
tokens1
.
length
;
i
++
)
{
var
token
=
tokens1
[
i
];
for
(
var
j
=
0
;
j
<
tokens2
.
length
;
j
++
)
{
if
(
token
==
tokens2
[
j
])
continue
outer
;
}
values
+=
(
values
.
length
>
0
?
'
'
:
''
)
+
token
;
}
return
values
;
}
/**
* @ngdoc object
* @name ng.$controllerProvider
...
...
@@ -7773,12 +7848,13 @@ var XHR = window.XMLHttpRequest || function() {
*/
function
$HttpBackendProvider
()
{
this
.
$get
=
[
'
$browser
'
,
'
$window
'
,
'
$document
'
,
function
(
$browser
,
$window
,
$document
)
{
return
createHttpBackend
(
$browser
,
XHR
,
$browser
.
defer
,
$window
.
angular
.
callbacks
,
$document
[
0
],
$window
.
location
.
protocol
.
replace
(
'
:
'
,
''
));
return
createHttpBackend
(
$browser
,
XHR
,
$browser
.
defer
,
$window
.
angular
.
callbacks
,
$document
[
0
]);
}];
}
function
createHttpBackend
(
$browser
,
XHR
,
$browserDefer
,
callbacks
,
rawDocument
,
locationProtocol
)
{
function
createHttpBackend
(
$browser
,
XHR
,
$browserDefer
,
callbacks
,
rawDocument
)
{
var
ABORTED
=
-
1
;
// TODO(vojta): fix the signature
return
function
(
method
,
url
,
post
,
callback
,
headers
,
timeout
,
withCredentials
,
responseType
)
{
var
status
;
...
...
@@ -7814,13 +7890,19 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
// always async
xhr
.
onreadystatechange
=
function
()
{
if
(
xhr
.
readyState
==
4
)
{
var
responseHeaders
=
xhr
.
getAllResponseHeaders
();
var
responseHeaders
=
null
,
response
=
null
;
if
(
status
!==
ABORTED
)
{
responseHeaders
=
xhr
.
getAllResponseHeaders
();
response
=
xhr
.
responseType
?
xhr
.
response
:
xhr
.
responseText
;
}
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
completeRequest
(
callback
,
status
||
xhr
.
status
,
(
xhr
.
responseType
?
xhr
.
response
:
xhr
.
responseText
)
,
response
,
responseHeaders
);
}
};
...
...
@@ -7844,20 +7926,20 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
function
timeoutRequest
()
{
status
=
-
1
;
status
=
ABORTED
;
jsonpDone
&&
jsonpDone
();
xhr
&&
xhr
.
abort
();
}
function
completeRequest
(
callback
,
status
,
response
,
headersString
)
{
var
protocol
=
locationProtocol
||
urlResolve
(
url
).
protocol
;
var
protocol
=
urlResolve
(
url
).
protocol
;
// cancel timeout and subsequent timeout promise resolution
timeoutId
&&
$browserDefer
.
cancel
(
timeoutId
);
jsonpDone
=
xhr
=
null
;
// fix status code for file protocol (it's always 0)
status
=
(
protocol
==
'
file
'
)
?
(
response
?
200
:
404
)
:
status
;
status
=
(
protocol
==
'
file
'
&&
status
===
0
)
?
(
response
?
200
:
404
)
:
status
;
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
status
=
status
==
1223
?
204
:
status
;
...
...
@@ -7873,6 +7955,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
// - adds and immediately removes script elements from the document
var
script
=
rawDocument
.
createElement
(
'
script
'
),
doneWrapper
=
function
()
{
script
.
onreadystatechange
=
script
.
onload
=
script
.
onerror
=
null
;
rawDocument
.
body
.
removeChild
(
script
);
if
(
done
)
done
();
};
...
...
@@ -7880,12 +7963,16 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
script
.
type
=
'
text/javascript
'
;
script
.
src
=
url
;
if
(
msie
)
{
if
(
msie
&&
msie
<=
8
)
{
script
.
onreadystatechange
=
function
()
{
if
(
/loaded|complete/
.
test
(
script
.
readyState
))
doneWrapper
();
if
(
/loaded|complete/
.
test
(
script
.
readyState
))
{
doneWrapper
();
}
};
}
else
{
script
.
onload
=
script
.
onerror
=
doneWrapper
;
script
.
onload
=
script
.
onerror
=
function
()
{
doneWrapper
();
};
}
rawDocument
.
body
.
appendChild
(
script
);
...
...
@@ -7996,8 +8083,8 @@ function $InterpolateProvider() {
*
<pre>
var $interpolate = ...; // injected
var exp = $interpolate('Hello {{name}}!');
expect(exp({name:'Angular'}).toEqual('Hello A
ngular
!');
var exp = $interpolate('Hello {{name
| uppercase
}}!');
expect(exp({name:'Angular'}).toEqual('Hello A
NGULAR
!');
</pre>
*
*
...
...
@@ -8472,7 +8559,47 @@ function LocationHashbangUrl(appBase, hashPrefix) {
hashPrefix
);
}
parseAppUrl
(
withoutHashUrl
,
this
,
appBase
);
this
.
$$path
=
removeWindowsDriveName
(
this
.
$$path
,
withoutHashUrl
,
appBase
);
this
.
$$compose
();
/*
* In Windows, on an anchor node on documents loaded from
* the filesystem, the browser will return a pathname
* prefixed with the drive name ('/C:/path') when a
* pathname without a drive is set:
* * a.setAttribute('href', '/foo')
* * a.pathname === '/C:/foo' //true
*
* Inside of Angular, we're always using pathnames that
* do not include drive names for routing.
*/
function
removeWindowsDriveName
(
path
,
url
,
base
)
{
/*
Matches paths for file protocol on windows,
such as /C:/foo/bar, and captures only /foo/bar.
*/
var
windowsFilePathExp
=
/^
\/?
.*
?
:
(\/
.*
)
/
;
var
firstPathSegmentMatch
;
//Get the relative path from the input URL.
if
(
url
.
indexOf
(
base
)
===
0
)
{
url
=
url
.
replace
(
base
,
''
);
}
/*
* The input URL intentionally contains a
* first path segment that ends with a colon.
*/
if
(
windowsFilePathExp
.
exec
(
url
))
{
return
path
;
}
firstPathSegmentMatch
=
windowsFilePathExp
.
exec
(
path
);
return
firstPathSegmentMatch
?
firstPathSegmentMatch
[
1
]
:
path
;
}
};
/**
...
...
@@ -8962,7 +9089,7 @@ function $LocationProvider(){
*
* The main purpose of this service is to simplify debugging and troubleshooting.
*
* The default is
not
to log `debug` messages. You can use
* The default is to log `debug` messages. You can use
* {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
*
* @example
...
...
@@ -9155,23 +9282,24 @@ function ensureSafeMemberName(name, fullExpression) {
function
ensureSafeObject
(
obj
,
fullExpression
)
{
// nifty check if obj is Function that is fast and works across iframes and other contexts
if
(
obj
&&
obj
.
constructor
===
obj
)
{
throw
$parseMinErr
(
'
isecfn
'
,
'
Referencing Function in Angular expressions is disallowed! Expression: {0}
'
,
fullExpression
);
}
else
if
(
// isWindow(obj)
obj
&&
obj
.
document
&&
obj
.
location
&&
obj
.
alert
&&
obj
.
setInterval
)
{
throw
$parseMinErr
(
'
isecwindow
'
,
'
Referencing the Window in Angular expressions is disallowed! Expression: {0}
'
,
fullExpression
);
}
else
if
(
// isElement(obj)
obj
&&
(
obj
.
nodeName
||
(
obj
.
on
&&
obj
.
find
)))
{
throw
$parseMinErr
(
'
isecdom
'
,
'
Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}
'
,
fullExpression
);
}
else
{
return
obj
;
if
(
obj
)
{
if
(
obj
.
constructor
===
obj
)
{
throw
$parseMinErr
(
'
isecfn
'
,
'
Referencing Function in Angular expressions is disallowed! Expression: {0}
'
,
fullExpression
);
}
else
if
(
// isWindow(obj)
obj
.
document
&&
obj
.
location
&&
obj
.
alert
&&
obj
.
setInterval
)
{
throw
$parseMinErr
(
'
isecwindow
'
,
'
Referencing the Window in Angular expressions is disallowed! Expression: {0}
'
,
fullExpression
);
}
else
if
(
// isElement(obj)
obj
.
children
&&
(
obj
.
nodeName
||
(
obj
.
on
&&
obj
.
find
)))
{
throw
$parseMinErr
(
'
isecdom
'
,
'
Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}
'
,
fullExpression
);
}
}
return
obj
;
}
var
OPERATORS
=
{
...
...
@@ -10128,7 +10256,7 @@ function getterFn(path, options, fullExp) {
:
'
((k&&k.hasOwnProperty("
'
+
key
+
'
"))?k:s)
'
)
+
'
["
'
+
key
+
'
"]
'
+
'
;
\n
'
+
(
options
.
unwrapPromises
?
'
if (s && s.then) {
\n
'
+
'
pw("
'
+
fullExp
.
replace
(
/
\"
/g
,
'
\\
"
'
)
+
'
");
\n
'
+
'
pw("
'
+
fullExp
.
replace
(
/
([
"
\r\n])
/g
,
'
\\
$1
'
)
+
'
");
\n
'
+
'
if (!("$$v" in s)) {
\n
'
+
'
p=s;
\n
'
+
'
p.$$v = undefined;
\n
'
+
...
...
@@ -10480,7 +10608,7 @@ function $ParseProvider() {
*
* # Differences between Kris Kowal's Q and $q
*
* There are t
hree
main differences:
* There are t
wo
main differences:
*
* - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
* mechanism in angular, which means faster propagation of resolution or rejection into your
...
...
@@ -10929,6 +11057,7 @@ function qFactory(nextTick, exceptionHandler) {
function
$RootScopeProvider
(){
var
TTL
=
10
;
var
$rootScopeMinErr
=
minErr
(
'
$rootScope
'
);
var
lastDirtyWatch
=
null
;
this
.
digestTtl
=
function
(
value
)
{
if
(
arguments
.
length
)
{
...
...
@@ -11013,11 +11142,11 @@ function $RootScopeProvider(){
* @description
* Creates a new child {@link ng.$rootScope.Scope scope}.
*
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
* {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
* scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
* The parent scope will propagate the {@link ng.$rootScope.Scope#
methods_
$digest $digest()} and
* {@link ng.$rootScope.Scope#
methods_
$digest $digest()} events. The scope can be removed from the
* scope hierarchy using {@link ng.$rootScope.Scope#
methods_
$destroy $destroy()}.
*
* {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
* {@link ng.$rootScope.Scope#
methods_
$destroy $destroy()} must be called on a scope when it is
* desired for the scope and its child scopes to be permanently detached from the parent and
* thus stop participating in model change detection and listener notification by invoking.
*
...
...
@@ -11030,7 +11159,7 @@ function $RootScopeProvider(){
*
*/
$new
:
function
(
isolate
)
{
var
Child
,
var
Child
Scope
,
child
;
if
(
isolate
)
{
...
...
@@ -11040,11 +11169,11 @@ function $RootScopeProvider(){
child
.
$$asyncQueue
=
this
.
$$asyncQueue
;
child
.
$$postDigestQueue
=
this
.
$$postDigestQueue
;
}
else
{
Child
=
function
()
{};
// should be anonymous; This is so that when the minifier munges
Child
Scope
=
function
()
{};
// should be anonymous; This is so that when the minifier munges
// the name it does not become random set of chars. This will then show up as class
// name in the debugger.
Child
.
prototype
=
this
;
child
=
new
Child
();
Child
Scope
.
prototype
=
this
;
child
=
new
Child
Scope
();
child
.
$id
=
nextUid
();
}
child
[
'
this
'
]
=
child
;
...
...
@@ -11070,11 +11199,11 @@ function $RootScopeProvider(){
* @description
* Registers a `listener` callback to be executed whenever the `watchExpression` changes.
*
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#
methods_
$digest
* $digest()} and should return the value that will be watched. (Since
* {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
* {@link ng.$rootScope.Scope#
methods_
$digest $digest()} reruns when it detects changes the
* `watchExpression` can execute multiple times per
* {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
* {@link ng.$rootScope.Scope#
methods_
$digest $digest()} and should be idempotent.)
* - The `listener` is called only when the value from the current `watchExpression` and the
* previous call to `watchExpression` are not equal (with the exception of the initial run,
* see below). The inequality is determined according to
...
...
@@ -11086,13 +11215,13 @@ function $RootScopeProvider(){
* iteration limit is 10 to prevent an infinite loop deadlock.
*
*
* If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
* If you want to be notified whenever {@link ng.$rootScope.Scope#
methods_
$digest $digest} is called,
* you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
* can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
* can execute multiple times per {@link ng.$rootScope.Scope#
methods_
$digest $digest} cycle when a
* change is detected, be prepared for multiple calls to your listener.)
*
* After a watcher is registered with the scope, the `listener` fn is called asynchronously
* (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
* (via {@link ng.$rootScope.Scope#
methods_
$evalAsync $evalAsync}) to initialize the
* watcher. In rare cases, this is undesirable because the listener is called when the result
* of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
* can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
...
...
@@ -11124,7 +11253,7 @@ function $RootScopeProvider(){
// Using a listener function
// Using a listener function
var food;
scope.foodCounter = 0;
expect(scope.foodCounter).toEqual(0);
...
...
@@ -11149,14 +11278,14 @@ function $RootScopeProvider(){
// Update food and run digest. Now the counter will increment
food = 'cheeseburger';
scope.$digest();
expect(scope.foodCounter).toEqual(1);
expect(scope.foodCounter).toEqual(1);
* </pre>
*
*
*
* @param {(function()|string)} watchExpression Expression that is evaluated on each
* {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
* {@link ng.$rootScope.Scope#
methods_
$digest $digest} cycle. A change in the return value triggers
* a call to the `listener`.
*
* - `string`: Evaluated as {@link guide/expression expression}
...
...
@@ -11183,6 +11312,8 @@ function $RootScopeProvider(){
eq
:
!!
objectEquality
};
lastDirtyWatch
=
null
;
// in the case user pass string, we need to compile it, do we really need this ?
if
(
!
isFunction
(
listener
))
{
var
listenFn
=
compileToFn
(
listener
||
noop
,
'
listener
'
);
...
...
@@ -11252,7 +11383,7 @@ function $RootScopeProvider(){
*
* @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The
* expression value should evaluate to an object or an array which is observed on each
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
* {@link ng.$rootScope.Scope#
methods_
$digest $digest} cycle. Any shallow change within the
* collection will trigger a call to the `listener`.
*
* @param {function(newCollection, oldCollection, scope)} listener a callback function that is
...
...
@@ -11357,9 +11488,9 @@ function $RootScopeProvider(){
* @function
*
* @description
* Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
* its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
* the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
* Processes all of the {@link ng.$rootScope.Scope#
methods_
$watch watchers} of the current scope and
* its children. Because a {@link ng.$rootScope.Scope#
methods_
$watch watcher}'s listener can change
* the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#
methods_
$watch watchers}
* until no more listeners are firing. This means that it is possible to get into an infinite
* loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
* iterations exceeds 10.
...
...
@@ -11367,12 +11498,12 @@ function $RootScopeProvider(){
* Usually, you don't call `$digest()` directly in
* {@link ng.directive:ngController controllers} or in
* {@link ng.$compileProvider#methods_directive directives}.
* Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
* Instead, you should call {@link ng.$rootScope.Scope#
methods_
$apply $apply()} (typically from within
* a {@link ng.$compileProvider#methods_directive directives}), which will force a `$digest()`.
*
* If you want to be notified whenever `$digest()` is called,
* you can register a `watchExpression` function with
* {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
* {@link ng.$rootScope.Scope#
methods_
$watch $watch()} with no `listener`.
*
* In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
*
...
...
@@ -11411,6 +11542,8 @@ function $RootScopeProvider(){
beginPhase
(
'
$digest
'
);
lastDirtyWatch
=
null
;
do
{
// "while dirty" loop
dirty
=
false
;
current
=
target
;
...
...
@@ -11420,10 +11553,13 @@ function $RootScopeProvider(){
asyncTask
=
asyncQueue
.
shift
();
asyncTask
.
scope
.
$eval
(
asyncTask
.
expression
);
}
catch
(
e
)
{
clearPhase
();
$exceptionHandler
(
e
);
}
lastDirtyWatch
=
null
;
}
traverseScopesLoop
:
do
{
// "traverse the scopes" loop
if
((
watchers
=
current
.
$$watchers
))
{
// process our watches
...
...
@@ -11433,25 +11569,34 @@ function $RootScopeProvider(){
watch
=
watchers
[
length
];
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
if
(
watch
&&
(
value
=
watch
.
get
(
current
))
!==
(
last
=
watch
.
last
)
&&
!
(
watch
.
eq
?
equals
(
value
,
last
)
:
(
typeof
value
==
'
number
'
&&
typeof
last
==
'
number
'
&&
isNaN
(
value
)
&&
isNaN
(
last
))))
{
dirty
=
true
;
watch
.
last
=
watch
.
eq
?
copy
(
value
)
:
value
;
watch
.
fn
(
value
,
((
last
===
initWatchVal
)
?
value
:
last
),
current
);
if
(
ttl
<
5
)
{
logIdx
=
4
-
ttl
;
if
(
!
watchLog
[
logIdx
])
watchLog
[
logIdx
]
=
[];
logMsg
=
(
isFunction
(
watch
.
exp
))
?
'
fn:
'
+
(
watch
.
exp
.
name
||
watch
.
exp
.
toString
())
:
watch
.
exp
;
logMsg
+=
'
; newVal:
'
+
toJson
(
value
)
+
'
; oldVal:
'
+
toJson
(
last
);
watchLog
[
logIdx
].
push
(
logMsg
);
if
(
watch
)
{
if
((
value
=
watch
.
get
(
current
))
!==
(
last
=
watch
.
last
)
&&
!
(
watch
.
eq
?
equals
(
value
,
last
)
:
(
typeof
value
==
'
number
'
&&
typeof
last
==
'
number
'
&&
isNaN
(
value
)
&&
isNaN
(
last
))))
{
dirty
=
true
;
lastDirtyWatch
=
watch
;
watch
.
last
=
watch
.
eq
?
copy
(
value
)
:
value
;
watch
.
fn
(
value
,
((
last
===
initWatchVal
)
?
value
:
last
),
current
);
if
(
ttl
<
5
)
{
logIdx
=
4
-
ttl
;
if
(
!
watchLog
[
logIdx
])
watchLog
[
logIdx
]
=
[];
logMsg
=
(
isFunction
(
watch
.
exp
))
?
'
fn:
'
+
(
watch
.
exp
.
name
||
watch
.
exp
.
toString
())
:
watch
.
exp
;
logMsg
+=
'
; newVal:
'
+
toJson
(
value
)
+
'
; oldVal:
'
+
toJson
(
last
);
watchLog
[
logIdx
].
push
(
logMsg
);
}
}
else
if
(
watch
===
lastDirtyWatch
)
{
// If the most recently dirty watcher is now clean, short circuit since the remaining watchers
// have already been tested.
dirty
=
false
;
break
traverseScopesLoop
;
}
}
}
catch
(
e
)
{
clearPhase
();
$exceptionHandler
(
e
);
}
}
...
...
@@ -11460,13 +11605,16 @@ function $RootScopeProvider(){
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $broadcast
if
(
!
(
next
=
(
current
.
$$childHead
||
(
current
!==
target
&&
current
.
$$nextSibling
))))
{
if
(
!
(
next
=
(
current
.
$$childHead
||
(
current
!==
target
&&
current
.
$$nextSibling
))))
{
while
(
current
!==
target
&&
!
(
next
=
current
.
$$nextSibling
))
{
current
=
current
.
$parent
;
}
}
}
while
((
current
=
next
));
// `break traverseScopesLoop;` takes us to here
if
(
dirty
&&
!
(
ttl
--
))
{
clearPhase
();
throw
$rootScopeMinErr
(
'
infdig
'
,
...
...
@@ -11474,6 +11622,7 @@ function $RootScopeProvider(){
'
Watchers fired in the last 5 iterations: {1}
'
,
TTL
,
toJson
(
watchLog
));
}
}
while
(
dirty
||
asyncQueue
.
length
);
clearPhase
();
...
...
@@ -11509,7 +11658,7 @@ function $RootScopeProvider(){
*
* @description
* Removes the current scope (and all of its children) from the parent scope. Removal implies
* that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
* that calls to {@link ng.$rootScope.Scope#
methods_
$digest $digest()} will no longer
* propagate to the current scope and its children. Removal also implies that the current
* scope is eligible for garbage collection.
*
...
...
@@ -11526,11 +11675,12 @@ function $RootScopeProvider(){
*/
$destroy
:
function
()
{
// we can't destroy the root scope or a scope that has been already destroyed
if
(
$rootScope
==
this
||
this
.
$$destroyed
)
return
;
if
(
this
.
$$destroyed
)
return
;
var
parent
=
this
.
$parent
;
this
.
$broadcast
(
'
$destroy
'
);
this
.
$$destroyed
=
true
;
if
(
this
===
$rootScope
)
return
;
if
(
parent
.
$$childHead
==
this
)
parent
.
$$childHead
=
this
.
$$nextSibling
;
if
(
parent
.
$$childTail
==
this
)
parent
.
$$childTail
=
this
.
$$prevSibling
;
...
...
@@ -11568,7 +11718,7 @@ function $RootScopeProvider(){
*
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with the current `scope` parameter.
*
*
* @param {(object)=} locals Local variables object, useful for overriding values in scope.
* @returns {*} The result of evaluating the expression.
*/
...
...
@@ -11590,7 +11740,7 @@ function $RootScopeProvider(){
*
* - it will execute after the function that scheduled the evaluation (preferably before DOM
* rendering).
* - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
* - at least one {@link ng.$rootScope.Scope#
methods_
$digest $digest cycle} will be performed after
* `expression` execution.
*
* Any exceptions from the execution of the expression are forwarded to the
...
...
@@ -11635,7 +11785,7 @@ function $RootScopeProvider(){
* framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
* Because we are calling into the angular framework we need to perform proper scope life
* cycle of {@link ng.$exceptionHandler exception handling},
* {@link ng.$rootScope.Scope#$digest executing watches}.
* {@link ng.$rootScope.Scope#
methods_
$digest executing watches}.
*
* ## Life cycle
*
...
...
@@ -11656,11 +11806,11 @@ function $RootScopeProvider(){
* Scope's `$apply()` method transitions through the following stages:
*
* 1. The {@link guide/expression expression} is executed using the
* {@link ng.$rootScope.Scope#$eval $eval()} method.
* {@link ng.$rootScope.Scope#
methods_
$eval $eval()} method.
* 2. Any exceptions from the execution of the expression are forwarded to the
* {@link ng.$exceptionHandler $exceptionHandler} service.
* 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
* expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
* 3. The {@link ng.$rootScope.Scope#
methods_
$watch watch} listeners are fired immediately after the
* expression was executed using the {@link ng.$rootScope.Scope#
methods_
$digest $digest()} method.
*
*
* @param {(string|function())=} exp An angular expression to be executed.
...
...
@@ -11694,7 +11844,7 @@ function $RootScopeProvider(){
* @function
*
* @description
* Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
* Listens on events of a given type. See {@link ng.$rootScope.Scope#
methods_
$emit $emit} for
* discussion of event life cycle.
*
* The event listener function format is: `function(event, args...)`. The `event` object
...
...
@@ -11735,20 +11885,20 @@ function $RootScopeProvider(){
*
* @description
* Dispatches an event `name` upwards through the scope hierarchy notifying the
* registered {@link ng.$rootScope.Scope#$on} listeners.
* registered {@link ng.$rootScope.Scope#
methods_
$on} listeners.
*
* The event life cycle starts at the scope on which `$emit` was called. All
* {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
* {@link ng.$rootScope.Scope#
methods_
$on listeners} listening for `name` event on this scope get
* notified. Afterwards, the event traverses upwards toward the root scope and calls all
* registered listeners along the way. The event will stop propagating if one of the listeners
* cancels it.
*
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* Any exception emitted from the {@link ng.$rootScope.Scope#
methods_
$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to emit.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
* @return {Object} Event object (see {@link ng.$rootScope.Scope#
methods_
$on}).
*/
$emit
:
function
(
name
,
args
)
{
var
empty
=
[],
...
...
@@ -11804,19 +11954,19 @@ function $RootScopeProvider(){
*
* @description
* Dispatches an event `name` downwards to all child scopes (and their children) notifying the
* registered {@link ng.$rootScope.Scope#$on} listeners.
* registered {@link ng.$rootScope.Scope#
methods_
$on} listeners.
*
* The event life cycle starts at the scope on which `$broadcast` was called. All
* {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
* {@link ng.$rootScope.Scope#
methods_
$on listeners} listening for `name` event on this scope get
* notified. Afterwards, the event propagates to all direct and indirect scopes of the current
* scope and calls all registered listeners along the way. The event cannot be canceled.
*
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* Any exception emitted from the {@link ng.$rootScope.Scope#
methods_
$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to broadcast.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
* @return {Object} Event object, see {@link ng.$rootScope.Scope#
methods_
$on}
*/
$broadcast
:
function
(
name
,
args
)
{
var
target
=
this
,
...
...
@@ -11899,6 +12049,79 @@ function $RootScopeProvider(){
}];
}
/**
* @description
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
*/
function
$$SanitizeUriProvider
()
{
var
aHrefSanitizationWhitelist
=
/^
\s
*
(
https
?
|ftp|mailto|tel|file
)
:/
,
imgSrcSanitizationWhitelist
=
/^
\s
*
(
https
?
|ftp|file
)
:|data:image
\/
/
;
/**
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this
.
aHrefSanitizationWhitelist
=
function
(
regexp
)
{
if
(
isDefined
(
regexp
))
{
aHrefSanitizationWhitelist
=
regexp
;
return
this
;
}
return
aHrefSanitizationWhitelist
;
};
/**
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during img[src] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this
.
imgSrcSanitizationWhitelist
=
function
(
regexp
)
{
if
(
isDefined
(
regexp
))
{
imgSrcSanitizationWhitelist
=
regexp
;
return
this
;
}
return
imgSrcSanitizationWhitelist
;
};
this
.
$get
=
function
()
{
return
function
sanitizeUri
(
uri
,
isImage
)
{
var
regex
=
isImage
?
imgSrcSanitizationWhitelist
:
aHrefSanitizationWhitelist
;
var
normalizedVal
;
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
if
(
!
msie
||
msie
>=
8
)
{
normalizedVal
=
urlResolve
(
uri
).
href
;
if
(
normalizedVal
!==
''
&&
!
normalizedVal
.
match
(
regex
))
{
return
'
unsafe:
'
+
normalizedVal
;
}
}
return
uri
;
};
};
}
var
$sceMinErr
=
minErr
(
'
$sce
'
);
var
SCE_CONTEXTS
=
{
...
...
@@ -12098,8 +12321,7 @@ function $SceDelegateProvider() {
return
resourceUrlBlacklist
;
};
this
.
$get
=
[
'
$log
'
,
'
$document
'
,
'
$injector
'
,
function
(
$log
,
$document
,
$injector
)
{
this
.
$get
=
[
'
$injector
'
,
function
(
$injector
)
{
var
htmlSanitizer
=
function
htmlSanitizer
(
html
)
{
throw
$sceMinErr
(
'
unsafe
'
,
'
Attempting to use an unsafe value in a safe context.
'
);
...
...
@@ -12630,18 +12852,15 @@ function $SceProvider() {
* sce.js and sceSpecs.js would need to be aware of this detail.
*/
this
.
$get
=
[
'
$parse
'
,
'
$
document
'
,
'
$sceDelegate
'
,
function
(
$parse
,
$
document
,
$sceDelegate
)
{
this
.
$get
=
[
'
$parse
'
,
'
$
sniffer
'
,
'
$sceDelegate
'
,
function
(
$parse
,
$
sniffer
,
$sceDelegate
)
{
// Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows
// the "expression(javascript expression)" syntax which is insecure.
if
(
enabled
&&
msie
)
{
var
documentMode
=
$document
[
0
].
documentMode
;
if
(
documentMode
!==
undefined
&&
documentMode
<
8
)
{
throw
$sceMinErr
(
'
iequirks
'
,
'
Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks
'
+
'
mode. You can fix this by adding the text <!doctype html> to the top of your HTML
'
+
'
document. See http://docs.angularjs.org/api/ng.$sce for more information.
'
);
}
if
(
enabled
&&
$sniffer
.
msie
&&
$sniffer
.
msieDocumentMode
<
8
)
{
throw
$sceMinErr
(
'
iequirks
'
,
'
Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks
'
+
'
mode. You can fix this by adding the text <!doctype html> to the top of your HTML
'
+
'
document. See http://docs.angularjs.org/api/ng.$sce for more information.
'
);
}
var
sce
=
copy
(
SCE_CONTEXTS
);
...
...
@@ -13003,6 +13222,7 @@ function $SnifferProvider() {
int
((
/android
(\d
+
)
/
.
exec
(
lowercase
((
$window
.
navigator
||
{}).
userAgent
))
||
[])[
1
]),
boxee
=
/Boxee/i
.
test
((
$window
.
navigator
||
{}).
userAgent
),
document
=
$document
[
0
]
||
{},
documentMode
=
document
.
documentMode
,
vendorPrefix
,
vendorRegex
=
/^
(
Moz|webkit|O|ms
)(?=[
A-Z
])
/
,
bodyStyle
=
document
.
body
&&
document
.
body
.
style
,
...
...
@@ -13047,7 +13267,7 @@ function $SnifferProvider() {
// jshint +W018
hashchange
:
'
onhashchange
'
in
$window
&&
// IE8 compatible mode lies
(
!
document
.
documentMode
||
document
.
documentMode
>
7
),
(
!
document
Mode
||
documentMode
>
7
),
hasEvent
:
function
(
event
)
{
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
// it. In particular the event is not fired when backspace or delete key are pressed or
...
...
@@ -13065,7 +13285,8 @@ function $SnifferProvider() {
vendorPrefix
:
vendorPrefix
,
transitions
:
transitions
,
animations
:
animations
,
msie
:
msie
msie
:
msie
,
msieDocumentMode
:
documentMode
};
}];
}
...
...
@@ -13250,11 +13471,6 @@ function $TimeoutProvider() {
// exactly the behavior needed here. There is little value is mocking these out for this
// service.
var
urlParsingNode
=
document
.
createElement
(
"
a
"
);
/*
Matches paths for file protocol on windows,
such as /C:/foo/bar, and captures only /foo/bar.
*/
var
windowsFilePathExp
=
/^
\/?
.*
?
:
(\/
.*
)
/
;
var
originUrl
=
urlResolve
(
window
.
location
.
href
,
true
);
...
...
@@ -13311,8 +13527,7 @@ var originUrl = urlResolve(window.location.href, true);
*
*/
function
urlResolve
(
url
,
base
)
{
var
href
=
url
,
pathname
;
var
href
=
url
;
if
(
msie
)
{
// Normalize before parse. Refer Implementation Notes on why this is
...
...
@@ -13323,21 +13538,6 @@ function urlResolve(url, base) {
urlParsingNode
.
setAttribute
(
'
href
'
,
href
);
/*
* In Windows, on an anchor node on documents loaded from
* the filesystem, the browser will return a pathname
* prefixed with the drive name ('/C:/path') when a
* pathname without a drive is set:
* * a.setAttribute('href', '/foo')
* * a.pathname === '/C:/foo' //true
*
* Inside of Angular, we're always using pathnames that
* do not include drive names for routing.
*/
pathname
=
removeWindowsDriveName
(
urlParsingNode
.
pathname
,
url
,
base
);
pathname
=
(
pathname
.
charAt
(
0
)
===
'
/
'
)
?
pathname
:
'
/
'
+
pathname
;
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
return
{
href
:
urlParsingNode
.
href
,
...
...
@@ -13347,11 +13547,12 @@ function urlResolve(url, base) {
hash
:
urlParsingNode
.
hash
?
urlParsingNode
.
hash
.
replace
(
/^#/
,
''
)
:
''
,
hostname
:
urlParsingNode
.
hostname
,
port
:
urlParsingNode
.
port
,
pathname
:
pathname
pathname
:
(
urlParsingNode
.
pathname
.
charAt
(
0
)
===
'
/
'
)
?
urlParsingNode
.
pathname
:
'
/
'
+
urlParsingNode
.
pathname
};
}
/**
* Parse a request URL and determine whether this is a same-origin request as the application document.
*
...
...
@@ -13365,26 +13566,6 @@ function urlIsSameOrigin(requestUrl) {
parsed
.
host
===
originUrl
.
host
);
}
function
removeWindowsDriveName
(
path
,
url
,
base
)
{
var
firstPathSegmentMatch
;
//Get the relative path from the input URL.
if
(
url
.
indexOf
(
base
)
===
0
)
{
url
=
url
.
replace
(
base
,
''
);
}
/*
* The input URL intentionally contains a
* first path segment that ends with a colon.
*/
if
(
windowsFilePathExp
.
exec
(
url
))
{
return
path
;
}
firstPathSegmentMatch
=
windowsFilePathExp
.
exec
(
path
);
return
firstPathSegmentMatch
?
firstPathSegmentMatch
[
1
]
:
path
;
}
/**
* @ngdoc object
* @name ng.$window
...
...
@@ -13405,13 +13586,15 @@ function removeWindowsDriveName (path, url, base) {
<doc:source>
<script>
function Ctrl($scope, $window) {
$scope.$window = $window;
$scope.greeting = 'Hello, World!';
$scope.doGreeting = function(greeting) {
$window.alert(greeting);
};
}
</script>
<div ng-controller="Ctrl">
<input type="text" ng-model="greeting" />
<button ng-click="
$window.alert
(greeting)">ALERT</button>
<button ng-click="
doGreeting
(greeting)">ALERT</button>
</div>
</doc:source>
<doc:scenario>
...
...
@@ -14520,14 +14703,16 @@ var htmlAnchorDirective = valueFn({
element
.
append
(
document
.
createComment
(
'
IE fix
'
));
}
return
function
(
scope
,
element
)
{
element
.
on
(
'
click
'
,
function
(
event
){
// if we have no href url, then don't navigate anywhere.
if
(
!
element
.
attr
(
'
href
'
))
{
event
.
preventDefault
();
}
});
};
if
(
!
attr
.
href
&&
!
attr
.
name
)
{
return
function
(
scope
,
element
)
{
element
.
on
(
'
click
'
,
function
(
event
){
// if we have no href url, then don't navigate anywhere.
if
(
!
element
.
attr
(
'
href
'
))
{
event
.
preventDefault
();
}
});
};
}
}
});
...
...
@@ -14680,8 +14865,11 @@ var htmlAnchorDirective = valueFn({
*
* The HTML specification does not require browsers to preserve the values of boolean attributes
* such as disabled. (Their presence means true and their absence means false.)
* This prevents the Angular compiler from retrieving the binding expression.
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
* The `ngDisabled` directive solves this problem for the `disabled` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
*
* @example
<doc:example>
...
...
@@ -14712,8 +14900,11 @@ var htmlAnchorDirective = valueFn({
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
* such as checked. (Their presence means true and their absence means false.)
* This prevents the Angular compiler from retrieving the binding expression.
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
* The `ngChecked` directive solves this problem for the `checked` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
* @example
<doc:example>
<doc:source>
...
...
@@ -14743,8 +14934,12 @@ var htmlAnchorDirective = valueFn({
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
* such as readonly. (Their presence means true and their absence means false.)
* This prevents the Angular compiler from retrieving the binding expression.
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
* @example
<doc:example>
<doc:source>
...
...
@@ -14774,8 +14969,11 @@ var htmlAnchorDirective = valueFn({
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
* such as selected. (Their presence means true and their absence means false.)
* This prevents the Angular compiler from retrieving the binding expression.
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
* The `ngSelected` directive solves this problem for the `selected` atttribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
* @example
<doc:example>
<doc:source>
...
...
@@ -14807,8 +15005,12 @@ var htmlAnchorDirective = valueFn({
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
* such as open. (Their presence means true and their absence means false.)
* This prevents the Angular compiler from retrieving the binding expression.
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
* The `ngOpen` directive solves this problem for the `open` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
*
* @example
<doc:example>
...
...
@@ -14901,9 +15103,22 @@ var nullFormCtrl = {
* @property {Object} $error Is an object hash, containing references to all invalid controls or
* forms, where:
*
* - keys are validation tokens (error names)
— such as `required`, `url` or `email`
,
* - values are arrays of controls or forms that are invalid
with given error
.
* - keys are validation tokens (error names),
* - values are arrays of controls or forms that are invalid
for given error name
.
*
*
* Built-in validation tokens:
*
* - `email`
* - `max`
* - `maxlength`
* - `min`
* - `minlength`
* - `number`
* - `pattern`
* - `required`
* - `url`
*
* @description
* `FormController` keeps track of all its controls and nested forms as well as state of them,
* such as being valid/invalid or dirty/pristine.
...
...
@@ -15638,8 +15853,21 @@ var inputType = {
function
textInputType
(
scope
,
element
,
attr
,
ctrl
,
$sniffer
,
$browser
)
{
// In composition mode, users are still inputing intermediate text buffer,
// hold the listener until composition is done.
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
var
composing
=
false
;
element
.
on
(
'
compositionstart
'
,
function
()
{
composing
=
true
;
});
element
.
on
(
'
compositionend
'
,
function
()
{
composing
=
false
;
});
var
listener
=
function
()
{
if
(
composing
)
return
;
var
value
=
element
.
val
();
// By default we will trim the value
...
...
@@ -15682,15 +15910,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
deferListener
();
});
// if user paste into input using mouse, we need "change" event to catch it
element
.
on
(
'
change
'
,
listener
);
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
if
(
$sniffer
.
hasEvent
(
'
paste
'
))
{
element
.
on
(
'
paste cut
'
,
deferListener
);
}
}
// if user paste into input using mouse on older browser
// or form autocomplete on newer browser, we need "change" event to catch it
element
.
on
(
'
change
'
,
listener
);
ctrl
.
$render
=
function
()
{
element
.
val
(
ctrl
.
$isEmpty
(
ctrl
.
$viewValue
)
?
''
:
ctrl
.
$viewValue
);
...
...
@@ -16184,39 +16412,6 @@ var VALID_CLASS = 'ng-valid',
</file>
* </example>
*
* ## Isolated Scope Pitfall
*
* Note that if you have a directive with an isolated scope, you cannot require `ngModel`
* since the model value will be looked up on the isolated scope rather than the outer scope.
* When the directive updates the model value, calling `ngModel.$setViewValue()` the property
* on the outer scope will not be updated. However you can get around this by using $parent.
*
* Here is an example of this situation. You'll notice that the first div is not updating the input.
* However the second div can update the input properly.
*
* <example module="badIsolatedDirective">
<file name="script.js">
angular.module('badIsolatedDirective', []).directive('isolate', function() {
return {
require: 'ngModel',
scope: { },
template: '<input ng-model="innerModel">',
link: function(scope, element, attrs, ngModel) {
scope.$watch('innerModel', function(value) {
console.log(value);
ngModel.$setViewValue(value);
});
}
};
});
</file>
<file name="index.html">
<input ng-model="someModel"/>
<div isolate ng-model="someModel"></div>
<div isolate ng-model="$parent.someModel"></div>
</file>
* </example>
*
*
*/
var
NgModelController
=
[
'
$scope
'
,
'
$exceptionHandler
'
,
'
$attrs
'
,
'
$element
'
,
'
$parse
'
,
...
...
@@ -16363,7 +16558,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* It will update the $viewValue, then pass this value through each of the functions in `$parsers`,
* which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to
* `$modelValue` and the **expression** specified in the `ng-model` attribute.
*
*
* Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
*
* Note that calling this function does not trigger a `$digest`.
...
...
@@ -16420,6 +16615,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
ctrl
.
$render
();
}
}
return
value
;
});
}];
...
...
@@ -16696,7 +16893,6 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
id="{{name}}"
name="favorite">
</label>
</span>
<div>You chose {{my.favorite}}</div>
</form>
</doc:source>
...
...
@@ -16929,11 +17125,10 @@ function classDirective(name, selector) {
// jshint bitwise: false
var
mod
=
$index
&
1
;
if
(
mod
!==
old$index
&
1
)
{
if
(
mod
===
selector
)
{
addClass
(
scope
.
$eval
(
attr
[
name
]));
}
else
{
removeClass
(
scope
.
$eval
(
attr
[
name
]));
}
var
classes
=
flattenClasses
(
scope
.
$eval
(
attr
[
name
]));
mod
===
selector
?
attr
.
$addClass
(
classes
)
:
attr
.
$removeClass
(
classes
);
}
});
}
...
...
@@ -16941,24 +17136,17 @@ function classDirective(name, selector) {
function
ngClassWatchAction
(
newVal
)
{
if
(
selector
===
true
||
scope
.
$index
%
2
===
selector
)
{
if
(
oldVal
&&
!
equals
(
newVal
,
oldVal
))
{
removeClass
(
oldVal
);
var
newClasses
=
flattenClasses
(
newVal
||
''
);
if
(
!
oldVal
)
{
attr
.
$addClass
(
newClasses
);
}
else
if
(
!
equals
(
newVal
,
oldVal
))
{
attr
.
$updateClass
(
newClasses
,
flattenClasses
(
oldVal
));
}
addClass
(
newVal
);
}
oldVal
=
copy
(
newVal
);
}
function
removeClass
(
classVal
)
{
attr
.
$removeClass
(
flattenClasses
(
classVal
));
}
function
addClass
(
classVal
)
{
attr
.
$addClass
(
flattenClasses
(
classVal
));
}
function
flattenClasses
(
classVal
)
{
if
(
isArray
(
classVal
))
{
return
classVal
.
join
(
'
'
);
...
...
@@ -17436,7 +17624,8 @@ var ngCloakDirective = ngDirective({
var
ngControllerDirective
=
[
function
()
{
return
{
scope
:
true
,
controller
:
'
@
'
controller
:
'
@
'
,
priority
:
500
};
}];
...
...
@@ -17920,9 +18109,12 @@ var ngIfDirective = ['$animate', function($animate) {
if
(
!
childScope
)
{
childScope
=
$scope
.
$new
();
$transclude
(
childScope
,
function
(
clone
)
{
clone
[
clone
.
length
++
]
=
document
.
createComment
(
'
end ngIf:
'
+
$attr
.
ngIf
+
'
'
);
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when it's template arrives.
block
=
{
startNode
:
clone
[
0
],
endNode
:
clone
[
clone
.
length
++
]
=
document
.
createComment
(
'
end ngIf:
'
+
$attr
.
ngIf
+
'
'
)
clone
:
clone
};
$animate
.
enter
(
clone
,
$element
.
parent
(),
$element
);
});
...
...
@@ -17935,7 +18127,7 @@ var ngIfDirective = ['$animate', function($animate) {
}
if
(
block
)
{
$animate
.
leave
(
getBlockElements
(
block
));
$animate
.
leave
(
getBlockElements
(
block
.
clone
));
block
=
null
;
}
}
...
...
@@ -18091,13 +18283,14 @@ var ngIfDirective = ['$animate', function($animate) {
* @description
* Emitted every time the ngInclude content is reloaded.
*/
var
ngIncludeDirective
=
[
'
$http
'
,
'
$templateCache
'
,
'
$anchorScroll
'
,
'
$
compile
'
,
'
$
animate
'
,
'
$sce
'
,
function
(
$http
,
$templateCache
,
$anchorScroll
,
$
compile
,
$
animate
,
$sce
)
{
var
ngIncludeDirective
=
[
'
$http
'
,
'
$templateCache
'
,
'
$anchorScroll
'
,
'
$animate
'
,
'
$sce
'
,
function
(
$http
,
$templateCache
,
$anchorScroll
,
$animate
,
$sce
)
{
return
{
restrict
:
'
ECA
'
,
priority
:
400
,
terminal
:
true
,
transclude
:
'
element
'
,
controller
:
angular
.
noop
,
compile
:
function
(
element
,
attr
)
{
var
srcExp
=
attr
.
ngInclude
||
attr
.
src
,
onloadExp
=
attr
.
onload
||
''
,
...
...
@@ -18131,25 +18324,31 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
$http
.
get
(
src
,
{
cache
:
$templateCache
}).
success
(
function
(
response
)
{
if
(
thisChangeId
!==
changeCounter
)
return
;
var
newScope
=
scope
.
$new
();
$transclude
(
newScope
,
function
(
clone
)
{
ctrl
.
template
=
response
;
// Note: This will also link all children of ng-include that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-include on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var
clone
=
$transclude
(
newScope
,
function
(
clone
)
{
cleanupLastIncludeContent
();
$animate
.
enter
(
clone
,
null
,
$element
,
afterAnimation
);
});
currentScope
=
newScope
;
currentElement
=
clone
;
currentScope
=
newScope
;
currentElement
=
clone
;
currentElement
.
html
(
response
);
$animate
.
enter
(
currentElement
,
null
,
$element
,
afterAnimation
);
$compile
(
currentElement
.
contents
())(
currentScope
);
currentScope
.
$emit
(
'
$includeContentLoaded
'
);
scope
.
$eval
(
onloadExp
);
});
currentScope
.
$emit
(
'
$includeContentLoaded
'
);
scope
.
$eval
(
onloadExp
);
}).
error
(
function
()
{
if
(
thisChangeId
===
changeCounter
)
cleanupLastIncludeContent
();
});
scope
.
$emit
(
'
$includeContentRequested
'
);
}
else
{
cleanupLastIncludeContent
();
ctrl
.
template
=
null
;
}
});
};
...
...
@@ -18157,6 +18356,24 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
};
}];
// This directive is called during the $transclude call of the first `ngInclude` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngInclude
// is called.
var
ngIncludeFillContentDirective
=
[
'
$compile
'
,
function
(
$compile
)
{
return
{
restrict
:
'
ECA
'
,
priority
:
-
400
,
require
:
'
ngInclude
'
,
link
:
function
(
scope
,
$element
,
$attr
,
ctrl
)
{
$element
.
html
(
ctrl
.
template
);
$compile
(
$element
.
contents
())(
scope
);
}
};
}];
/**
* @ngdoc directive
* @name ng.directive:ngInit
...
...
@@ -18173,6 +18390,8 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
* to initialize values on a scope.
* </div>
*
* @priority 450
*
* @element ANY
* @param {expression} ngInit {@link guide/expression Expression} to eval.
*
...
...
@@ -18204,6 +18423,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
</doc:example>
*/
var
ngInitDirective
=
ngDirective
({
priority
:
450
,
compile
:
function
()
{
return
{
pre
:
function
(
scope
,
element
,
attrs
)
{
...
...
@@ -18298,7 +18518,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
* other numbers, for example 12, so that instead of showing "12 people are viewing", you can
* show "a dozen people are viewing".
*
* You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
* You can use a set of closed braces
(`{}`) as a placeholder for the number that you want substituted
* into pluralized strings. In the previous example, Angular will replace `{}` with
* <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
* for <span ng-non-bindable>{{numberExpression}}</span>.
...
...
@@ -18559,7 +18779,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
* For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
* `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
* with the corresponding item in the array by identity. Moving the same object in array would move the DOM
* element in the same way i
a
n the DOM.
* element in the same way in the DOM.
*
* For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
* case the object identity does not matter. Two objects are considered equivalent as long as their `id`
...
...
@@ -18761,7 +18981,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
}
else
if
(
nextBlockMap
.
hasOwnProperty
(
trackById
))
{
// restore lastBlockMap
forEach
(
nextBlockOrder
,
function
(
block
)
{
if
(
block
&&
block
.
s
tartNod
e
)
lastBlockMap
[
block
.
id
]
=
block
;
if
(
block
&&
block
.
s
cop
e
)
lastBlockMap
[
block
.
id
]
=
block
;
});
// This is a duplicate and we need to throw an error
throw
ngRepeatMinErr
(
'
dupes
'
,
"
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}
"
,
...
...
@@ -18778,7 +18998,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
// lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn
if
(
lastBlockMap
.
hasOwnProperty
(
key
))
{
block
=
lastBlockMap
[
key
];
elementsToRemove
=
getBlockElements
(
block
);
elementsToRemove
=
getBlockElements
(
block
.
clone
);
$animate
.
leave
(
elementsToRemove
);
forEach
(
elementsToRemove
,
function
(
element
)
{
element
[
NG_REMOVED
]
=
true
;
});
block
.
scope
.
$destroy
();
...
...
@@ -18790,9 +19010,9 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
key
=
(
collection
===
collectionKeys
)
?
index
:
collectionKeys
[
index
];
value
=
collection
[
key
];
block
=
nextBlockOrder
[
index
];
if
(
nextBlockOrder
[
index
-
1
])
previousNode
=
nextBlockOrder
[
index
-
1
].
endNode
;
if
(
nextBlockOrder
[
index
-
1
])
previousNode
=
getBlockEnd
(
nextBlockOrder
[
index
-
1
])
;
if
(
block
.
s
tartNod
e
)
{
if
(
block
.
s
cop
e
)
{
// if we have already seen this object, then we need to reuse the
// associated scope/element
childScope
=
block
.
scope
;
...
...
@@ -18802,11 +19022,11 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
nextNode
=
nextNode
.
nextSibling
;
}
while
(
nextNode
&&
nextNode
[
NG_REMOVED
]);
if
(
block
.
startNode
!=
nextNode
)
{
if
(
getBlockStart
(
block
)
!=
nextNode
)
{
// existing item which got moved
$animate
.
move
(
getBlockElements
(
block
),
null
,
jqLite
(
previousNode
));
$animate
.
move
(
getBlockElements
(
block
.
clone
),
null
,
jqLite
(
previousNode
));
}
previousNode
=
block
.
endNode
;
previousNode
=
getBlockEnd
(
block
)
;
}
else
{
// new item which we don't know about
childScope
=
$scope
.
$new
();
...
...
@@ -18822,14 +19042,16 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
childScope
.
$odd
=
!
(
childScope
.
$even
=
(
index
&
1
)
===
0
);
// jshint bitwise: true
if
(
!
block
.
s
tartNod
e
)
{
if
(
!
block
.
s
cop
e
)
{
$transclude
(
childScope
,
function
(
clone
)
{
clone
[
clone
.
length
++
]
=
document
.
createComment
(
'
end ngRepeat:
'
+
expression
+
'
'
);
$animate
.
enter
(
clone
,
null
,
jqLite
(
previousNode
));
previousNode
=
clone
;
block
.
scope
=
childScope
;
block
.
startNode
=
previousNode
&&
previousNode
.
endNode
?
previousNode
.
endNode
:
clone
[
0
];
block
.
endNode
=
clone
[
clone
.
length
-
1
];
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when it's template arrives.
block
.
clone
=
clone
;
nextBlockMap
[
block
.
id
]
=
block
;
});
}
...
...
@@ -18838,6 +19060,14 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
});
}
};
function
getBlockStart
(
block
)
{
return
block
.
clone
[
0
];
}
function
getBlockEnd
(
block
)
{
return
block
.
clone
[
block
.
clone
.
length
-
1
];
}
}];
/**
...
...
@@ -19190,19 +19420,26 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
* @restrict EA
*
* @description
* The
ngSwitch
directive is used to conditionally swap DOM structure on your template based on a scope expression.
* Elements within
ngSwitch but without ngSwitchWhen or ngSwitchDefault
directives will be preserved at the location
* The
`ngSwitch`
directive is used to conditionally swap DOM structure on your template based on a scope expression.
* Elements within
`ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault`
directives will be preserved at the location
* as specified in the template.
*
* The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
* from the template cache),
ngSwitch
simply choses one of the nested elements and makes it visible based on which element
* from the template cache),
`ngSwitch`
simply choses one of the nested elements and makes it visible based on which element
* matches the value obtained from the evaluated expression. In other words, you define a container element
* (where you place the directive), place an expression on the **
on="..."
attribute**
* (or the **
ng-switch="..."
attribute**), define any inner elements inside of the directive and place
* (where you place the directive), place an expression on the **
`on="..."`
attribute**
* (or the **
`ng-switch="..."`
attribute**), define any inner elements inside of the directive and place
* a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
* expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
* attribute is displayed.
*
* <div class="alert alert-info">
* Be aware that the attribute values to match against cannot be expressions. They are interpreted
* as literal string values to match against.
* For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
* value of the expression `$scope.someVal`.
* </div>
* @animations
* enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
...
...
@@ -19214,6 +19451,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
* <ANY ng-switch-default>...</ANY>
* </ANY>
*
*
* @scope
* @priority 800
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
...
...
@@ -19431,7 +19669,7 @@ var ngTranscludeDirective = ngDirective({
link
:
function
(
$scope
,
$element
,
$attrs
,
controller
)
{
controller
.
$transclude
(
function
(
clone
)
{
$element
.
html
(
''
);
$element
.
empty
(
);
$element
.
append
(
clone
);
});
}
...
...
@@ -19815,13 +20053,13 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
// becomes the compilation root
nullOption
.
removeClass
(
'
ng-scope
'
);
// we need to remove it before calling selectElement.
html(''
) because otherwise IE will
// we need to remove it before calling selectElement.
empty(
) because otherwise IE will
// remove the label from the element. wtf?
nullOption
.
remove
();
}
// clear contents, we'll add what's needed based on the model
selectElement
.
html
(
''
);
selectElement
.
empty
(
);
selectElement
.
on
(
'
change
'
,
function
()
{
scope
.
$apply
(
function
()
{
...
...
@@ -20128,4 +20366,4 @@ var styleDirective = valueFn({
})(
window
,
document
);
!
angular
.
$$csp
()
&&
angular
.
element
(
document
).
find
(
'
head
'
).
prepend
(
'
<style type="text/css">@charset "UTF-8";[ng
\\
:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng
\\
:form{display:block;}.ng-animate-start{clip:rect(0,auto,auto,0);-ms-zoom:1.0001;}.ng-animate-active{clip:rect(-1px,auto,auto,0);-ms-zoom:1;}</style>
'
);
\ No newline at end of file
!
angular
.
$$csp
()
&&
angular
.
element
(
document
).
find
(
'
head
'
).
prepend
(
'
<style type="text/css">@charset "UTF-8";[ng
\\
:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng
\\
:form{display:block;}.ng-animate-start{border-spacing:1px 1px;-ms-zoom:1.0001;}.ng-animate-active{border-spacing:0px 0px;-ms-zoom:1;}</style>
'
);
\ No newline at end of file
architecture-examples/angularjs/index.html
View file @
1239e6a6
<!doctype html>
<html
lang=
"en"
ng-app=
"todomvc"
data-framework=
"angularjs"
>
<html
lang=
"en"
data-framework=
"angularjs"
>
<head>
<meta
charset=
"utf-8"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
>
...
...
@@ -7,60 +7,65 @@
<link
rel=
"stylesheet"
href=
"bower_components/todomvc-common/base.css"
>
<style>
[
ng-cloak
]
{
display
:
none
;
}
</style>
</head>
<body>
<section
id=
"todoapp"
ng-controller=
"TodoCtrl"
>
<header
id=
"header"
>
<h1>
todos
</h1>
<form
id=
"todo-form"
ng-submit=
"addTodo()"
>
<input
id=
"new-todo"
placeholder=
"What needs to be done?"
ng-model=
"newTodo"
autofocus
>
</form>
</header>
<section
id=
"main"
ng-show=
"todos.length"
ng-cloak
>
<input
id=
"toggle-all"
type=
"checkbox"
ng-model=
"allChecked"
ng-click=
"markAll(allChecked)"
>
<label
for=
"toggle-all"
>
Mark all as complete
</label>
<ul
id=
"todo-list"
>
<li
ng-repeat=
"todo in todos | filter:statusFilter track by $index"
ng-class=
"{completed: todo.completed, editing: todo == editedTodo}"
>
<div
class=
"view"
>
<input
class=
"toggle"
type=
"checkbox"
ng-model=
"todo.completed"
>
<label
ng-dblclick=
"editTodo(todo)"
>
{{todo.title}}
</label>
<button
class=
"destroy"
ng-click=
"removeTodo(todo)"
></button>
</div>
<form
ng-submit=
"doneEditing(todo)"
>
<input
class=
"edit"
ng-trim=
"false"
ng-model=
"todo.title"
todo-escape=
"revertEditing(todo)"
ng-blur=
"doneEditing(todo)"
todo-focus=
"todo == editedTodo"
>
</form>
</li>
</ul>
<body
ng-app=
"todomvc"
>
<ng-view
/>
<script
type=
"text/ng-template"
id=
"todomvc-index.html"
>
<
section
id
=
"
todoapp
"
ng
-
controller
=
"
TodoCtrl
"
>
<
header
id
=
"
header
"
>
<
h1
>
todos
<
/h1
>
<
form
id
=
"
todo-form
"
ng
-
submit
=
"
addTodo()
"
>
<
input
id
=
"
new-todo
"
placeholder
=
"
What needs to be done?
"
ng
-
model
=
"
newTodo
"
autofocus
>
<
/form
>
<
/header
>
<
section
id
=
"
main
"
ng
-
show
=
"
todos.length
"
ng
-
cloak
>
<
input
id
=
"
toggle-all
"
type
=
"
checkbox
"
ng
-
model
=
"
allChecked
"
ng
-
click
=
"
markAll(allChecked)
"
>
<
label
for
=
"
toggle-all
"
>
Mark
all
as
complete
<
/label
>
<
ul
id
=
"
todo-list
"
>
<
li
ng
-
repeat
=
"
todo in todos | filter:statusFilter track by $index
"
ng
-
class
=
"
{completed: todo.completed, editing: todo == editedTodo}
"
>
<
div
class
=
"
view
"
>
<
input
class
=
"
toggle
"
type
=
"
checkbox
"
ng
-
model
=
"
todo.completed
"
>
<
label
ng
-
dblclick
=
"
editTodo(todo)
"
>
{{
todo
.
title
}}
<
/label
>
<
button
class
=
"
destroy
"
ng
-
click
=
"
removeTodo(todo)
"
><
/button
>
<
/div
>
<
form
ng
-
submit
=
"
doneEditing(todo)
"
>
<
input
class
=
"
edit
"
ng
-
trim
=
"
false
"
ng
-
model
=
"
todo.title
"
todo
-
escape
=
"
revertEditing(todo)
"
ng
-
blur
=
"
doneEditing(todo)
"
todo
-
focus
=
"
todo == editedTodo
"
>
<
/form
>
<
/li
>
<
/ul
>
<
/section
>
<
footer
id
=
"
footer
"
ng
-
show
=
"
todos.length
"
ng
-
cloak
>
<
span
id
=
"
todo-count
"
><
strong
>
{{
remainingCount
}}
<
/strong
>
<
ng
-
pluralize
count
=
"
remainingCount
"
when
=
"
{ one: 'item left', other: 'items left' }
"
><
/ng-pluralize
>
<
/span
>
<
ul
id
=
"
filters
"
>
<
li
>
<
a
ng
-
class
=
"
{selected: status == ''}
"
href
=
"
#/
"
>
All
<
/a
>
<
/li
>
<
li
>
<
a
ng
-
class
=
"
{selected: status == 'active'}
"
href
=
"
#/active
"
>
Active
<
/a
>
<
/li
>
<
li
>
<
a
ng
-
class
=
"
{selected: status == 'completed'}
"
href
=
"
#/completed
"
>
Completed
<
/a
>
<
/li
>
<
/ul
>
<
button
id
=
"
clear-completed
"
ng
-
click
=
"
clearCompletedTodos()
"
ng
-
show
=
"
completedCount
"
>
Clear
completed
({{
completedCount
}})
<
/button
>
<
/footer
>
<
/section
>
<footer
id=
"footer"
ng-show=
"todos.length"
ng-cloak
>
<span
id=
"todo-count"
><strong>
{{remainingCount}}
</strong>
<ng-pluralize
count=
"remainingCount"
when=
"{ one: 'item left', other: 'items left' }"
></ng-pluralize>
</span>
<ul
id=
"filters"
>
<li>
<a
ng-class=
"{selected: location.path() == '/'} "
href=
"#/"
>
All
</a>
</li>
<li>
<a
ng-class=
"{selected: location.path() == '/active'}"
href=
"#/active"
>
Active
</a>
</li>
<li>
<a
ng-class=
"{selected: location.path() == '/completed'}"
href=
"#/completed"
>
Completed
</a>
</li>
</ul>
<button
id=
"clear-completed"
ng-click=
"clearCompletedTodos()"
ng-show=
"completedCount"
>
Clear completed ({{completedCount}})
</button>
<
footer
id
=
"
info
"
>
<
p
>
Double
-
click
to
edit
a
todo
<
/p
>
<
p
>
Credits
:
<
a
href
=
"
http://twitter.com/cburgdorf
"
>
Christoph
Burgdorf
<
/a>
,
<
a
href
=
"
http://ericbidelman.com
"
>
Eric
Bidelman
<
/a>
,
<
a
href
=
"
http://jacobmumm.com
"
>
Jacob
Mumm
<
/a> an
d
<
a
href
=
"
http://igorminar.com
"
>
Igor
Minar
<
/a
>
<
/p
>
<
p
>
Part
of
<
a
href
=
"
http://todomvc.com
"
>
TodoMVC
<
/a></
p
>
<
/footer
>
</section>
<footer
id=
"info"
>
<p>
Double-click to edit a todo
</p>
<p>
Credits:
<a
href=
"http://twitter.com/cburgdorf"
>
Christoph Burgdorf
</a>
,
<a
href=
"http://ericbidelman.com"
>
Eric Bidelman
</a>
,
<a
href=
"http://jacobmumm.com"
>
Jacob Mumm
</a>
and
<a
href=
"http://igorminar.com"
>
Igor Minar
</a>
</p>
<p>
Part of
<a
href=
"http://todomvc.com"
>
TodoMVC
</a></p>
</footer>
</script>
<script
src=
"bower_components/todomvc-common/base.js"
></script>
<script
src=
"bower_components/angular/angular.js"
></script>
<script
src=
"bower_components/angular-route/angular-route.js"
></script>
<script
src=
"js/app.js"
></script>
<script
src=
"js/controllers/todoCtrl.js"
></script>
<script
src=
"js/services/todoStorage.js"
></script>
...
...
architecture-examples/angularjs/js/app.js
View file @
1239e6a6
...
...
@@ -7,4 +7,15 @@
*
* @type {angular.Module}
*/
var
todomvc
=
angular
.
module
(
'
todomvc
'
,
[]);
var
todomvc
=
angular
.
module
(
'
todomvc
'
,
[
'
ngRoute
'
])
.
config
(
function
(
$routeProvider
)
{
$routeProvider
.
when
(
'
/
'
,
{
controller
:
'
TodoCtrl
'
,
templateUrl
:
'
todomvc-index.html
'
}).
when
(
'
/:status
'
,
{
controller
:
'
TodoCtrl
'
,
templateUrl
:
'
todomvc-index.html
'
}).
otherwise
({
redirectTo
:
'
/
'
});
});
architecture-examples/angularjs/js/controllers/todoCtrl.js
View file @
1239e6a6
...
...
@@ -6,7 +6,7 @@
* - retrieves and persists the model via the todoStorage service
* - exposes the model to the template and provides event handlers
*/
todomvc
.
controller
(
'
TodoCtrl
'
,
function
TodoCtrl
(
$scope
,
$
location
,
todoStorage
,
filterFilter
)
{
todomvc
.
controller
(
'
TodoCtrl
'
,
function
TodoCtrl
(
$scope
,
$
routeParams
,
todoStorage
,
filterFilter
)
{
var
todos
=
$scope
.
todos
=
todoStorage
.
get
();
$scope
.
newTodo
=
''
;
...
...
@@ -21,15 +21,12 @@ todomvc.controller('TodoCtrl', function TodoCtrl($scope, $location, todoStorage,
}
},
true
);
if
(
$location
.
path
()
===
''
)
{
$location
.
path
(
'
/
'
);
}
// Monitor the current route for changes and adjust the filter accordingly.
$scope
.
$on
(
'
$routeChangeSuccess
'
,
function
()
{
var
status
=
$scope
.
status
=
$routeParams
.
status
||
''
;
$scope
.
location
=
$location
;
$scope
.
$watch
(
'
location.path()
'
,
function
(
path
)
{
$scope
.
statusFilter
=
(
path
===
'
/active
'
)
?
{
completed
:
false
}
:
(
path
===
'
/completed
'
)
?
$scope
.
statusFilter
=
(
status
===
'
active
'
)
?
{
completed
:
false
}
:
(
status
===
'
completed
'
)
?
{
completed
:
true
}
:
null
;
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment