diff --git a/README.md b/README.md
index bd4dfdd..8589bbf 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,12 @@ Configuration Changes Needed for Djangular
'djangular.middleware.AngularJsonVulnerabilityMiddleware'
)
++ Finally, if your project root's settings file is not located in the root folder,
+ or not named settings.py, you'll need to explictly tell djangular where your
+ project root is by setting `DJANGULAR_TEMPLATE_PATH` in your enviroment.
+
+ export DJANGULAR_TEMPLATE_PATH='project_root'
+
Comparison between Djangular and Angular-Seed
---------------------------------------------
diff --git a/djangular/static/lib/angular/angular.js b/djangular/static/lib/angular/angular.js
old mode 100755
new mode 100644
index a860c85..5a732aa
--- a/djangular/static/lib/angular/angular.js
+++ b/djangular/static/lib/angular/angular.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.0.7
+ * @license AngularJS v1.1.5
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -61,12 +61,31 @@ var /** holds major version number for IE or NaN for real browsers */
push = [].push,
toString = Object.prototype.toString,
+
+ _angular = window.angular,
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
nodeName_,
uid = ['0', '0', '0'];
+/**
+ * @ngdoc function
+ * @name angular.noConflict
+ * @function
+ *
+ * @description
+ * Restores the previous global value of angular and returns the current instance. Other libraries may already use the
+ * angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to
+ * restore the previous namespace and keep a reference to angular.
+ *
+ * @return {Object} The current angular namespace
+ */
+function noConflict() {
+ var a = window.angular;
+ window.angular = _angular;
+ return a;
+}
/**
* @private
@@ -89,7 +108,6 @@ function isArrayLike(obj) {
}
}
-
/**
* @ngdoc function
* @name angular.forEach
@@ -251,6 +269,11 @@ function inherit(parent, extra) {
return extend(new (extend(function() {}, {prototype:parent}))(), extra);
}
+var START_SPACE = /^\s*/;
+var END_SPACE = /\s*$/;
+function stripWhitespace(str) {
+ return isString(str) ? str.replace(START_SPACE, '').replace(END_SPACE, '') : str;
+}
/**
* @ngdoc function
@@ -637,7 +660,7 @@ function shallowCopy(src, dst) {
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
* * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
*
- * During a property comparision, properties of `function` type and properties with names
+ * During a property comparison, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
* Scope and DOMWindow objects are being compared only by identify (`===`).
@@ -844,7 +867,7 @@ function toKeyValue(obj) {
/**
- * We need our custom method because encodeURIComponent is too agressive and doesn't follow
+ * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
* segments:
* segment = *pchar
@@ -864,7 +887,7 @@ function encodeUriSegment(val) {
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
- * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
+ * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
* encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
@@ -976,12 +999,13 @@ function bootstrap(element, modules) {
}]);
modules.unshift('ng');
var injector = createInjector(modules);
- injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
- function(scope, element, compile, injector) {
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animator',
+ function(scope, element, compile, injector, animator) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
+ animator.enabled(true);
}]
);
return injector;
@@ -1214,6 +1238,33 @@ function setupModuleLoader(window) {
*/
constant: invokeLater('$provide', 'constant', 'unshift'),
+ /**
+ * @ngdoc method
+ * @name angular.Module#animation
+ * @methodOf angular.Module
+ * @param {string} name animation name
+ * @param {Function} animationFactory Factory function for creating new instance of an animation.
+ * @description
+ *
+ * Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate}
+ * alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives.
+ *
+ * module.animation('animation-name', function($inject1, $inject2) {
+ * return {
+ * //this gets called in preparation to setup an animation
+ * setup : function(element) { ... },
+ *
+ * //this gets called once the animation is run
+ * start : function(element, done, memo) { ... }
+ * }
+ * })
+ *
+ *
+ * See {@link ng.$animationProvider#register $animationProvider.register()} and
+ * {@link ng.directive:ngAnimate ngAnimate} for more information.
+ */
+ animation: invokeLater('$animationProvider', 'register'),
+
/**
* @ngdoc method
* @name angular.Module#filter
@@ -1313,11 +1364,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.0.7', // all of these placeholder strings will be replaced by grunt's
+ full: '1.1.5', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
- minor: 0,
- dot: 7,
- codeName: 'monochromatic-rainbow'
+ minor: 1,
+ dot: 5,
+ codeName: 'triangle-squarification'
};
@@ -1347,7 +1398,8 @@ function publishExternalAPI(angular){
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
- 'callbacks': {counter: 0}
+ 'callbacks': {counter: 0},
+ 'noConflict': noConflict
});
angularModule = setupModuleLoader(window);
@@ -1380,6 +1432,7 @@ function publishExternalAPI(angular){
ngController: ngControllerDirective,
ngForm: ngFormDirective,
ngHide: ngHideDirective,
+ ngIf: ngIfDirective,
ngInclude: ngIncludeDirective,
ngInit: ngInitDirective,
ngNonBindable: ngNonBindableDirective,
@@ -1405,6 +1458,8 @@ function publishExternalAPI(angular){
directive(ngEventDirectives);
$provide.provider({
$anchorScroll: $AnchorScrollProvider,
+ $animation: $AnimationProvider,
+ $animator: $AnimatorProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
@@ -1484,12 +1539,12 @@ function publishExternalAPI(angular){
* - [replaceWith()](http://api.jquery.com/replaceWith/)
* - [text()](http://api.jquery.com/text/)
* - [toggleClass()](http://api.jquery.com/toggleClass/)
- * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
+ * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
* - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
* - [val()](http://api.jquery.com/val/)
* - [wrap()](http://api.jquery.com/wrap/)
*
- * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
+ * ## In addition to the above, Angular provides additional methods to both jQuery and jQuery lite:
*
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
* retrieves controller associated with the `ngController` directive. If `name` is provided as
@@ -1757,9 +1812,14 @@ var JQLitePrototype = JQLite.prototype = {
fn();
}
- this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9
- // we can not use jqLite since we are not done loading and jQuery could be loaded later.
- JQLite(window).bind('load', trigger); // fallback to window.onload for others
+ // check if document already is loaded
+ if (document.readyState === 'complete'){
+ setTimeout(trigger);
+ } else {
+ this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9
+ // we can not use jqLite since we are not done loading and jQuery could be loaded later.
+ JQLite(window).bind('load', trigger); // fallback to window.onload for others
+ }
},
toString: function() {
var value = [];
@@ -1783,11 +1843,11 @@ var JQLitePrototype = JQLite.prototype = {
// value on get.
//////////////////////////////////////////
var BOOLEAN_ATTR = {};
-forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) {
+forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
BOOLEAN_ATTR[lowercase(value)] = value;
});
var BOOLEAN_ELEMENTS = {};
-forEach('input,select,option,textarea,button,form'.split(','), function(value) {
+forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
BOOLEAN_ELEMENTS[uppercase(value)] = true;
});
@@ -1985,7 +2045,7 @@ function createEventHandler(element, events) {
}
event.isDefaultPrevented = function() {
- return event.defaultPrevented;
+ return event.defaultPrevented || event.returnValue == false;
};
forEach(events[type || event.type], function(fn) {
@@ -2109,8 +2169,9 @@ forEach({
append: function(element, node) {
forEach(new JQLite(node), function(child){
- if (element.nodeType === 1)
+ if (element.nodeType === 1 || element.nodeType === 11) {
element.appendChild(child);
+ }
});
},
@@ -2187,9 +2248,10 @@ forEach({
triggerHandler: function(element, eventName) {
var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName];
+ var event;
forEach(eventFns, function(fn) {
- fn.call(element, null);
+ fn.call(element, {preventDefault: noop});
});
}
}, function(fn, name){
@@ -2278,50 +2340,6 @@ HashMap.prototype = {
}
};
-/**
- * A map where multiple values can be added to the same key such that they form a queue.
- * @returns {HashQueueMap}
- */
-function HashQueueMap() {}
-HashQueueMap.prototype = {
- /**
- * Same as array push, but using an array as the value for the hash
- */
- push: function(key, value) {
- var array = this[key = hashKey(key)];
- if (!array) {
- this[key] = [value];
- } else {
- array.push(value);
- }
- },
-
- /**
- * Same as array shift, but using an array as the value for the hash
- */
- shift: function(key) {
- var array = this[key = hashKey(key)];
- if (array) {
- if (array.length == 1) {
- delete this[key];
- return array[0];
- } else {
- return array.shift();
- }
- }
- },
-
- /**
- * return the first item without deleting it
- */
- peek: function(key) {
- var array = this[hashKey(key)];
- if (array) {
- return array[0];
- }
- }
-};
-
/**
* @ngdoc function
* @name angular.injector
@@ -2473,6 +2491,18 @@ function annotate(fn) {
* @returns {*} the value returned by the invoked `fn` function.
*/
+/**
+ * @ngdoc method
+ * @name AUTO.$injector#has
+ * @methodOf AUTO.$injector
+ *
+ * @description
+ * Allows the user to query if the particular service exist.
+ *
+ * @param {string} Name of the service to query.
+ * @returns {boolean} returns true if injector has given service.
+ */
+
/**
* @ngdoc method
* @name AUTO.$injector#instantiate
@@ -2730,9 +2760,10 @@ function createInjector(modulesToLoad) {
decorator: decorator
}
},
- providerInjector = createInternalInjector(providerCache, function() {
- throw Error("Unknown provider: " + path.join(' <- '));
- }),
+ providerInjector = (providerCache.$injector =
+ createInternalInjector(providerCache, function() {
+ throw Error("Unknown provider: " + path.join(' <- '));
+ })),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(servicename) {
@@ -2809,9 +2840,7 @@ function createInjector(modulesToLoad) {
try {
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
var invokeArgs = invokeQueue[i],
- provider = invokeArgs[0] == '$injector'
- ? providerInjector
- : providerInjector.get(invokeArgs[0]);
+ provider = providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
@@ -2920,7 +2949,10 @@ function createInjector(modulesToLoad) {
invoke: invoke,
instantiate: instantiate,
get: getService,
- annotate: annotate
+ annotate: annotate,
+ has: function(name) {
+ return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
+ }
};
}
}
@@ -2992,6 +3024,506 @@ function $AnchorScrollProvider() {
}];
}
+
+/**
+ * @ngdoc object
+ * @name ng.$animationProvider
+ * @description
+ *
+ * The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside
+ * of a module.
+ *
+ */
+$AnimationProvider.$inject = ['$provide'];
+function $AnimationProvider($provide) {
+ var suffix = 'Animation';
+
+ /**
+ * @ngdoc function
+ * @name ng.$animation#register
+ * @methodOf ng.$animationProvider
+ *
+ * @description
+ * Registers a new injectable animation factory function. The factory function produces the animation object which
+ * has these two properties:
+ *
+ * * `setup`: `function(Element):*` A function which receives the starting state of the element. The purpose
+ * of this function is to get the element ready for animation. Optionally the function returns an memento which
+ * is passed to the `start` function.
+ * * `start`: `function(Element, doneFunction, *)` The element to animate, the `doneFunction` to be called on
+ * element animation completion, and an optional memento from the `setup` function.
+ *
+ * @param {string} name The name of the animation.
+ * @param {function} factory The factory function that will be executed to return the animation object.
+ *
+ */
+ this.register = function(name, factory) {
+ $provide.factory(camelCase(name) + suffix, factory);
+ };
+
+ this.$get = ['$injector', function($injector) {
+ /**
+ * @ngdoc function
+ * @name ng.$animation
+ * @function
+ *
+ * @description
+ * The $animation service is used to retrieve any defined animation functions. When executed, the $animation service
+ * will return a object that contains the setup and start functions that were defined for the animation.
+ *
+ * @param {String} name Name of the animation function to retrieve. Animation functions are registered and stored
+ * inside of the AngularJS DI so a call to $animate('custom') is the same as injecting `customAnimation`
+ * via dependency injection.
+ * @return {Object} the animation object which contains the `setup` and `start` functions that perform the animation.
+ */
+ return function $animation(name) {
+ if (name) {
+ var animationName = camelCase(name) + suffix;
+ if ($injector.has(animationName)) {
+ return $injector.get(animationName);
+ }
+ }
+ };
+ }];
+}
+
+// NOTE: this is a pseudo directive.
+
+/**
+ * @ngdoc directive
+ * @name ng.directive:ngAnimate
+ *
+ * @description
+ * The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives.
+ * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place
+ * without burdening the directive which uses the animation with animation details. The built in directives
+ * `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive.
+ * Custom directives can take advantage of animation through {@link ng.$animator $animator service}.
+ *
+ * Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives:
+ *
+ * | Directive | Supported Animations |
+ * |========================================================== |====================================================|
+ * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
+ * | {@link ng.directive:ngView#animations ngView} | enter and leave |
+ * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
+ * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
+ * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
+ * | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide |
+ *
+ * You can find out more information about animations upon visiting each directive page.
+ *
+ * Below is an example of a directive that makes use of the ngAnimate attribute:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned.
+ *
+ * Keep in mind that if an animation is running, no child element of such animation can also be animated.
+ *
+ *
CSS-defined Animations
+ * By default, ngAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation.
+ * It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as
+ * well as CSS animations.
+ *
+ * The following code below demonstrates how to perform animations using **CSS transitions** with ngAnimate:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * The following code below demonstrates how to perform animations using **CSS animations** with ngAnimate:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * ngAnimate will first examine any CSS animation code and then fallback to using CSS transitions.
+ *
+ * Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then,
+ * the active class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
+ * of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be
+ * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
+ * immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element
+ * has no CSS transition/animation classes surrounding it.
+ *
+ *
JavaScript-defined Animations
+ * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations to browsers that do not
+ * yet support them, then you can make use of JavaScript animations defined inside of your AngularJS module.
+ *
+ *
+ * var ngModule = angular.module('YourApp', []);
+ * ngModule.animation('animate-enter', function() {
+ * return {
+ * setup : function(element) {
+ * //prepare the element for animation
+ * element.css({ 'opacity': 0 });
+ * var memo = "..."; //this value is passed to the start function
+ * return memo;
+ * },
+ * start : function(element, done, memo) {
+ * //start the animation
+ * element.animate({
+ * 'opacity' : 1
+ * }, function() {
+ * //call when the animation is complete
+ * done()
+ * });
+ * }
+ * }
+ * });
+ *
+ *
+ * As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation
+ * can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled
+ * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using
+ * CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values.
+ * It will instead close off the animation once the provided done function is executed. So it's important that you
+ * make sure your animations remember to fire off the done function once the animations are complete.
+ *
+ * @param {expression} ngAnimate Used to configure the DOM manipulation animations.
+ *
+ */
+
+var $AnimatorProvider = function() {
+ var NG_ANIMATE_CONTROLLER = '$ngAnimateController';
+ var rootAnimateController = {running:true};
+
+ this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope',
+ function($animation, $window, $sniffer, $rootElement, $rootScope) {
+ $rootElement.data(NG_ANIMATE_CONTROLLER, rootAnimateController);
+
+ /**
+ * @ngdoc function
+ * @name ng.$animator
+ * @function
+ *
+ * @description
+ * The $animator.create service provides the DOM manipulation API which is decorated with animations.
+ *
+ * @param {Scope} scope the scope for the ng-animate.
+ * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
+ * passed into the linking function of the directive using the `$animator`.)
+ * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
+ */
+ var AnimatorService = function(scope, attrs) {
+ var animator = {};
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#enter
+ * @methodOf ng.$animator
+ * @function
+ *
+ * @description
+ * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
+ *
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
+ * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
+ */
+ animator.enter = animateActionFactory('enter', insert, noop);
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#leave
+ * @methodOf ng.$animator
+ * @function
+ *
+ * @description
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM.
+ *
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
+ */
+ animator.leave = animateActionFactory('leave', noop, remove);
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#move
+ * @methodOf ng.$animator
+ * @function
+ *
+ * @description
+ * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
+ * add the element directly after the after element if present. Then the move animation will be run.
+ *
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
+ * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
+ */
+ animator.move = animateActionFactory('move', move, noop);
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#show
+ * @methodOf ng.$animator
+ * @function
+ *
+ * @description
+ * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
+ *
+ * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
+ */
+ animator.show = animateActionFactory('show', show, noop);
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#hide
+ * @methodOf ng.$animator
+ *
+ * @description
+ * Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
+ *
+ * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
+ */
+ animator.hide = animateActionFactory('hide', noop, hide);
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#animate
+ * @methodOf ng.$animator
+ *
+ * @description
+ * Triggers a custom animation event to be executed on the given element
+ *
+ * @param {jQuery/jqLite element} element that will be animated
+ */
+ animator.animate = function(event, element) {
+ animateActionFactory(event, noop, noop)(element);
+ }
+ return animator;
+
+ function animateActionFactory(type, beforeFn, afterFn) {
+ return function(element, parent, after) {
+ var ngAnimateValue = scope.$eval(attrs.ngAnimate);
+ var className = ngAnimateValue
+ ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
+ : '';
+ var animationPolyfill = $animation(className);
+ var polyfillSetup = animationPolyfill && animationPolyfill.setup;
+ var polyfillStart = animationPolyfill && animationPolyfill.start;
+ var polyfillCancel = animationPolyfill && animationPolyfill.cancel;
+
+ if (!className) {
+ beforeFn(element, parent, after);
+ afterFn(element, parent, after);
+ } else {
+ var activeClassName = className + '-active';
+
+ if (!parent) {
+ parent = after ? after.parent() : element.parent();
+ }
+ if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) ||
+ (parent.inheritedData(NG_ANIMATE_CONTROLLER) || noop).running) {
+ beforeFn(element, parent, after);
+ afterFn(element, parent, after);
+ return;
+ }
+
+ var animationData = element.data(NG_ANIMATE_CONTROLLER) || {};
+ if(animationData.running) {
+ (polyfillCancel || noop)(element);
+ animationData.done();
+ }
+
+ element.data(NG_ANIMATE_CONTROLLER, {running:true, done:done});
+ element.addClass(className);
+ beforeFn(element, parent, after);
+ if (element.length == 0) return done();
+
+ var memento = (polyfillSetup || noop)(element);
+
+ // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
+ // keep at 1 for animation dom rerender
+ $window.setTimeout(beginAnimation, 1);
+ }
+
+ function parseMaxTime(str) {
+ var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : [];
+ forEach(values, function(value) {
+ total = Math.max(parseFloat(value) || 0, total);
+ });
+ return total;
+ }
+
+ function beginAnimation() {
+ element.addClass(activeClassName);
+ if (polyfillStart) {
+ polyfillStart(element, done, memento);
+ } else if (isFunction($window.getComputedStyle)) {
+ //one day all browsers will have these properties
+ var w3cAnimationProp = 'animation';
+ var w3cTransitionProp = 'transition';
+
+ //but some still use vendor-prefixed styles
+ var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
+ var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
+
+ var durationKey = 'Duration',
+ delayKey = 'Delay',
+ animationIterationCountKey = 'IterationCount',
+ duration = 0;
+
+ //we want all the styles defined before and after
+ var ELEMENT_NODE = 1;
+ forEach(element, function(element) {
+ if (element.nodeType == ELEMENT_NODE) {
+ var w3cProp = w3cTransitionProp,
+ vendorProp = vendorTransitionProp,
+ iterations = 1,
+ elementStyles = $window.getComputedStyle(element) || {};
+
+ //use CSS Animations over CSS Transitions
+ if(parseFloat(elementStyles[w3cAnimationProp + durationKey]) > 0 ||
+ parseFloat(elementStyles[vendorAnimationProp + durationKey]) > 0) {
+ w3cProp = w3cAnimationProp;
+ vendorProp = vendorAnimationProp;
+ iterations = Math.max(parseInt(elementStyles[w3cProp + animationIterationCountKey]) || 0,
+ parseInt(elementStyles[vendorProp + animationIterationCountKey]) || 0,
+ iterations);
+ }
+
+ var parsedDelay = Math.max(parseMaxTime(elementStyles[w3cProp + delayKey]),
+ parseMaxTime(elementStyles[vendorProp + delayKey]));
+
+ var parsedDuration = Math.max(parseMaxTime(elementStyles[w3cProp + durationKey]),
+ parseMaxTime(elementStyles[vendorProp + durationKey]));
+
+ duration = Math.max(parsedDelay + (iterations * parsedDuration), duration);
+ }
+ });
+ $window.setTimeout(done, duration * 1000);
+ } else {
+ done();
+ }
+ }
+
+ function done() {
+ if(!done.run) {
+ done.run = true;
+ afterFn(element, parent, after);
+ element.removeClass(className);
+ element.removeClass(activeClassName);
+ element.removeData(NG_ANIMATE_CONTROLLER);
+ }
+ }
+ };
+ }
+
+ function show(element) {
+ element.css('display', '');
+ }
+
+ function hide(element) {
+ element.css('display', 'none');
+ }
+
+ function insert(element, parent, after) {
+ if (after) {
+ after.after(element);
+ } else {
+ parent.append(element);
+ }
+ }
+
+ function remove(element) {
+ element.remove();
+ }
+
+ function move(element, parent, after) {
+ // Do not remove element before insert. Removing will cause data associated with the
+ // element to be dropped. Insert will implicitly do the remove.
+ insert(element, parent, after);
+ }
+ };
+
+ /**
+ * @ngdoc function
+ * @name ng.animator#enabled
+ * @methodOf ng.$animator
+ * @function
+ *
+ * @param {Boolean=} If provided then set the animation on or off.
+ * @return {Boolean} Current animation state.
+ *
+ * @description
+ * Globally enables/disables animations.
+ *
+ */
+ AnimatorService.enabled = function(value) {
+ if (arguments.length) {
+ rootAnimateController.running = !value;
+ }
+ return !rootAnimateController.running;
+ };
+
+ return AnimatorService;
+ }];
+};
+
/**
* ! This is a private undocumented service !
*
@@ -3244,7 +3776,7 @@ function Browser(window, document, $log, $sniffer) {
* @methodOf ng.$browser
*
* @param {string=} name Cookie name
- * @param {string=} value Cokkie value
+ * @param {string=} value Cookie value
*
* @description
* The cookies method provides a 'private' low level access to browser cookies.
@@ -3312,7 +3844,7 @@ function Browser(window, document, $log, $sniffer) {
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
*
* @description
- * Executes a fn asynchroniously via `setTimeout(fn, delay)`.
+ * Executes a fn asynchronously via `setTimeout(fn, delay)`.
*
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
* `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
@@ -3339,7 +3871,7 @@ function Browser(window, document, $log, $sniffer) {
* Cancels a defered task identified with `deferId`.
*
* @param {*} deferId Token returned by the `$browser.defer` function.
- * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
+ * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully canceled.
*/
self.defer.cancel = function(deferId) {
if (pendingDeferIds[deferId]) {
@@ -3376,7 +3908,7 @@ function $BrowserProvider(){
* @returns {object} Newly created cache object with the following set of methods:
*
* - `{object}` `info()` — Returns id, size, and options of cache.
- * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache.
+ * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it.
* - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
* - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
* - `{void}` `removeAll()` — Removes all cached values.
@@ -3415,6 +3947,8 @@ function $CacheFactoryProvider() {
if (size > capacity) {
this.remove(staleEnd.key);
}
+
+ return value;
},
@@ -3701,7 +4235,7 @@ function $CompileProvider($provide) {
*
* @param {string} name Name of the directive in camel-case. (ie ngBind which will match as
* ng-bind).
- * @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more
+ * @param {function} directiveFactory An injectable directive factory function. See {@link guide/directive} for more
* info.
* @returns {ng.$compileProvider} Self for chaining.
*/
@@ -3880,7 +4414,8 @@ function $CompileProvider($provide) {
? identity
: function denormalizeTemplate(template) {
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
- };
+ },
+ NG_ATTR_BINDING = /^ngAttr[A-Z]/;
return compile;
@@ -4045,11 +4580,16 @@ function $CompileProvider($provide) {
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);
// iterate over the attributes
- for (var attr, name, nName, value, nAttrs = node.attributes,
+ for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
attr = nAttrs[j];
if (attr.specified) {
name = attr.name;
+ // support ngAttr attribute binding
+ ngAttrName = directiveNormalize(name);
+ if (NG_ATTR_BINDING.test(ngAttrName)) {
+ name = ngAttrName.substr(6).toLowerCase();
+ }
nName = directiveNormalize(name.toLowerCase());
attrsMap[nName] = name;
attrs[nName] = value = trim((msie && name == 'href')
@@ -4177,9 +4717,14 @@ function $CompileProvider($provide) {
}
}
- if ((directiveValue = directive.template)) {
+ if (directive.template) {
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
+
+ directiveValue = (isFunction(directive.template))
+ ? directive.template($compileNode, templateAttrs)
+ : directive.template;
+
directiveValue = denormalizeTemplate(directiveValue);
if (directive.replace) {
@@ -4299,13 +4844,14 @@ function $CompileProvider($provide) {
$element = attrs.$$element;
if (newIsolateScopeDirective) {
- var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/;
+ var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
var parentScope = scope.$parent || scope;
forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
var match = definiton.match(LOCAL_REGEXP) || [],
- attrName = match[2]|| scopeName,
+ attrName = match[3] || scopeName,
+ optional = (match[2] == '?'),
mode = match[1], // @, =, or &
lastValue,
parentGet, parentSet;
@@ -4319,10 +4865,17 @@ function $CompileProvider($provide) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = parentScope;
+ if( attrs[attrName] ) {
+ // If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn
+ scope[scopeName] = $interpolate(attrs[attrName])(parentScope);
+ }
break;
}
case '=': {
+ if (optional && !attrs[attrName]) {
+ return;
+ }
parentGet = $parse(attrs[attrName]);
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
@@ -4494,11 +5047,14 @@ function $CompileProvider($provide) {
// The fact that we have to copy and patch the directive seems wrong!
derivedSyncDirective = extend({}, origAsyncDirective, {
controller: null, templateUrl: null, transclude: null, scope: null
- });
+ }),
+ templateUrl = (isFunction(origAsyncDirective.templateUrl))
+ ? origAsyncDirective.templateUrl($compileNode, tAttrs)
+ : origAsyncDirective.templateUrl;
$compileNode.html('');
- $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}).
+ $http.get(templateUrl, {cache: $templateCache}).
success(function(content) {
var compileNode, tempTemplateAttrs, $template;
@@ -4527,10 +5083,10 @@ function $CompileProvider($provide) {
while(linkQueue.length) {
- var controller = linkQueue.pop(),
- linkRootElement = linkQueue.pop(),
- beforeTemplateLinkNode = linkQueue.pop(),
- scope = linkQueue.pop(),
+ var scope = linkQueue.shift(),
+ beforeTemplateLinkNode = linkQueue.shift(),
+ linkRootElement = linkQueue.shift(),
+ controller = linkQueue.shift(),
linkNode = compileNode;
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
@@ -4611,13 +5167,15 @@ function $CompileProvider($provide) {
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
var $$observers = (attr.$$observers || (attr.$$observers = {}));
- if (name === 'class') {
- // we need to interpolate classes again, in the case the element was replaced
- // and therefore the two class attrs got merged - we want to interpolate the result
- interpolateFn = $interpolate(attr[name], true);
- }
+ // we need to interpolate again, in case the attribute value has been updated
+ // (e.g. by another directive's compile function)
+ interpolateFn = $interpolate(attr[name], true);
- attr[name] = undefined;
+ // if attribute was updated so that there is no interpolation going on we don't want to
+ // register any observers
+ if (!interpolateFn) return;
+
+ attr[name] = interpolateFn(scope);
($$observers[name] || ($$observers[name] = [])).$$inter = true;
(attr.$$observers && attr.$$observers[name].$$scope || scope).
$watch(interpolateFn, function interpolateFnWatchAction(value) {
@@ -4712,7 +5270,7 @@ function directiveNormalize(name) {
* @param {string} name Normalized element attribute name of the property to modify. The name is
* revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
* property to the original name.
- * @param {string} value Value to set the attribute to.
+ * @param {string} value Value to set the attribute to. The value can be an interpolated string.
*/
@@ -4747,7 +5305,8 @@ function directiveLinkingFn(
* {@link ng.$controllerProvider#register register} method.
*/
function $ControllerProvider() {
- var controllers = {};
+ var controllers = {},
+ CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
/**
@@ -4792,17 +5351,32 @@ function $ControllerProvider() {
* a service, so that one can override this service with {@link https://gist.github.com/1649788
* BC version}.
*/
- return function(constructor, locals) {
- if(isString(constructor)) {
- var name = constructor;
- constructor = controllers.hasOwnProperty(name)
- ? controllers[name]
- : getter(locals.$scope, name, true) || getter($window, name, true);
+ return function(expression, locals) {
+ var instance, match, constructor, identifier;
+
+ if(isString(expression)) {
+ match = expression.match(CNTRL_REG),
+ constructor = match[1],
+ identifier = match[3];
+ expression = controllers.hasOwnProperty(constructor)
+ ? controllers[constructor]
+ : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
+
+ assertArgFn(expression, constructor, true);
+ }
- assertArgFn(constructor, name, true);
+ instance = $injector.instantiate(expression, locals);
+
+ if (identifier) {
+ if (typeof locals.$scope !== 'object') {
+ throw new Error('Can not export controller as "' + identifier + '". ' +
+ 'No scope object provided!');
+ }
+
+ locals.$scope[identifier] = instance;
}
- return $injector.instantiate(constructor, locals);
+ return instance;
};
}];
}
@@ -4900,7 +5474,7 @@ function $InterpolateProvider() {
};
- this.$get = ['$parse', function($parse) {
+ this.$get = ['$parse', '$exceptionHandler', function($parse, $exceptionHandler) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length;
@@ -4972,18 +5546,24 @@ function $InterpolateProvider() {
if (!mustHaveExpression || hasInterpolation) {
concat.length = length;
fn = function(context) {
- for(var i = 0, ii = length, part; i html5 url
- } else {
- return composeProtocolHostPort(match.protocol, match.host, match.port) +
- pathPrefixFromBase(basePath) + match.hash.substr(hashPrefix.length);
- }
-}
-
-
-function convertToHashbangUrl(url, basePath, hashPrefix) {
- var match = matchUrl(url);
-
- // already hashbang url
- if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
- match.hash.indexOf(hashPrefix) === 0) {
- return url;
- // convert html5 url -> hashbang url
- } else {
- var search = match.search && '?' + match.search || '',
- hash = match.hash && '#' + match.hash || '',
- pathPrefix = pathPrefixFromBase(basePath),
- path = match.path.substr(pathPrefix.length);
-
- if (match.path.indexOf(pathPrefix) !== 0) {
- throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !');
- }
-
- return composeProtocolHostPort(match.protocol, match.host, match.port) + basePath +
- '#' + hashPrefix + path + search + hash;
- }
+/* return the server only */
+function serverBase(url) {
+ return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
}
/**
- * LocationUrl represents an url
+ * LocationHtml5Url represents an url
* This object is exposed as $location service when HTML5 mode is enabled and supported
*
* @constructor
- * @param {string} url HTML5 url
- * @param {string} pathPrefix
+ * @param {string} appBase application base URL
+ * @param {string} basePrefix url path prefix
*/
-function LocationUrl(url, pathPrefix, appBaseUrl) {
- pathPrefix = pathPrefix || '';
-
+function LocationHtml5Url(appBase, basePrefix) {
+ basePrefix = basePrefix || '';
+ var appBaseNoFile = stripFile(appBase);
/**
* Parse given html5 (regular) url string into properties
* @param {string} newAbsoluteUrl HTML5 url
* @private
*/
- this.$$parse = function(newAbsoluteUrl) {
- var match = matchUrl(newAbsoluteUrl, this);
-
- if (match.path.indexOf(pathPrefix) !== 0) {
- throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !');
+ this.$$parse = function(url) {
+ var parsed = {}
+ matchUrl(url, parsed);
+ var pathUrl = beginsWith(appBaseNoFile, url);
+ if (!isString(pathUrl)) {
+ throw Error('Invalid url "' + url + '", missing path prefix "' + appBaseNoFile + '".');
+ }
+ matchAppUrl(pathUrl, parsed);
+ extend(this, parsed);
+ if (!this.$$path) {
+ this.$$path = '/';
}
-
- this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length));
- this.$$search = parseKeyValue(match.search);
- this.$$hash = match.hash && decodeURIComponent(match.hash) || '';
this.$$compose();
};
@@ -5167,19 +5724,25 @@ function LocationUrl(url, pathPrefix, appBaseUrl) {
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
- this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
- pathPrefix + this.$$url;
+ this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
};
+ this.$$rewrite = function(url) {
+ var appUrl, prevAppUrl;
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
- if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
- return absoluteLinkUrl;
+ if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
+ prevAppUrl = appUrl;
+ if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
+ return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
+ } else {
+ return appBase + prevAppUrl;
+ }
+ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
+ return appBaseNoFile + appUrl;
+ } else if (appBaseNoFile == url + '/') {
+ return appBaseNoFile;
}
}
-
-
- this.$$parse(url);
}
@@ -5188,11 +5751,11 @@ function LocationUrl(url, pathPrefix, appBaseUrl) {
* This object is exposed as $location service when html5 history api is disabled or not supported
*
* @constructor
- * @param {string} url Legacy url
- * @param {string} hashPrefix Prefix for hash part (containing path and search)
+ * @param {string} appBase application base URL
+ * @param {string} hashPrefix hashbang prefix
*/
-function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
- var basePath;
+function LocationHashbangUrl(appBase, hashPrefix) {
+ var appBaseNoFile = stripFile(appBase);
/**
* Parse given hashbang url into properties
@@ -5200,24 +5763,16 @@ function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
* @private
*/
this.$$parse = function(url) {
- var match = matchUrl(url, this);
-
-
- if (match.hash && match.hash.indexOf(hashPrefix) !== 0) {
- throw Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !');
+ matchUrl(url, this);
+ var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
+ if (!isString(withoutBaseUrl)) {
+ throw new Error('Invalid url "' + url + '", does not start with "' + appBase + '".');
}
-
- basePath = match.path + (match.search ? '?' + match.search : '');
- match = HASH_MATCH.exec((match.hash || '').substr(hashPrefix.length));
- if (match[1]) {
- this.$$path = (match[1].charAt(0) == '/' ? '' : '/') + decodeURIComponent(match[1]);
- } else {
- this.$$path = '';
+ var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' ? beginsWith(hashPrefix, withoutBaseUrl) : withoutBaseUrl;
+ if (!isString(withoutHashUrl)) {
+ throw new Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '".');
}
-
- this.$$search = parseKeyValue(match[3]);
- this.$$hash = match[5] && decodeURIComponent(match[5]) || '';
-
+ matchAppUrl(withoutHashUrl, this);
this.$$compose();
};
@@ -5230,22 +5785,48 @@ function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
- this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
- basePath + (this.$$url ? '#' + hashPrefix + this.$$url : '');
+ this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
};
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
- if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
- return absoluteLinkUrl;
+ this.$$rewrite = function(url) {
+ if(stripHash(appBase) == stripHash(url)) {
+ return url;
}
}
+}
+
+
+/**
+ * LocationHashbangUrl represents url
+ * This object is exposed as $location service when html5 history api is enabled but the browser
+ * does not support it.
+ *
+ * @constructor
+ * @param {string} appBase application base URL
+ * @param {string} hashPrefix hashbang prefix
+ */
+function LocationHashbangInHtml5Url(appBase, hashPrefix) {
+ LocationHashbangUrl.apply(this, arguments);
+
+ var appBaseNoFile = stripFile(appBase);
+ this.$$rewrite = function(url) {
+ var appUrl;
- this.$$parse(url);
+ if ( appBase == stripHash(url) ) {
+ return url;
+ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
+ return appBase + hashPrefix + appUrl;
+ } else if ( appBaseNoFile === url + '/') {
+ return appBaseNoFile;
+ }
+ }
}
-LocationUrl.prototype = {
+LocationHashbangInHtml5Url.prototype =
+ LocationHashbangUrl.prototype =
+ LocationHtml5Url.prototype = {
/**
* Has any change been replacing ?
@@ -5427,21 +6008,6 @@ LocationUrl.prototype = {
}
};
-LocationHashbangUrl.prototype = inherit(LocationUrl.prototype);
-
-function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) {
- LocationHashbangUrl.apply(this, arguments);
-
-
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
- if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
- return appBaseUrl + baseExtra + '#' + hashPrefix + absoluteLinkUrl.substr(appBaseUrl.length);
- }
- }
-}
-
-LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype);
-
function locationGetter(property) {
return function() {
return this[property];
@@ -5538,37 +6104,20 @@ function $LocationProvider(){
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
function( $rootScope, $browser, $sniffer, $rootElement) {
var $location,
- basePath,
- pathPrefix,
- initUrl = $browser.url(),
- initUrlParts = matchUrl(initUrl),
- appBaseUrl;
+ LocationMode,
+ baseHref = $browser.baseHref(),
+ initialUrl = $browser.url(),
+ appBase;
if (html5Mode) {
- basePath = $browser.baseHref() || '/';
- pathPrefix = pathPrefixFromBase(basePath);
- appBaseUrl =
- composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
- pathPrefix + '/';
-
- if ($sniffer.history) {
- $location = new LocationUrl(
- convertToHtml5Url(initUrl, basePath, hashPrefix),
- pathPrefix, appBaseUrl);
- } else {
- $location = new LocationHashbangInHtml5Url(
- convertToHashbangUrl(initUrl, basePath, hashPrefix),
- hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1));
- }
+ appBase = baseHref ? serverBase(initialUrl) + baseHref : initialUrl;
+ LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
} else {
- appBaseUrl =
- composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
- (initUrlParts.path || '') +
- (initUrlParts.search ? ('?' + initUrlParts.search) : '') +
- '#' + hashPrefix + '/';
-
- $location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl);
+ appBase = stripHash(initialUrl);
+ LocationMode = LocationHashbangUrl;
}
+ $location = new LocationMode(appBase, '#' + hashPrefix);
+ $location.$$parse($location.$$rewrite(initialUrl));
$rootElement.bind('click', function(event) {
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
@@ -5584,22 +6133,24 @@ function $LocationProvider(){
if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
}
- var absHref = elm.prop('href'),
- rewrittenUrl = $location.$$rewriteAppUrl(absHref);
+ var absHref = elm.prop('href');
+ var rewrittenUrl = $location.$$rewrite(absHref);
- if (absHref && !elm.attr('target') && rewrittenUrl) {
- // update location manually
- $location.$$parse(rewrittenUrl);
- $rootScope.$apply();
+ if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
event.preventDefault();
- // hack to work around FF6 bug 684208 when scenario runner clicks on links
- window.angular['ff-684208-preventDefault'] = true;
+ if (rewrittenUrl != $browser.url()) {
+ // update location manually
+ $location.$$parse(rewrittenUrl);
+ $rootScope.$apply();
+ // hack to work around FF6 bug 684208 when scenario runner clicks on links
+ window.angular['ff-684208-preventDefault'] = true;
+ }
}
});
// rewrite hashbang url <> html5 url
- if ($location.absUrl() != initUrl) {
+ if ($location.absUrl() != initialUrl) {
$browser.url($location.absUrl(), true);
}
@@ -5684,7 +6235,33 @@ function $LocationProvider(){
*/
+/**
+ * @ngdoc object
+ * @name ng.$logProvider
+ * @description
+ * Use the `$logProvider` to configure how the application logs messages
+ */
function $LogProvider(){
+ var debug = true,
+ self = this;
+
+ /**
+ * @ngdoc property
+ * @name ng.$logProvider#debugEnabled
+ * @methodOf ng.$logProvider
+ * @description
+ * @param {string=} flag enable or disable debug level messages
+ * @returns {*} current value if used as getter or itself (chaining) if used as setter
+ */
+ this.debugEnabled = function(flag) {
+ if (isDefined(flag)) {
+ debug = flag;
+ return this;
+ } else {
+ return debug;
+ }
+ };
+
this.$get = ['$window', function($window){
return {
/**
@@ -5725,7 +6302,25 @@ function $LogProvider(){
* @description
* Write an error message
*/
- error: consoleLog('error')
+ error: consoleLog('error'),
+
+ /**
+ * @ngdoc method
+ * @name ng.$log#debug
+ * @methodOf ng.$log
+ *
+ * @description
+ * Write a debug message
+ */
+ debug: (function () {
+ var fn = consoleLog('debug');
+
+ return function() {
+ if (debug) {
+ fn.apply(self, arguments);
+ }
+ }
+ }())
};
function formatError(arg) {
@@ -5784,6 +6379,8 @@ var OPERATORS = {
'%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
'^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
'=':noop,
+ '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
+ '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
'==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
'!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
'<':function(self, locals, a,b){return a(self, locals)} promises An array of promises.
- * @returns {Promise} Returns a single promise that will be resolved with an array of values,
- * each value corresponding to the promise at the same index in the `promises` array. If any of
+ * @param {Array.|Object.} promises An array or hash of promises.
+ * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
+ * each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of
* the promises is resolved with a rejection, this resulting promise will be resolved with the
* same rejection.
*/
function all(promises) {
var deferred = defer(),
- counter = promises.length,
- results = [];
-
- if (counter) {
- forEach(promises, function(promise, index) {
- ref(promise).then(function(value) {
- if (index in results) return;
- results[index] = value;
- if (!(--counter)) deferred.resolve(results);
- }, function(reason) {
- if (index in results) return;
- deferred.reject(reason);
- });
+ counter = 0,
+ results = isArray(promises) ? [] : {};
+
+ forEach(promises, function(promise, key) {
+ counter++;
+ ref(promise).then(function(value) {
+ if (results.hasOwnProperty(key)) return;
+ results[key] = value;
+ if (!(--counter)) deferred.resolve(results);
+ }, function(reason) {
+ if (results.hasOwnProperty(key)) return;
+ deferred.reject(reason);
});
- } else {
+ });
+
+ if (counter === 0) {
deferred.resolve(results);
}
@@ -7063,9 +7763,18 @@ function $RouteProvider(){
* `$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 (`: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 (`: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 star (`*name`). All characters are
+ * eagerly stored in `$routeParams` under the given `name` when the route matches.
+ *
+ * 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.
@@ -7075,12 +7784,26 @@ function $RouteProvider(){
* - `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.
- * - `template` – `{string=}` – html template as a string that should be used by
- * {@link ng.directive:ngView ngView} or
+ * - `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 function that returns
+ * an html template as a string which should be used by {@link ng.directive:ngView ngView} or
* {@link ng.directive:ngInclude ngInclude} directives.
- * this property takes precedence over `templateUrl`.
- * - `templateUrl` – `{string=}` – path to an html template that should be used by
- * {@link ng.directive:ngView ngView}.
+ * This property takes precedence over `templateUrl`.
+ *
+ * If `template` is a function, it will be called with the following parameters:
+ *
+ * - `{Array.