').append(element).html().
+ match(/^(<[^>]+>)/)[1].
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
}
@@ -910,7 +912,7 @@ function angularInit(element, bootstrap) {
* @description
* Use this function to manually start up angular application.
*
- * See: {@link guide/dev_guide.bootstrap.manual_bootstrap Bootstrap}
+ * See: {@link guide/bootstrap Bootstrap}
*
* @param {Element} element DOM element which is the root of angular application.
* @param {Array=} modules an array of module declarations. See: {@link angular.module modules}
@@ -919,10 +921,13 @@ function angularInit(element, bootstrap) {
function bootstrap(element, modules) {
element = jqLite(element);
modules = modules || [];
+ modules.unshift(['$provide', function($provide) {
+ $provide.value('$rootElement', element);
+ }]);
modules.unshift('ng');
var injector = createInjector(modules);
injector.invoke(
- ['$rootScope', '$compile', '$injector', function(scope, compile, injector){
+ ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
@@ -1243,11 +1248,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.0.0rc10', // all of these placeholder strings will be replaced by rake's
+ full: '1.0.0rc11', // all of these placeholder strings will be replaced by rake's
major: 1, // compile task
minor: 0,
dot: 0,
- codeName: 'tesseract-giftwrapping'
+ codeName: 'promise-resolution'
};
@@ -1726,8 +1731,12 @@ forEach('input,select,option,textarea,button,form'.split(','), function(value) {
BOOLEAN_ELEMENTS[uppercase(value)] = true;
});
-function isBooleanAttr(element, name) {
- return BOOLEAN_ELEMENTS[element.nodeName] && BOOLEAN_ATTR[name.toLowerCase()];
+function getBooleanAttrName(element, name) {
+ // check dom last since we will most likely fail on name
+ var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
+
+ // booleanAttr is here twice to minimize DOM access
+ return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
}
forEach({
@@ -1920,11 +1929,7 @@ function createEventHandler(element, events) {
};
forEach(events[type || event.type], function(fn) {
- try {
- fn.call(element, event);
- } catch (e) {
- // Not much to do here since jQuery ignores these anyway
- }
+ fn.call(element, event);
});
// Remove monkey-patched methods (IE),
@@ -2217,7 +2222,7 @@ HashQueueMap.prototype = {
*
* @description
* Creates an injector function that can be used for retrieving services as well as for
- * dependency injection (see {@link guide/dev_guide.di dependency injection}).
+ * dependency injection (see {@link guide/di dependency injection}).
*
* @param {Array.} modules A list of module functions or their aliases. See
@@ -2252,19 +2257,32 @@ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
-function inferInjectionArgs(fn) {
- assertArgFn(fn);
- if (!fn.$inject) {
- var args = fn.$inject = [];
- var fnText = fn.toString().replace(STRIP_COMMENTS, '');
- var argDecl = fnText.match(FN_ARGS);
- forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
- arg.replace(FN_ARG, function(all, underscore, name){
- args.push(name);
+function annotate(fn) {
+ var $inject,
+ fnText,
+ argDecl,
+ last;
+
+ if (typeof fn == 'function') {
+ if (!($inject = fn.$inject)) {
+ $inject = [];
+ fnText = fn.toString().replace(STRIP_COMMENTS, '');
+ argDecl = fnText.match(FN_ARGS);
+ forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
+ arg.replace(FN_ARG, function(all, underscore, name){
+ $inject.push(name);
+ });
});
- });
+ fn.$inject = $inject;
+ }
+ } else if (isArray(fn)) {
+ last = fn.length - 1;
+ assertArgFn(fn[last], 'fn')
+ $inject = fn.slice(0, last);
+ } else {
+ assertArgFn(fn, 'fn', true);
}
- return fn.$inject;
+ return $inject;
}
///////////////////////////////////////
@@ -2345,7 +2363,7 @@ function inferInjectionArgs(fn) {
* @param {Object=} self The `this` for the invoked method.
* @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before
* the `$injector` is consulted.
- * @return the value returned by the invoked `fn` function.
+ * @returns {*} the value returned by the invoked `fn` function.
*/
/**
@@ -2359,9 +2377,90 @@ function inferInjectionArgs(fn) {
* @param {function} Type Annotated constructor function.
* @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before
* the `$injector` is consulted.
- * @return new instance of `Type`.
+ * @returns {Object} new instance of `Type`.
*/
+/**
+ * @ngdoc method
+ * @name angular.module.AUTO.$injector#annotate
+ * @methodOf angular.module.AUTO.$injector
+ *
+ * @description
+ * Returns an array of service names which the function is requesting for injection. This API is used by the injector
+ * to determine which services need to be injected into the function when the function is invoked. There are three
+ * ways in which the function can be annotated with the needed dependencies.
+ *
+ * # Argument names
+ *
+ * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting
+ * the function into a string using `toString()` method and extracting the argument names.
+ *
+ * // Given
+ * function MyController($scope, $route) {
+ * // ...
+ * }
+ *
+ * // Then
+ * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
+ *
+ *
+ * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
+ * are supported.
+ *
+ * # The `$injector` property
+ *
+ * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
+ * services to be injected into the function.
+ *
+ *
+ * # The array notation
+ *
+ * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very
+ * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives
+ * minification is a better choice:
+ *
+ *
+ * // We wish to write this (not minification / obfuscation safe)
+ * injector.invoke(function($compile, $rootScope) {
+ * // ...
+ * });
+ *
+ * // We are forced to write break inlining
+ * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
+ * // ...
+ * };
+ * tmpFn.$inject = ['$compile', '$rootScope'];
+ * injector.invoke(tempFn);
+ *
+ * // To better support inline function the inline annotation is supported
+ * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
+ * // ...
+ * }]);
+ *
+ * // Therefore
+ * expect(injector.annotate(
+ * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
+ * ).toEqual(['$compile', '$rootScope']);
+ *
+ *
+ * @param {function|Array.} fn Function for which dependent service names need to be retrieved as described
+ * above.
+ *
+ * @returns {Array.} The names of the services which the function requires.
+ */
+
+
+
/**
* @ngdoc object
@@ -2664,23 +2763,11 @@ function createInjector(modulesToLoad) {
function invoke(fn, self, locals){
var args = [],
- $inject,
- length,
+ $inject = annotate(fn),
+ length, i,
key;
- if (typeof fn == 'function') {
- $inject = inferInjectionArgs(fn);
- length = $inject.length;
- } else {
- if (isArray(fn)) {
- $inject = fn;
- length = $inject.length - 1;
- fn = $inject[length];
- }
- assertArgFn(fn, 'fn');
- }
-
- for(var i = 0; i < length; i++) {
+ for(i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
args.push(
locals && locals.hasOwnProperty(key)
@@ -2688,6 +2775,11 @@ function createInjector(modulesToLoad) {
: getService(key, path)
);
}
+ if (!fn.$inject) {
+ // this means that we must be an array.
+ fn = fn[length];
+ }
+
// Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
switch (self ? -1 : args.length) {
@@ -2720,7 +2812,8 @@ function createInjector(modulesToLoad) {
return {
invoke: invoke,
instantiate: instantiate,
- get: getService
+ get: getService,
+ annotate: annotate
};
}
}
@@ -3342,6 +3435,9 @@ function $TemplateCacheProvider() {
*/
+var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: ';
+
+
/**
* @ngdoc function
* @name angular.module.ng.$compile
@@ -3459,7 +3555,7 @@ function $TemplateCacheProvider() {
*
*
* For information on how the compiler works, see the
- * {@link guide/dev_guide.compiler Angular HTML Compiler} section of the Developer Guide.
+ * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
*/
@@ -3469,7 +3565,20 @@ function $TemplateCacheProvider() {
* @function
*
* @description
+ */
+
+/**
+ * @ngdoc function
+ * @name angular.module.ng.$compileProvider#directive
+ * @methodOf angular.module.ng.$compileProvider
+ * @function
*
+ * @description
+ * Register a new directive with compiler
+ *
+ * @param {string} name name of the directive.
+ * @param {function} directiveFactory An injectable directive factory function.
+ * @returns {angular.module.ng.$compileProvider} Self for chaining.
*/
$CompileProvider.$inject = ['$provide'];
function $CompileProvider($provide) {
@@ -3532,54 +3641,12 @@ function $CompileProvider($provide) {
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
- '$controller',
+ '$controller', '$rootScope',
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
- $controller) {
-
- var LOCAL_MODE = {
- attribute: function(localName, mode, parentScope, scope, attr) {
- scope[localName] = attr[localName];
- },
-
- evaluate: function(localName, mode, parentScope, scope, attr) {
- scope[localName] = parentScope.$eval(attr[localName]);
- },
-
- bind: function(localName, mode, parentScope, scope, attr) {
- var getter = $interpolate(attr[localName]);
- scope.$watch(
- function() { return getter(parentScope); },
- function(v) { scope[localName] = v; }
- );
- },
-
- accessor: function(localName, mode, parentScope, scope, attr) {
- var getter = noop,
- setter = noop,
- exp = attr[localName];
-
- if (exp) {
- getter = $parse(exp);
- setter = getter.assign || function() {
- throw Error("Expression '" + exp + "' not assignable.");
- };
- }
-
- scope[localName] = function(value) {
- return arguments.length ? setter(parentScope, value) : getter(parentScope);
- };
- },
-
- expression: function(localName, mode, parentScope, scope, attr) {
- scope[localName] = function(locals) {
- $parse(attr[localName])(parentScope, locals);
- };
- }
- };
+ $controller, $rootScope) {
var Attributes = function(element, attr) {
this.$$element = element;
- this.$$observers = {};
this.$attr = attr || {};
};
@@ -3597,7 +3664,8 @@ function $CompileProvider($provide) {
* @param {string=} attrName Optional none normalized name. Defaults to key.
*/
$set: function(key, value, writeAttr, attrName) {
- var booleanKey = isBooleanAttr(this.$$element[0], key.toLowerCase());
+ var booleanKey = getBooleanAttrName(this.$$element[0], key),
+ $$observers = this.$$observers;
if (booleanKey) {
this.$$element.prop(key, value);
@@ -3625,7 +3693,7 @@ function $CompileProvider($provide) {
}
// fire observers
- forEach(this.$$observers[key], function(fn) {
+ $$observers && forEach($$observers[key], function(fn) {
try {
fn(value);
} catch (e) {
@@ -3644,10 +3712,17 @@ function $CompileProvider($provide) {
* @returns {function(*)} the `fn` Function passed in.
*/
$observe: function(key, fn) {
- // keep only observers for interpolated attrs
- if (this.$$observers[key]) {
- this.$$observers[key].push(fn);
- }
+ var attrs = this,
+ $$observers = (attrs.$$observers || (attrs.$$observers = {})),
+ listeners = ($$observers[key] || ($$observers[key] = []));
+
+ listeners.push(fn);
+ $rootScope.$evalAsync(function() {
+ if (!listeners.$$inter) {
+ // no one registered attribute interpolation function, so lets call it manually
+ fn(attrs[key]);
+ }
+ });
return fn;
}
};
@@ -3809,7 +3884,7 @@ function $CompileProvider($provide) {
attrs[nName] = value = trim((msie && name == 'href')
? decodeURIComponent(node.getAttribute(name, 2))
: attr.value);
- if (isBooleanAttr(node, nName)) {
+ if (getBooleanAttrName(node, nName)) {
attrs[nName] = true; // presence means true
}
addAttrInterpolateDirective(node, directives, value, nName);
@@ -4050,9 +4125,67 @@ function $CompileProvider($provide) {
$element = attrs.$$element;
if (newScopeDirective && isObject(newScopeDirective.scope)) {
- forEach(newScopeDirective.scope, function(mode, name) {
- (LOCAL_MODE[mode] || wrongMode)(name, mode,
- scope.$parent || scope, scope, attrs);
+ var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/;
+
+ var parentScope = scope.$parent || scope;
+
+ forEach(newScopeDirective.scope, function(definiton, scopeName) {
+ var match = definiton.match(LOCAL_REGEXP) || [],
+ attrName = match[2]|| scopeName,
+ mode = match[1], // @, =, or &
+ lastValue,
+ parentGet, parentSet;
+
+ switch (mode) {
+
+ case '@': {
+ attrs.$observe(attrName, function(value) {
+ scope[scopeName] = value;
+ });
+ attrs.$$observers[attrName].$$scope = parentScope;
+ break;
+ }
+
+ case '=': {
+ parentGet = $parse(attrs[attrName]);
+ parentSet = parentGet.assign || function() {
+ // reset the change, or we will throw this exception on every $digest
+ lastValue = scope[scopeName] = parentGet(parentScope);
+ throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] +
+ ' (directive: ' + newScopeDirective.name + ')');
+ };
+ lastValue = scope[scopeName] = parentGet(parentScope);
+ scope.$watch(function() {
+ var parentValue = parentGet(parentScope);
+
+ if (parentValue !== scope[scopeName]) {
+ // we are out of sync and need to copy
+ if (parentValue !== lastValue) {
+ // parent changed and it has precedence
+ lastValue = scope[scopeName] = parentValue;
+ } else {
+ // if the parent can be assigned then do so
+ parentSet(parentScope, lastValue = scope[scopeName]);
+ }
+ }
+ return parentValue;
+ });
+ break;
+ }
+
+ case '&': {
+ parentGet = $parse(attrs[attrName]);
+ scope[scopeName] = function(locals) {
+ return parentGet(parentScope, locals);
+ }
+ break;
+ }
+
+ default: {
+ throw Error('Invalid isolate scope definition for directive ' +
+ newScopeDirective.name + ': ' + definiton);
+ }
+ }
});
}
@@ -4065,12 +4198,6 @@ function $CompileProvider($provide) {
$transclude: boundTranscludeFn
};
-
- forEach(directive.inject || {}, function(mode, name) {
- (LOCAL_MODE[mode] || wrongMode)(name, mode,
- newScopeDirective ? scope.$parent || scope : scope, locals, attrs);
- });
-
controller = directive.controller;
if (controller == '@') {
controller = attrs[directive.name];
@@ -4155,6 +4282,7 @@ function $CompileProvider($provide) {
var srcAttr = src.$attr,
dstAttr = dst.$attr,
$element = dst.$$element;
+
// reapply the old attributes to the new element
forEach(dst, function(value, key) {
if (key.charAt(0) != '$') {
@@ -4164,10 +4292,12 @@ function $CompileProvider($provide) {
dst.$set(key, value, true, srcAttr[key]);
}
});
+
// copy the new attributes on the old attrs object
forEach(src, function(value, key) {
if (key == 'class') {
safeAddClass($element, value);
+ dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
} else if (key == 'style') {
$element.attr('style', $element.attr('style') + ';' + value);
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
@@ -4301,19 +4431,20 @@ function $CompileProvider($provide) {
directives.push({
priority: 100,
compile: valueFn(function(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 define observers array only for interpolated attrs
- // and ignore observers for non interpolated attrs to save some memory
- attr.$$observers[name] = [];
attr[name] = undefined;
- scope.$watch(interpolateFn, function(value) {
- attr.$set(name, value);
- });
+ ($$observers[name] || ($$observers[name] = [])).$$inter = true;
+ (attr.$$observers && attr.$$observers[name].$$scope || scope).
+ $watch(interpolateFn, function(value) {
+ attr.$set(name, value);
+ });
})
});
}
@@ -4369,6 +4500,43 @@ function directiveNormalize(name) {
return camelCase(name.replace(PREFIX_REGEXP, ''));
}
+/**
+ * @ngdoc object
+ * @name angular.module.ng.$compile.directive.Attributes
+ * @description
+ *
+ * A shared object between directive compile / linking functions which contains normalized DOM element
+ * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed
+ * since all of these are treated as equivalent in Angular:
+ *
+ *
+ */
+
+/**
+ * @ngdoc property
+ * @name angular.module.ng.$compile.directive.Attributes#$attr
+ * @propertyOf angular.module.ng.$compile.directive.Attributes
+ * @returns {object} A map of DOM element attribute names to the normalized name. This is
+ * needed to do reverse lookup from normalized name back to actual name.
+ */
+
+
+/**
+ * @ngdoc function
+ * @name angular.module.ng.$compile.directive.Attributes#$set
+ * @methodOf angular.module.ng.$compile.directive.Attributes
+ * @function
+ *
+ * @description
+ * Set DOM element attribute value.
+ *
+ *
+ * @param {string} name Normalized element attribute name of the property to modify. The name is
+ * revers translated using the {@link angular.module.ng.$compile.directive.Attributes#$attr $attr}
+ * property to the original name.
+ * @param {string} value Value to set the attribute to.
+ */
+
/**
@@ -4906,7 +5074,7 @@ LocationUrl.prototype = {
* Return full url representation with all segments encoded according to rules specified in
* {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}.
*
- * @return {string}
+ * @return {string} full url
*/
absUrl: locationGetter('$$absUrl'),
@@ -4923,7 +5091,7 @@ LocationUrl.prototype = {
* Change path, search and hash, when called with parameter and return `$location`.
*
* @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
- * @return {string}
+ * @return {string} url
*/
url: function(url, replace) {
if (isUndefined(url))
@@ -4947,7 +5115,7 @@ LocationUrl.prototype = {
*
* Return protocol of current url.
*
- * @return {string}
+ * @return {string} protocol of current url
*/
protocol: locationGetter('$$protocol'),
@@ -4961,7 +5129,7 @@ LocationUrl.prototype = {
*
* Return host of current url.
*
- * @return {string}
+ * @return {string} host of current url.
*/
host: locationGetter('$$host'),
@@ -4975,7 +5143,7 @@ LocationUrl.prototype = {
*
* Return port of current url.
*
- * @return {Number}
+ * @return {Number} port
*/
port: locationGetter('$$port'),
@@ -4995,7 +5163,7 @@ LocationUrl.prototype = {
* if it is missing.
*
* @param {string=} path New path
- * @return {string}
+ * @return {string} path
*/
path: locationGetterSetter('$$path', function(path) {
return path.charAt(0) == '/' ? path : '/' + path;
@@ -5017,7 +5185,7 @@ LocationUrl.prototype = {
* @param {string=} paramValue If `search` is a string, then `paramValue` will override only a
* single search parameter. If the value is `null`, the parameter will be deleted.
*
- * @return {string}
+ * @return {string} search
*/
search: function(search, paramValue) {
if (isUndefined(search))
@@ -5050,7 +5218,7 @@ LocationUrl.prototype = {
* Change hash fragment when called with parameter and return `$location`.
*
* @param {string=} hash New hash fragment
- * @return {string}
+ * @return {string} hash
*/
hash: locationGetterSetter('$$hash', identity),
@@ -5097,10 +5265,13 @@ function locationGetterSetter(property, preprocess) {
*
* @requires $browser
* @requires $sniffer
- * @requires $document
+ * @requires $rootElement
*
* @description
- * The $location service parses the URL in the browser address bar (based on the {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL available to your application. Changes to the URL in the address bar are reflected into $location service and changes to $location are reflected into the browser address bar.
+ * The $location service parses the URL in the browser address bar (based on the
+ * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL
+ * available to your application. Changes to the URL in the address bar are reflected into
+ * $location service and changes to $location are reflected into the browser address bar.
*
* **The $location service:**
*
@@ -5113,7 +5284,8 @@ function locationGetterSetter(property, preprocess) {
* - Clicks on a link.
* - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
*
- * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location}
+ * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular
+ * Services: Using $location}
*/
/**
@@ -5160,67 +5332,75 @@ function $LocationProvider(){
}
};
- this.$get = ['$rootScope', '$browser', '$sniffer', '$document',
- function( $rootScope, $browser, $sniffer, $document) {
- var currentUrl,
+ this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
+ function( $rootScope, $browser, $sniffer, $rootElement) {
+ var $location,
basePath = $browser.baseHref() || '/',
pathPrefix = pathPrefixFromBase(basePath),
- initUrl = $browser.url();
+ initUrl = $browser.url(),
+ absUrlPrefix;
if (html5Mode) {
if ($sniffer.history) {
- currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix);
+ $location = new LocationUrl(
+ convertToHtml5Url(initUrl, basePath, hashPrefix),
+ pathPrefix);
} else {
- currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix),
- hashPrefix);
+ $location = new LocationHashbangUrl(
+ convertToHashbangUrl(initUrl, basePath, hashPrefix),
+ hashPrefix);
}
-
- // link rewriting
- var u = currentUrl,
- absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix;
-
- $document.bind('click', function(event) {
- // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
- // currently we open nice url link and redirect then
-
- if (event.ctrlKey || event.metaKey || event.which == 2) return;
-
- var elm = jqLite(event.target);
-
- // traverse the DOM up to find first A tag
- while (elm.length && lowercase(elm[0].nodeName) !== 'a') {
- elm = elm.parent();
- }
-
- var absHref = elm.prop('href');
-
- if (!absHref ||
- elm.attr('target') ||
- absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path
- return;
- }
-
- // update location with href without the prefix
- currentUrl.url(absHref.substr(absUrlPrefix.length));
- $rootScope.$apply();
- event.preventDefault();
- // hack to work around FF6 bug 684208 when scenario runner clicks on links
- window.angular['ff-684208-preventDefault'] = true;
- });
} else {
- currentUrl = new LocationHashbangUrl(initUrl, hashPrefix);
+ $location = new LocationHashbangUrl(initUrl, hashPrefix);
}
+ // link rewriting
+ absUrlPrefix = composeProtocolHostPort(
+ $location.protocol(), $location.host(), $location.port()) + pathPrefix;
+
+ $rootElement.bind('click', function(event) {
+ // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
+ // currently we open nice url link and redirect then
+
+ if (event.ctrlKey || event.metaKey || event.which == 2) return;
+
+ var elm = jqLite(event.target);
+
+ // traverse the DOM up to find first A tag
+ while (elm.length && lowercase(elm[0].nodeName) !== 'a') {
+ elm = elm.parent();
+ }
+
+ var absHref = elm.prop('href');
+
+ if (!absHref ||
+ elm.attr('target') ||
+ absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path
+ return;
+ }
+
+ // update location with href without the prefix
+ $location.url(absHref.substr(absUrlPrefix.length));
+ $rootScope.$apply();
+ event.preventDefault();
+ // 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 (currentUrl.absUrl() != initUrl) {
- $browser.url(currentUrl.absUrl(), true);
+ if ($location.absUrl() != initUrl) {
+ $browser.url($location.absUrl(), true);
}
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl) {
- if (currentUrl.absUrl() != newUrl) {
+ if ($location.absUrl() != newUrl) {
$rootScope.$evalAsync(function() {
- currentUrl.$$parse(newUrl);
+ var oldUrl = $location.absUrl();
+
+ $location.$$parse(newUrl);
+ afterLocationChange(oldUrl);
});
if (!$rootScope.$$phase) $rootScope.$digest();
}
@@ -5229,18 +5409,30 @@ function $LocationProvider(){
// update browser
var changeCounter = 0;
$rootScope.$watch(function $locationWatch() {
- if ($browser.url() != currentUrl.absUrl()) {
+ var oldUrl = $browser.url();
+
+ if (!changeCounter || oldUrl != $location.absUrl()) {
changeCounter++;
$rootScope.$evalAsync(function() {
- $browser.url(currentUrl.absUrl(), currentUrl.$$replace);
- currentUrl.$$replace = false;
+ if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
+ defaultPrevented) {
+ $location.$$parse(oldUrl);
+ } else {
+ $browser.url($location.absUrl(), $location.$$replace);
+ $location.$$replace = false;
+ afterLocationChange(oldUrl);
+ }
});
}
return changeCounter;
});
- return currentUrl;
+ return $location;
+
+ function afterLocationChange(oldUrl) {
+ $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl);
+ }
}];
}
@@ -6166,6 +6358,39 @@ function getterFn(path, csp) {
///////////////////////////////////
+/**
+ * @ngdoc function
+ * @name angular.module.ng.$parse
+ * @function
+ *
+ * @description
+ *
+ * Converts Angular {@link guide/expression expression} into a function.
+ *
+ *
+ *
+ *
+ * @param {string} expression String expression to compile.
+ * @returns {function(context, locals)} a function which represents the compiled expression:
+ *
+ * * `context`: an object against which any expressions embedded in the strings are evaluated
+ * against (Topically a scope object).
+ * * `locals`: local variables context object, useful for overriding values in `context`.
+ *
+ * The return function also has an `assign` property, if the expression is assignable, which
+ * allows one to set values to expressions.
+ *
+ */
function $ParseProvider() {
var cache = {};
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
@@ -6592,7 +6817,7 @@ function $RouteProvider(){
* @methodOf angular.module.ng.$routeProvider
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
- * contains redudant trailing slash or is missing one, the route will still match and the
+ * 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 exacly match the
* route definition.
* @param {Object} route Mapping information to be assigned to `$route.current` on route
@@ -6602,16 +6827,30 @@ function $RouteProvider(){
*
* - `controller` – `{function()=}` – Controller fn that should be associated with newly
* created scope.
- * - `template` – `{string=}` – path to an html template that should be used by
+ * - `template` – `{string=}` – html template as a string that should be used by
* {@link angular.module.ng.$compileProvider.directive.ngView ngView} or
* {@link angular.module.ng.$compileProvider.directive.ngInclude ngInclude} directives.
+ * this property takes precedence over `templateUrl`.
+ * - `templateUrl` – `{string=}` – path to an html template that should be used by
+ * {@link angular.module.ng.$compileProvider.directive.ngView ngView}.
+ * - `resolve` - `{Object.=}` - An optional map of dependencies which should
+ * be injected into the controller. If any of these dependencies are promises, they will be
+ * resolved and converted to a value before the controller is instantiated and the
+ * `$aftreRouteChange` 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/angular.module.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.
+ *
* - `redirectTo` – {(string|function())=} – value to update
* {@link angular.module.ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
*
* - `{Object.}` - route parameters extracted from the current
- * `$location.path()` by applying the current route template.
+ * `$location.path()` by applying the current route templateUrl.
* - `{string}` - current `$location.path()`
* - `{Object}` - current `$location.search()`
*
@@ -6662,8 +6901,8 @@ function $RouteProvider(){
};
- this.$get = ['$rootScope', '$location', '$routeParams',
- function( $rootScope, $location, $routeParams) {
+ this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache',
+ function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) {
/**
* @ngdoc object
@@ -6672,6 +6911,16 @@ function $RouteProvider(){
* @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 angular.module.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.