summaryrefslogtreecommitdiffstats
path: root/js/vendor/angular
diff options
context:
space:
mode:
Diffstat (limited to 'js/vendor/angular')
-rw-r--r--js/vendor/angular/.bower.json8
-rw-r--r--js/vendor/angular/angular-csp.css2
-rw-r--r--js/vendor/angular/angular.js1819
-rw-r--r--js/vendor/angular/angular.min.js479
-rw-r--r--js/vendor/angular/angular.min.js.gzipbin44238 -> 45101 bytes
-rw-r--r--js/vendor/angular/angular.min.js.map6
-rw-r--r--js/vendor/angular/bower.json2
-rw-r--r--js/vendor/angular/package.json25
8 files changed, 1523 insertions, 818 deletions
diff --git a/js/vendor/angular/.bower.json b/js/vendor/angular/.bower.json
index 66af47a13..5d87ab94c 100644
--- a/js/vendor/angular/.bower.json
+++ b/js/vendor/angular/.bower.json
@@ -1,14 +1,14 @@
{
"name": "angular",
- "version": "1.3.0-rc.4",
+ "version": "1.3.0",
"main": "./angular.js",
"dependencies": {},
"homepage": "https://github.com/angular/bower-angular",
- "_release": "1.3.0-rc.4",
+ "_release": "1.3.0",
"_resolution": {
"type": "version",
- "tag": "v1.3.0-rc.4",
- "commit": "9086c430b6c8aef6d3a2d7e3542c9641413977e8"
+ "tag": "v1.3.0",
+ "commit": "992364cfc43aaa7755d5359c783aba0a2d1a5f4a"
},
"_source": "git://github.com/angular/bower-angular.git",
"_target": "~1.3.*",
diff --git a/js/vendor/angular/angular-csp.css b/js/vendor/angular/angular-csp.css
index 212454025..0ce9d864c 100644
--- a/js/vendor/angular/angular-csp.css
+++ b/js/vendor/angular/angular-csp.css
@@ -4,7 +4,7 @@
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak,
-.ng-hide:not(.ng-animate) {
+.ng-hide:not(.ng-hide-animate) {
display: none !important;
}
diff --git a/js/vendor/angular/angular.js b/js/vendor/angular/angular.js
index f91161493..b804d6490 100644
--- a/js/vendor/angular/angular.js
+++ b/js/vendor/angular/angular.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.3.0-rc.4
+ * @license AngularJS v1.3.0
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -71,7 +71,7 @@ function minErr(module, ErrorConstructor) {
return match;
});
- message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.4/' +
+ message = message + '\nhttp://errors.angularjs.org/1.3.0/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -87,6 +87,7 @@ function minErr(module, ErrorConstructor) {
jqLite: true,
jQuery: true,
slice: true,
+ splice: true,
push: true,
toString: true,
ngMinErr: true,
@@ -163,6 +164,12 @@ function minErr(module, ErrorConstructor) {
getBlockNodes: true,
hasOwnProperty: true,
createMap: true,
+
+ NODE_TYPE_ELEMENT: true,
+ NODE_TYPE_TEXT: true,
+ NODE_TYPE_COMMENT: true,
+ NODE_TYPE_DOCUMENT: true,
+ NODE_TYPE_DOCUMENT_FRAGMENT: true,
*/
////////////////////////////////////
@@ -242,6 +249,7 @@ var /** holds major version number for IE or NaN for real browsers */
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
slice = [].slice,
+ splice = [].splice,
push = [].push,
toString = Object.prototype.toString,
ngMinErr = minErr('ng'),
@@ -252,13 +260,10 @@ var /** holds major version number for IE or NaN for real browsers */
uid = 0;
/**
- * IE 11 changed the format of the UserAgent string.
- * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
+ * documentMode is an IE-only property
+ * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
*/
-msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
-if (isNaN(msie)) {
- msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
-}
+msie = document.documentMode;
/**
@@ -274,7 +279,7 @@ function isArrayLike(obj) {
var length = obj.length;
- if (obj.nodeType === 1 && length) {
+ if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
return true;
}
@@ -408,7 +413,8 @@ function setHashKey(obj, h) {
*
* @description
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
- * to `dst`. You can specify multiple `src` objects.
+ * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
+ * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
@@ -1110,11 +1116,9 @@ function startingTag(element) {
// are not allowed to have children. So we just ignore it.
element.empty();
} catch(e) {}
- // As Per DOM Standards
- var TEXT_NODE = 3;
var elemHtml = jqLite('<div>').append(element).html();
try {
- return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
+ return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
@@ -1693,6 +1697,12 @@ function createMap() {
return Object.create(null);
}
+var NODE_TYPE_ELEMENT = 1;
+var NODE_TYPE_TEXT = 3;
+var NODE_TYPE_COMMENT = 8;
+var NODE_TYPE_DOCUMENT = 9;
+var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
+
/**
* @ngdoc type
* @name angular.Module
@@ -1915,7 +1925,7 @@ function setupModuleLoader(window) {
* })
* ```
*
- * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
+ * See {@link ng.$animateProvider#register $animateProvider.register()} and
* {@link ngAnimate ngAnimate module} for more information.
*/
animation: invokeLater('$animateProvider', 'register'),
@@ -1965,7 +1975,7 @@ function setupModuleLoader(window) {
* @description
* Use this method to register work which needs to be performed on module loading.
* For more about how to configure services, see
- * {@link providers#providers_provider-recipe Provider Recipe}.
+ * {@link providers#provider-recipe Provider Recipe}.
*/
config: config,
@@ -2112,11 +2122,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.3.0-rc.4', // all of these placeholder strings will be replaced by grunt's
+ full: '1.3.0', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 3,
dot: 0,
- codeName: 'unicorn-hydrafication'
+ codeName: 'superluminal-nudge'
};
@@ -2150,8 +2160,7 @@ function publishExternalAPI(angular){
'getTestability': getTestability,
'$$minErr': minErr,
'$$csp': csp,
- 'reloadWithDebugInfo': reloadWithDebugInfo,
- '$$hasClass': jqLiteHasClass
+ 'reloadWithDebugInfo': reloadWithDebugInfo
});
angularModule = setupModuleLoader(window);
@@ -2292,12 +2301,12 @@ function publishExternalAPI(angular){
* - [`addClass()`](http://api.jquery.com/addClass/)
* - [`after()`](http://api.jquery.com/after/)
* - [`append()`](http://api.jquery.com/append/)
- * - [`attr()`](http://api.jquery.com/attr/)
+ * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
* - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/)
- * - [`css()`](http://api.jquery.com/css/)
+ * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()`
* - [`data()`](http://api.jquery.com/data/)
* - [`detach()`](http://api.jquery.com/detach/)
* - [`empty()`](http://api.jquery.com/empty/)
@@ -2419,7 +2428,7 @@ function jqLiteAcceptsData(node) {
// The window object can accept data but has no nodeType
// Otherwise we are only interested in elements (1) and documents (9)
var nodeType = node.nodeType;
- return nodeType === 1 || !nodeType || nodeType === 9;
+ return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
}
function jqLiteBuildFragment(html, context) {
@@ -2527,18 +2536,22 @@ function jqLiteOff(element, type, fn, unsupported) {
if (!type) {
for (type in events) {
if (type !== '$destroy') {
- removeEventListenerFn(element, type, events[type]);
+ removeEventListenerFn(element, type, handle);
}
delete events[type];
}
} else {
forEach(type.split(' '), function(type) {
- if (isUndefined(fn)) {
- removeEventListenerFn(element, type, events[type]);
- delete events[type];
- } else {
- arrayRemove(events[type] || [], fn);
+ if (isDefined(fn)) {
+ var listenerFns = events[type];
+ arrayRemove(listenerFns || [], fn);
+ if (listenerFns && listenerFns.length > 0) {
+ return;
+ }
}
+
+ removeEventListenerFn(element, type, handle);
+ delete events[type];
});
}
}
@@ -2672,7 +2685,7 @@ function jqLiteController(element, name) {
function jqLiteInheritedData(element, name, value) {
// if element is the document object work with the html element instead
// this makes $(document).scope() possible
- if(element.nodeType == 9) {
+ if(element.nodeType == NODE_TYPE_DOCUMENT) {
element = element.documentElement;
}
var names = isArray(name) ? name : [name];
@@ -2685,7 +2698,7 @@ function jqLiteInheritedData(element, name, value) {
// If dealing with a document fragment node with a host element, and no parent, use the host
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
// to lookup parent controllers.
- element = element.parentNode || (element.nodeType === 11 && element.host);
+ element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
}
}
@@ -2702,6 +2715,20 @@ function jqLiteRemove(element, keepData) {
if (parent) parent.removeChild(element);
}
+
+function jqLiteDocumentLoaded(action, win) {
+ win = win || window;
+ if (win.document.readyState === 'complete') {
+ // Force the action to be run async for consistent behaviour
+ // from the action's point of view
+ // i.e. it will definitely not be in a $apply
+ win.setTimeout(action);
+ } else {
+ // No need to unbind this handler as load is only ever called once
+ jqLite(win).on('load', action);
+ }
+}
+
//////////////////////////////////////////
// Functions which are declared directly.
//////////////////////////////////////////
@@ -2863,7 +2890,7 @@ forEach({
function getText(element, value) {
if (isUndefined(value)) {
var nodeType = element.nodeType;
- return (nodeType === 1 || nodeType === 3) ? element.textContent : '';
+ return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
}
element.textContent = value;
}
@@ -3085,7 +3112,7 @@ forEach({
children: function(element) {
var children = [];
forEach(element.childNodes, function(element){
- if (element.nodeType === 1)
+ if (element.nodeType === NODE_TYPE_ELEMENT)
children.push(element);
});
return children;
@@ -3097,7 +3124,7 @@ forEach({
append: function(element, node) {
var nodeType = element.nodeType;
- if (nodeType !== 1 && nodeType !== 11) return;
+ if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
node = new JQLite(node);
@@ -3108,7 +3135,7 @@ forEach({
},
prepend: function(element, node) {
- if (element.nodeType === 1) {
+ if (element.nodeType === NODE_TYPE_ELEMENT) {
var index = element.firstChild;
forEach(new JQLite(node), function(child){
element.insertBefore(child, index);
@@ -3159,7 +3186,7 @@ forEach({
parent: function(element) {
var parent = element.parentNode;
- return parent && parent.nodeType !== 11 ? parent : null;
+ return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
},
next: function(element) {
@@ -3318,13 +3345,13 @@ HashMap.prototype = {
* @kind function
*
* @description
- * Creates an injector function that can be used for retrieving services as well as for
+ * Creates an injector object that can be used for retrieving services as well as for
* dependency injection (see {@link guide/di dependency injection}).
*
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See
* {@link angular.module}. The `ng` module must be explicitly added.
- * @returns {function()} Injector function. See {@link auto.$injector $injector}.
+ * @returns {injector} Injector object. See {@link auto.$injector $injector}.
*
* @example
* Typical usage
@@ -3431,7 +3458,6 @@ function annotate(fn, strictDi, name) {
/**
* @ngdoc service
* @name $injector
- * @kind function
*
* @description
*
@@ -3446,7 +3472,7 @@ function annotate(fn, strictDi, name) {
* expect($injector.get('$injector')).toBe($injector);
* expect($injector.invoke(function($injector) {
* return $injector;
- * }).toBe($injector);
+ * })).toBe($injector);
* ```
*
* # Injection Function Annotation
@@ -3513,8 +3539,8 @@ function annotate(fn, strictDi, name) {
* @description
* Allows the user to query if the particular service exists.
*
- * @param {string} Name of the service to query.
- * @returns {boolean} returns true if injector has given service.
+ * @param {string} name Name of the service to query.
+ * @returns {boolean} `true` if injector has given service.
*/
/**
@@ -3974,7 +4000,21 @@ function createInjector(modulesToLoad, strictDi) {
return providerCache[name + providerSuffix] = provider_;
}
- function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
+ function enforceReturnValue(name, factory) {
+ return function enforcedReturnValue() {
+ var result = instanceInjector.invoke(factory, this, undefined, name);
+ if (isUndefined(result)) {
+ throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
+ }
+ return result;
+ };
+ }
+
+ function factory(name, factoryFn, enforce) {
+ return provider(name, {
+ $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
+ });
+ }
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
@@ -3982,7 +4022,7 @@ function createInjector(modulesToLoad, strictDi) {
}]);
}
- function value(name, val) { return factory(name, valueFn(val)); }
+ function value(name, val) { return factory(name, valueFn(val), false); }
function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
@@ -4140,101 +4180,265 @@ function createInjector(modulesToLoad, strictDi) {
createInjector.$$annotate = annotate;
/**
- * @ngdoc service
- * @name $anchorScroll
- * @kind function
- * @requires $window
- * @requires $location
- * @requires $rootScope
+ * @ngdoc provider
+ * @name $anchorScrollProvider
*
* @description
- * When called, it checks current value of `$location.hash()` and scrolls to the related element,
- * according to rules specified in
- * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
- *
- * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor.
- * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
- *
- * @example
- <example module="anchorScrollExample">
- <file name="index.html">
- <div id="scrollArea" ng-controller="ScrollController">
- <a ng-click="gotoBottom()">Go to bottom</a>
- <a id="bottom"></a> You're at the bottom!
- </div>
- </file>
- <file name="script.js">
- angular.module('anchorScrollExample', [])
- .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
- function ($scope, $location, $anchorScroll) {
- $scope.gotoBottom = function() {
- // set the location.hash to the id of
- // the element you wish to scroll to.
- $location.hash('bottom');
-
- // call $anchorScroll()
- $anchorScroll();
- };
- }]);
- </file>
- <file name="style.css">
- #scrollArea {
- height: 350px;
- overflow: auto;
- }
-
- #bottom {
- display: block;
- margin-top: 2000px;
- }
- </file>
- </example>
+ * Use `$anchorScrollProvider` to disable automatic scrolling whenever
+ * {@link ng.$location#hash $location.hash()} changes.
*/
function $AnchorScrollProvider() {
var autoScrollingEnabled = true;
+ /**
+ * @ngdoc method
+ * @name $anchorScrollProvider#disableAutoScrolling
+ *
+ * @description
+ * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically will detect changes to
+ * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
+ * Use this method to disable automatic scrolling.
+ *
+ * If automatic scrolling is disabled, one must explicitly call
+ * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
+ * current hash.
+ */
this.disableAutoScrolling = function() {
autoScrollingEnabled = false;
};
+ /**
+ * @ngdoc service
+ * @name $anchorScroll
+ * @kind function
+ * @requires $window
+ * @requires $location
+ * @requires $rootScope
+ *
+ * @description
+ * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
+ * scrolls to the related element, according to the rules specified in the
+ * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
+ *
+ * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
+ * match any anchor whenever it changes. This can be disabled by calling
+ * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
+ *
+ * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
+ * vertical scroll-offset (either fixed or dynamic).
+ *
+ * @property {(number|function|jqLite)} yOffset
+ * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
+ * positioned elements at the top of the page, such as navbars, headers etc.
+ *
+ * `yOffset` can be specified in various ways:
+ * - **number**: A fixed number of pixels to be used as offset.<br /><br />
+ * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
+ * a number representing the offset (in pixels).<br /><br />
+ * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
+ * the top of the page to the element's bottom will be used as offset.<br />
+ * **Note**: The element will be taken into account only as long as its `position` is set to
+ * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
+ * their height and/or positioning according to the viewport's size.
+ *
+ * <br />
+ * <div class="alert alert-warning">
+ * In order for `yOffset` to work properly, scrolling should take place on the document's root and
+ * not some child element.
+ * </div>
+ *
+ * @example
+ <example module="anchorScrollExample">
+ <file name="index.html">
+ <div id="scrollArea" ng-controller="ScrollController">
+ <a ng-click="gotoBottom()">Go to bottom</a>
+ <a id="bottom"></a> You're at the bottom!
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('anchorScrollExample', [])
+ .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
+ function ($scope, $location, $anchorScroll) {
+ $scope.gotoBottom = function() {
+ // set the location.hash to the id of
+ // the element you wish to scroll to.
+ $location.hash('bottom');
+
+ // call $anchorScroll()
+ $anchorScroll();
+ };
+ }]);
+ </file>
+ <file name="style.css">
+ #scrollArea {
+ height: 280px;
+ overflow: auto;
+ }
+
+ #bottom {
+ display: block;
+ margin-top: 2000px;
+ }
+ </file>
+ </example>
+ *
+ * <hr />
+ * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
+ * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
+ *
+ * @example
+ <example module="anchorScrollOffsetExample">
+ <file name="index.html">
+ <div class="fixed-header" ng-controller="headerCtrl">
+ <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
+ Go to anchor {{x}}
+ </a>
+ </div>
+ <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
+ Anchor {{x}} of 5
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('anchorScrollOffsetExample', [])
+ .run(['$anchorScroll', function($anchorScroll) {
+ $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
+ }])
+ .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
+ function ($anchorScroll, $location, $scope) {
+ $scope.gotoAnchor = function(x) {
+ var newHash = 'anchor' + x;
+ if ($location.hash() !== newHash) {
+ // set the $location.hash to `newHash` and
+ // $anchorScroll will automatically scroll to it
+ $location.hash('anchor' + x);
+ } else {
+ // call $anchorScroll() explicitly,
+ // since $location.hash hasn't changed
+ $anchorScroll();
+ }
+ };
+ }
+ ]);
+ </file>
+ <file name="style.css">
+ body {
+ padding-top: 50px;
+ }
+
+ .anchor {
+ border: 2px dashed DarkOrchid;
+ padding: 10px 10px 200px 10px;
+ }
+
+ .fixed-header {
+ background-color: rgba(0, 0, 0, 0.2);
+ height: 50px;
+ position: fixed;
+ top: 0; left: 0; right: 0;
+ }
+
+ .fixed-header > a {
+ display: inline-block;
+ margin: 5px 15px;
+ }
+ </file>
+ </example>
+ */
this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
var document = $window.document;
+ var scrollScheduled = false;
- // helper function to get first anchor from a NodeList
- // can't use filter.filter, as it accepts only instances of Array
- // and IE can't convert NodeList to an array using [].slice
- // TODO(vojta): use filter if we change it to accept lists as well
+ // Helper function to get first anchor from a NodeList
+ // (using `Array#some()` instead of `angular#forEach()` since it's more performant
+ // and working in all supported browsers.)
function getFirstAnchor(list) {
var result = null;
- forEach(list, function(element) {
- if (!result && nodeName_(element) === 'a') result = element;
+ Array.prototype.some.call(list, function(element) {
+ if (nodeName_(element) === 'a') {
+ result = element;
+ return true;
+ }
});
return result;
}
+ function getYOffset() {
+
+ var offset = scroll.yOffset;
+
+ if (isFunction(offset)) {
+ offset = offset();
+ } else if (isElement(offset)) {
+ var elem = offset[0];
+ var style = $window.getComputedStyle(elem);
+ if (style.position !== 'fixed') {
+ offset = 0;
+ } else {
+ offset = elem.getBoundingClientRect().bottom;
+ }
+ } else if (!isNumber(offset)) {
+ offset = 0;
+ }
+
+ return offset;
+ }
+
+ function scrollTo(elem) {
+ if (elem) {
+ elem.scrollIntoView();
+
+ var offset = getYOffset();
+
+ if (offset) {
+ // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
+ // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
+ // top of the viewport.
+ //
+ // IF the number of pixels from the top of `elem` to the end of the page's content is less
+ // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
+ // way down the page.
+ //
+ // This is often the case for elements near the bottom of the page.
+ //
+ // In such cases we do not need to scroll the whole `offset` up, just the difference between
+ // the top of the element and the offset, which is enough to align the top of `elem` at the
+ // desired position.
+ var elemTop = elem.getBoundingClientRect().top;
+ $window.scrollBy(0, elemTop - offset);
+ }
+ } else {
+ $window.scrollTo(0, 0);
+ }
+ }
+
function scroll() {
var hash = $location.hash(), elm;
// empty hash, scroll to the top of the page
- if (!hash) $window.scrollTo(0, 0);
+ if (!hash) scrollTo(null);
// element with given id
- else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
+ else if ((elm = document.getElementById(hash))) scrollTo(elm);
// first anchor with given name :-D
- else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
+ else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
// no element and hash == 'top', scroll to the top of the page
- else if (hash === 'top') $window.scrollTo(0, 0);
+ else if (hash === 'top') scrollTo(null);
}
// does not scroll when user clicks on anchor link that is currently on
// (no url change, no $location.hash() change), browser native does scroll
if (autoScrollingEnabled) {
$rootScope.$watch(function autoScrollWatch() {return $location.hash();},
- function autoScrollWatchAction() {
- $rootScope.$evalAsync(scroll);
+ function autoScrollWatchAction(newVal, oldVal) {
+ // skip the initial scroll if $location.hash is empty
+ if (newVal === oldVal && newVal === '') return;
+
+ jqLiteDocumentLoaded(function() {
+ $rootScope.$evalAsync(scroll);
+ });
});
}
@@ -4323,9 +4527,58 @@ var $AnimateProvider = ['$provide', function($provide) {
return this.$$classNameFilter;
};
- this.$get = ['$$q', '$$asyncCallback', function($$q, $$asyncCallback) {
+ this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
var currentDefer;
+
+ function runAnimationPostDigest(fn) {
+ var cancelFn, defer = $$q.defer();
+ defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
+ cancelFn && cancelFn();
+ };
+
+ $rootScope.$$postDigest(function ngAnimatePostDigest() {
+ cancelFn = fn(function ngAnimateNotifyComplete() {
+ defer.resolve();
+ });
+ });
+
+ return defer.promise;
+ }
+
+ function resolveElementClasses(element, classes) {
+ var toAdd = [], toRemove = [];
+
+ var hasClasses = createMap();
+ forEach((element.attr('class') || '').split(/\s+/), function(className) {
+ hasClasses[className] = true;
+ });
+
+ forEach(classes, function(status, className) {
+ var hasClass = hasClasses[className];
+
+ // If the most recent class manipulation (via $animate) was to remove the class, and the
+ // element currently has the class, the class is scheduled for removal. Otherwise, if
+ // the most recent class manipulation (via $animate) was to add the class, and the
+ // element does not currently have the class, the class is scheduled to be added.
+ if (status === false && hasClass) {
+ toRemove.push(className);
+ } else if (status === true && !hasClass) {
+ toAdd.push(className);
+ }
+ });
+
+ return (toAdd.length + toRemove.length) > 0 &&
+ [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
+ }
+
+ function cachedClassManipulation(cache, classes, op) {
+ for (var i=0, ii = classes.length; i < ii; ++i) {
+ var className = classes[i];
+ cache[className] = op;
+ }
+ }
+
function asyncPromise() {
// only serve one instance of a promise in order to save CPU cycles
if (!currentDefer) {
@@ -4338,6 +4591,13 @@ var $AnimateProvider = ['$provide', function($provide) {
return currentDefer.promise;
}
+ function applyStyles(element, options) {
+ if (angular.isObject(options)) {
+ var styles = extend(options.from || {}, options.to || {});
+ element.css(styles);
+ }
+ }
+
/**
*
* @ngdoc service
@@ -4356,6 +4616,10 @@ var $AnimateProvider = ['$provide', function($provide) {
* page}.
*/
return {
+ animate : function(element, from, to) {
+ applyStyles(element, { from: from, to: to });
+ return asyncPromise();
+ },
/**
*
@@ -4370,9 +4634,11 @@ var $AnimateProvider = ['$provide', function($provide) {
* a child (if the after element is not present)
* @param {DOMElement} after the sibling element which will append the element
* after itself
+ * @param {object=} options an optional collection of styles that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- enter : function(element, parent, after) {
+ enter : function(element, parent, after, options) {
+ applyStyles(element, options);
after ? after.after(element)
: parent.prepend(element);
return asyncPromise();
@@ -4386,9 +4652,10 @@ var $AnimateProvider = ['$provide', function($provide) {
* @description Removes the element from the DOM. When the function is called a promise
* is returned that will be resolved at a later time.
* @param {DOMElement} element the element which will be removed from the DOM
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- leave : function(element) {
+ leave : function(element, options) {
element.remove();
return asyncPromise();
},
@@ -4408,12 +4675,13 @@ var $AnimateProvider = ['$provide', function($provide) {
* inserted into (if the after element is not present)
* @param {DOMElement} after the sibling element where the element will be
* positioned next to
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- move : function(element, parent, after) {
+ move : function(element, parent, after, options) {
// Do not remove element before insert. Removing will cause data associated with the
// element to be dropped. Insert will implicitly do the remove.
- return this.enter(element, parent, after);
+ return this.enter(element, parent, after, options);
},
/**
@@ -4426,15 +4694,22 @@ var $AnimateProvider = ['$provide', function($provide) {
* @param {DOMElement} element the element which will have the className value
* added to it
* @param {string} className the CSS class which will be added to the element
+ * @param {object=} options an optional collection of options that will be applied to the element.
* @return {Promise} the animation callback promise
*/
- addClass : function(element, className) {
+ addClass : function(element, className, options) {
+ return this.setClass(element, className, [], options);
+ },
+
+ $$addClassImmediately : function(element, className, options) {
+ element = jqLite(element);
className = !isString(className)
? (isArray(className) ? className.join(' ') : '')
: className;
forEach(element, function (element) {
jqLiteAddClass(element, className);
});
+ applyStyles(element, options);
return asyncPromise();
},
@@ -4448,15 +4723,22 @@ var $AnimateProvider = ['$provide', function($provide) {
* @param {DOMElement} element the element which will have the className value
* removed from it
* @param {string} className the CSS class which will be removed from the element
+ * @param {object=} options an optional collection of options that will be applied to the element.