(js/css) Update generated files

This commit is contained in:
InverseBot
2016-02-03 13:34:41 -05:00
parent 3c53186db3
commit 2bbb88e014
30 changed files with 9322 additions and 6112 deletions
+336 -179
View File
@@ -1,6 +1,6 @@
/**
* State-based routing for AngularJS
* @version v0.2.15
* @version v0.2.17
* @link http://angular-ui.github.com/
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
@@ -22,7 +22,8 @@ var isDefined = angular.isDefined,
isArray = angular.isArray,
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy;
copy = angular.copy,
toJson = angular.toJson;
function inherit(parent, extra) {
return extend(new (extend(function() {}, { prototype: parent }))(), extra);
@@ -109,7 +110,7 @@ function inheritParams(currentParams, newParams, $current, $to) {
var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
for (var i in parents) {
if (!parents[i].params) continue;
if (!parents[i] || !parents[i].params) continue;
parentParams = objectKeys(parents[i].params);
if (!parentParams.length) continue;
@@ -746,13 +747,13 @@ function UrlMatcher(pattern, config, parentMatcher) {
// The regular expression is somewhat complicated due to the need to allow curly braces
// inside the regular expression. The placeholder regexp breaks down as follows:
// ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case)
// \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
// \{([\w\[\]]+)(?:\:\s*( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
// (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either
// [^{}\\]+ - anything other than curly braces or backslash
// \\. - a backslash escape
// \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
searchPlaceholder = /([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
compiled = '^', last = 0, m,
segments = this.segments = [],
parentParams = parentMatcher ? parentMatcher.params : {},
@@ -762,7 +763,7 @@ function UrlMatcher(pattern, config, parentMatcher) {
function addParameter(id, type, config, location) {
paramNames.push(id);
if (parentParams[id]) return parentParams[id];
if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
if (!/^\w+([-.]+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
params[id] = new $$UMFP.Param(id, type, config, location);
return params[id];
@@ -773,7 +774,10 @@ function UrlMatcher(pattern, config, parentMatcher) {
if (!pattern) return result;
switch(squash) {
case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
case true: surroundPattern = ['?(', ')?']; break;
case true:
result = result.replace(/\/$/, '');
surroundPattern = ['(?:\/(', ')|\/)?'];
break;
default: surroundPattern = ['(' + squash + "|", ')?']; break;
}
return result + surroundPattern[0] + pattern + surroundPattern[1];
@@ -789,7 +793,11 @@ function UrlMatcher(pattern, config, parentMatcher) {
cfg = config.params[id];
segment = pattern.substring(last, m.index);
regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
if (regexp) {
type = $$UMFP.type(regexp) || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
}
return {
id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
};
@@ -919,20 +927,29 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
return map(allReversed, unquoteDashes).reverse();
}
var param, paramVal;
for (i = 0; i < nPath; i++) {
paramName = paramNames[i];
var param = this.params[paramName];
var paramVal = m[i+1];
param = this.params[paramName];
paramVal = m[i+1];
// if the param value matches a pre-replace pair, replace the value before decoding.
for (j = 0; j < param.replace; j++) {
for (j = 0; j < param.replace.length; j++) {
if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
}
if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
if (isDefined(paramVal)) paramVal = param.type.decode(paramVal);
values[paramName] = param.value(paramVal);
}
for (/**/; i < nTotal; i++) {
paramName = paramNames[i];
values[paramName] = this.params[paramName].value(searchParams[paramName]);
param = this.params[paramName];
paramVal = searchParams[paramName];
for (j = 0; j < param.replace.length; j++) {
if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
}
if (isDefined(paramVal)) paramVal = param.type.decode(paramVal);
values[paramName] = param.value(paramVal);
}
return values;
@@ -956,7 +973,7 @@ UrlMatcher.prototype.parameters = function (param) {
/**
* @ngdoc function
* @name ui.router.util.type:UrlMatcher#validate
* @name ui.router.util.type:UrlMatcher#validates
* @methodOf ui.router.util.type:UrlMatcher
*
* @description
@@ -1009,6 +1026,8 @@ UrlMatcher.prototype.format = function (values) {
if (isPathParam) {
var nextSegment = segments[i + 1];
var isFinalPathParam = i + 1 === nPath;
if (squash === false) {
if (encoded != null) {
if (isArray(encoded)) {
@@ -1024,9 +1043,12 @@ UrlMatcher.prototype.format = function (values) {
} else if (isString(squash)) {
result += squash + nextSegment;
}
if (isFinalPathParam && param.squash === true && result.slice(-1) === '/') result = result.slice(0, -1);
} else {
if (encoded == null || (isDefaultValue && squash !== false)) continue;
if (!isArray(encoded)) encoded = [ encoded ];
if (encoded.length === 0) continue;
encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
result += (search ? '&' : '?') + (name + '=' + encoded);
search = true;
@@ -1191,6 +1213,7 @@ Type.prototype.$asArray = function(mode, isSearch) {
// Wraps type (.is/.encode/.decode) functions to operate on each value of an array
function arrayHandler(callback, allTruthyMode) {
return function handleArray(val) {
if (isArray(val) && val.length === 0) return val;
val = arrayWrap(val);
var result = map(val, callback);
if (allTruthyMode === true)
@@ -1239,8 +1262,12 @@ function $UrlMatcherFactory() {
var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
// Use tildes to pre-encode slashes.
// If the slashes are simply URLEncoded, the browser can choose to pre-decode them,
// and bidirectional encoding/decoding fails.
// Tilde was chosen because it's not a RFC 3986 section 2.2 Reserved Character
function valToString(val) { return val != null ? val.toString().replace(/~/g, "~~").replace(/\//g, "~2F") : val; }
function valFromString(val) { return val != null ? val.toString().replace(/~2F/g, "/").replace(/~~/g, "~") : val; }
var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
string: {
@@ -1583,7 +1610,12 @@ function $UrlMatcherFactory() {
if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
if (urlType) return urlType;
if (!config.type) return (location === "config" ? $types.any : $types.string);
return config.type instanceof Type ? config.type : new Type(config.type);
if (angular.isString(config.type))
return $types[config.type];
if (config.type instanceof Type)
return config.type;
return new Type(config.type);
}
// array config: param name (param[]) overrides default settings. explicit config overrides param name.
@@ -1778,7 +1810,7 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
* });
* </pre>
*
* @param {object} rule Handler function that takes `$injector` and `$location`
* @param {function} rule Handler function that takes `$injector` and `$location`
* services as arguments. You can use them to return a valid path as a string.
*
* @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
@@ -1814,7 +1846,7 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
* });
* </pre>
*
* @param {string|object} rule The url path you want to redirect to or a function
* @param {string|function} rule The url path you want to redirect to or a function
* rule that returns the url path. The function version is passed two params:
* `$injector` and `$location` services, and must return a url string.
*
@@ -1843,7 +1875,9 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
* @methodOf ui.router.router.$urlRouterProvider
*
* @description
* Registers a handler for a given url matching. if handle is a string, it is
* Registers a handler for a given url matching.
*
* If the handler is a string, it is
* treated as a redirect, and is interpolated according to the syntax of match
* (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
*
@@ -1872,7 +1906,7 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
* </pre>
*
* @param {string|object} what The incoming path that you want to redirect.
* @param {string|object} handler The path you want to redirect your user to.
* @param {string|function} handler The path you want to redirect your user to.
*/
this.when = function (what, handler) {
var redirect, handlerIsString = isString(handler);
@@ -1983,8 +2017,8 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
*
*/
this.$get = $get;
$get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
function $get( $location, $rootScope, $injector, $browser) {
$get.$inject = ['$location', '$rootScope', '$injector', '$browser', '$sniffer'];
function $get( $location, $rootScope, $injector, $browser, $sniffer) {
var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
@@ -2024,6 +2058,12 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
return listener;
}
rules.sort(function(ruleA, ruleB) {
var aLength = ruleA.prefix ? ruleA.prefix.length : 0;
var bLength = ruleB.prefix ? ruleB.prefix.length : 0;
return bLength - aLength;
});
if (!interceptDeferred) listen();
return {
@@ -2117,6 +2157,8 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
if (angular.isObject(isHtml5)) {
isHtml5 = isHtml5.enabled;
}
isHtml5 = isHtml5 && $sniffer.history;
var url = urlMatcher.format(params);
options = options || {};
@@ -2190,7 +2232,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
// inherit 'data' from parent and override by own values (if any)
data: function(state) {
if (state.parent && state.parent.data) {
state.data = state.self.data = extend({}, state.parent.data, state.data);
state.data = state.self.data = inherit(state.parent.data, state.data);
}
return state.data;
},
@@ -2321,7 +2363,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
var name = state.name;
if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
if (states.hasOwnProperty(name)) throw new Error("State '" + name + "' is already defined");
// Get parent name
var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
@@ -2689,7 +2731,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
*
* Callback function for when a state is entered. Good way
* to trigger an action or dispatch an event, such as opening a dialog.
* If minifying your scripts, make sure to explictly annotate this function,
* If minifying your scripts, make sure to explicitly annotate this function,
* because it won't be automatically annotated by your build tools.
*
* <pre>onEnter: function(MyService, $stateParams) {
@@ -2701,7 +2743,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
*
* Callback function for when a state is exited. Good way to
* trigger an action or dispatch an event, such as opening a dialog.
* If minifying your scripts, make sure to explictly annotate this function,
* If minifying your scripts, make sure to explicitly annotate this function,
* because it won't be automatically annotated by your build tools.
*
* <pre>onExit: function(MyService, $stateParams) {
@@ -3032,7 +3074,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
*
* @param {object=} params A map of the parameters that will be sent to the state,
* will populate $stateParams. Any parameters that are not specified will be inherited from currently
* defined parameters. This allows, for example, going to a sibling state that shares parameters
* defined parameters. Only parameters specified in the state definition can be overridden, new
* parameters will be ignored. This allows, for example, going to a sibling state that shares parameters
* specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
* transitioning to a sibling will get you the parameters for all parents, transitioning to a child
* will get you all current parameters, etc.
@@ -3044,9 +3087,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
* - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
* defines which state to be relative from.
* - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
* - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
* have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
* use this when you want to force a reload when *everything* is the same, including search params.
* - **`reload`** (v0.2.5) - {boolean=false|string|object}, If `true` will force transition even if no state or params
* have changed. It will reload the resolves and views of the current state and parent states.
* If `reload` is a string (or state object), the state object is fetched (by name, or object reference); and \
* the transition reloads the resolves and views for that matched state, and all its children states.
*
* @returns {promise} A promise representing the state of the new transition.
*
@@ -3184,6 +3228,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
if (hash) toParams['#'] = hash;
$state.params = toParams;
copy($state.params, $stateParams);
copy(filterByKeys(to.params.$$keys(), $stateParams), to.locals.globals.$stateParams);
if (options.location && to.navigable && to.navigable.url) {
$urlRouter.push(to.navigable.url, toParams, {
$$avoidResync: true, replace: options.location === 'replace'
@@ -3196,7 +3241,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
// Filter parameters before we pass them to event handlers etc.
toParams = filterByKeys(to.params.$$keys(), toParams || {});
// Re-add the saved hash before we start returning things or broadcasting $stateChangeStart
if (hash) toParams['#'] = hash;
// Broadcast start event and cancel the transition if requested
if (options.notify) {
/**
@@ -3226,9 +3274,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
* })
* </pre>
*/
if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams, options).defaultPrevented) {
$rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
$urlRouter.update();
//Don't update and resync url if there's been a new transition started. see issue #2238, #600
if ($state.transition == null) $urlRouter.update();
return TransitionPrevented;
}
}
@@ -3274,9 +3323,6 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
}
}
// Re-add the saved hash before we start returning things
if (hash) toParams['#'] = hash;
// Run it again, to catch any transitions in callbacks
if ($state.transition !== transition) return TransitionSuperseded;
@@ -3610,7 +3656,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
}
angular.module('ui.router.state')
.value('$stateParams', {})
.factory('$stateParams', function () { return {}; })
.provider('$state', $StateProvider);
@@ -3651,32 +3697,6 @@ function $ViewProvider() {
if (options.view) {
result = $templateFactory.fromConfig(options.view, options.params, options.locals);
}
if (result && options.notify) {
/**
* @ngdoc event
* @name ui.router.state.$state#$viewContentLoading
* @eventOf ui.router.state.$view
* @eventType broadcast on root scope
* @description
*
* Fired once the view **begins loading**, *before* the DOM is rendered.
*
* @param {Object} event Event object.
* @param {Object} viewConfig The view config properties (template, controller, etc).
*
* @example
*
* <pre>
* $scope.$on('$viewContentLoading',
* function(event, viewConfig){
* // Access to all the view config properties.
* // and one special property 'targetView'
* // viewConfig.targetView
* });
* </pre>
*/
$rootScope.$broadcast('$viewContentLoading', options);
}
return result;
}
};
@@ -3883,12 +3903,18 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
if ($animate) {
return {
enter: function(element, target, cb) {
var promise = $animate.enter(element, null, target, cb);
if (promise && promise.then) promise.then(cb);
if (angular.version.minor > 2) {
$animate.enter(element, null, target).then(cb);
} else {
$animate.enter(element, null, target, cb);
}
},
leave: function(element, cb) {
var promise = $animate.leave(element, cb);
if (promise && promise.then) promise.then(cb);
if (angular.version.minor > 2) {
$animate.leave(element).then(cb);
} else {
$animate.leave(element, cb);
}
}
};
}
@@ -3920,31 +3946,41 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
scope.$on('$stateChangeSuccess', function() {
updateView(false);
});
scope.$on('$viewContentLoading', function() {
updateView(false);
});
updateView(true);
function cleanupLastView() {
if (previousEl) {
previousEl.remove();
previousEl = null;
var _previousEl = previousEl;
var _currentScope = currentScope;
if (_currentScope) {
_currentScope._willBeDestroyed = true;
}
if (currentScope) {
currentScope.$destroy();
currentScope = null;
function cleanOld() {
if (_previousEl) {
_previousEl.remove();
}
if (_currentScope) {
_currentScope.$destroy();
}
}
if (currentEl) {
renderer.leave(currentEl, function() {
cleanOld();
previousEl = null;
});
previousEl = currentEl;
currentEl = null;
} else {
cleanOld();
previousEl = null;
}
currentEl = null;
currentScope = null;
}
function updateView(firstTime) {
@@ -3952,10 +3988,24 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
name = getUiViewName(scope, attrs, $element, $interpolate),
previousLocals = name && $state.$current && $state.$current.locals[name];
if (!firstTime && previousLocals === latestLocals) return; // nothing to do
if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
newScope = scope.$new();
latestLocals = $state.$current.locals[name];
/**
* @ngdoc event
* @name ui.router.state.directive:ui-view#$viewContentLoading
* @eventOf ui.router.state.directive:ui-view
* @eventType emits on ui-view directive scope
* @description
*
* Fired once the view **begins loading**, *before* the DOM is rendered.
*
* @param {Object} event Event object.
* @param {string} viewName Name of the view.
*/
newScope.$emit('$viewContentLoading', name);
var clone = $transclude(newScope, function(clone) {
renderer.enter(clone, $element, function onUiViewEnter() {
if(currentScope) {
@@ -3976,12 +4026,13 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
* @name ui.router.state.directive:ui-view#$viewContentLoaded
* @eventOf ui.router.state.directive:ui-view
* @eventType emits on ui-view directive scope
* @description *
* @description
* Fired once the view is **loaded**, *after* the DOM is rendered.
*
* @param {Object} event Event object.
* @param {string} viewName Name of the view.
*/
currentScope.$emit('$viewContentLoaded');
currentScope.$emit('$viewContentLoaded', name);
currentScope.$eval(onloadExp);
}
};
@@ -4058,6 +4109,43 @@ function stateContext(el) {
}
}
function getTypeInfo(el) {
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]';
var isForm = el[0].nodeName === "FORM";
return {
attr: isForm ? "action" : (isSvg ? 'xlink:href' : 'href'),
isAnchor: el.prop("tagName").toUpperCase() === "A",
clickable: !isForm
};
}
function clickHook(el, $state, $timeout, type, current) {
return function(e) {
var button = e.which || e.button, target = current();
if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) {
// HACK: This is to allow ng-clicks to be processed before the transition is initiated:
var transition = $timeout(function() {
$state.go(target.state, target.params, target.options);
});
e.preventDefault();
// if the state has no URL, ignore one preventDefault from the <a> directive.
var ignorePreventDefaultCount = type.isAnchor && !target.href ? 1: 0;
e.preventDefault = function() {
if (ignorePreventDefaultCount-- <= 0) $timeout.cancel(transition);
};
}
};
}
function defaultOpts(el, $state) {
return { relative: stateContext(el) || $state.$current, inherit: true };
}
/**
* @ngdoc directive
* @name ui.router.state.directive:ui-sref
@@ -4068,17 +4156,17 @@ function stateContext(el) {
* @restrict A
*
* @description
* A directive that binds a link (`<a>` tag) to a state. If the state has an associated
* URL, the directive will automatically generate & update the `href` attribute via
* the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
* the link will trigger a state transition with optional parameters.
* A directive that binds a link (`<a>` tag) to a state. If the state has an associated
* URL, the directive will automatically generate & update the `href` attribute via
* the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
* the link will trigger a state transition with optional parameters.
*
* Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
* Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
* handled natively by the browser.
*
* You can also use relative state paths within ui-sref, just like the relative
* You can also use relative state paths within ui-sref, just like the relative
* paths passed to `$state.go()`. You just need to be aware that the path is relative
* to the state that the link lives in, in other words the state that loaded the
* to the state that the link lives in, in other words the state that loaded the
* template containing the link.
*
* You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
@@ -4086,22 +4174,22 @@ function stateContext(el) {
* and `reload`.
*
* @example
* Here's an example of how you'd use ui-sref and how it would compile. If you have the
* Here's an example of how you'd use ui-sref and how it would compile. If you have the
* following template:
* <pre>
* <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
*
*
* <ul>
* <li ng-repeat="contact in contacts">
* <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
* </li>
* </ul>
* </pre>
*
*
* Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
* <pre>
* <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
*
*
* <ul>
* <li ng-repeat="contact in contacts">
* <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
@@ -4122,78 +4210,83 @@ function stateContext(el) {
*/
$StateRefDirective.$inject = ['$state', '$timeout'];
function $StateRefDirective($state, $timeout) {
var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
return {
restrict: 'A',
require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
link: function(scope, element, attrs, uiSrefActive) {
var ref = parseStateRef(attrs.uiSref, $state.current.name);
var params = null, url = null, base = stateContext(element) || $state.$current;
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
'xlink:href' : 'href';
var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
var isForm = element[0].nodeName === "FORM";
var attr = isForm ? "action" : hrefKind, nav = true;
var ref = parseStateRef(attrs.uiSref, $state.current.name);
var def = { state: ref.state, href: null, params: null };
var type = getTypeInfo(element);
var active = uiSrefActive[1] || uiSrefActive[0];
var options = { relative: base, inherit: true };
var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
def.options = extend(defaultOpts(element, $state), attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {});
angular.forEach(allowedOptions, function(option) {
if (option in optionsOverride) {
options[option] = optionsOverride[option];
}
});
var update = function(val) {
if (val) def.params = angular.copy(val);
def.href = $state.href(ref.state, def.params, def.options);
var update = function(newVal) {
if (newVal) params = angular.copy(newVal);
if (!nav) return;
newHref = $state.href(ref.state, params, options);
var activeDirective = uiSrefActive[1] || uiSrefActive[0];
if (activeDirective) {
activeDirective.$$addStateInfo(ref.state, params);
}
if (newHref === null) {
nav = false;
return false;
}
attrs.$set(attr, newHref);
if (active) active.$$addStateInfo(ref.state, def.params);
if (def.href !== null) attrs.$set(type.attr, def.href);
};
if (ref.paramExpr) {
scope.$watch(ref.paramExpr, function(newVal, oldVal) {
if (newVal !== params) update(newVal);
}, true);
params = angular.copy(scope.$eval(ref.paramExpr));
scope.$watch(ref.paramExpr, function(val) { if (val !== def.params) update(val); }, true);
def.params = angular.copy(scope.$eval(ref.paramExpr));
}
update();
if (isForm) return;
element.bind("click", function(e) {
var button = e.which || e.button;
if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
// HACK: This is to allow ng-clicks to be processed before the transition is initiated:
var transition = $timeout(function() {
$state.go(ref.state, params, options);
});
e.preventDefault();
// if the state has no URL, ignore one preventDefault from the <a> directive.
var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
e.preventDefault = function() {
if (ignorePreventDefaultCount-- <= 0)
$timeout.cancel(transition);
};
}
});
if (!type.clickable) return;
element.bind("click", clickHook(element, $state, $timeout, type, function() { return def; }));
}
};
}
/**
* @ngdoc directive
* @name ui.router.state.directive:ui-state
*
* @requires ui.router.state.uiSref
*
* @restrict A
*
* @description
* Much like ui-sref, but will accept named $scope properties to evaluate for a state definition,
* params and override options.
*
* @param {string} ui-state 'stateName' can be any valid absolute or relative state
* @param {Object} ui-state-params params to pass to {@link ui.router.state.$state#href $state.href()}
* @param {Object} ui-state-opts options to pass to {@link ui.router.state.$state#go $state.go()}
*/
$StateRefDynamicDirective.$inject = ['$state', '$timeout'];
function $StateRefDynamicDirective($state, $timeout) {
return {
restrict: 'A',
require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
link: function(scope, element, attrs, uiSrefActive) {
var type = getTypeInfo(element);
var active = uiSrefActive[1] || uiSrefActive[0];
var group = [attrs.uiState, attrs.uiStateParams || null, attrs.uiStateOpts || null];
var watch = '[' + group.map(function(val) { return val || 'null'; }).join(', ') + ']';
var def = { state: null, params: null, options: null, href: null };
function runStateRefLink (group) {
def.state = group[0]; def.params = group[1]; def.options = group[2];
def.href = $state.href(def.state, def.params, def.options);
if (active) active.$$addStateInfo(ref.state, def.params);
if (def.href) attrs.$set(type.attr, def.href);
}
scope.$watch(watch, runStateRefLink, true);
runStateRefLink(scope.$eval(watch));
if (!type.clickable) return;
element.bind("click", clickHook(element, $state, $timeout, type, function() { return def; }));
}
};
}
/**
* @ngdoc directive
* @name ui.router.state.directive:ui-sref-active
@@ -4251,6 +4344,24 @@ function $StateRefDirective($state, $timeout) {
* </li>
* </ul>
* </pre>
*
* It is also possible to pass ui-sref-active an expression that evaluates
* to an object hash, whose keys represent active class names and whose
* values represent the respective state names/globs.
* ui-sref-active will match if the current active state **includes** any of
* the specified state names/globs, even the abstract ones.
*
* @Example
* Given the following template, with "admin" being an abstract state:
* <pre>
* <div ui-sref-active="{'active': 'admin.*'}">
* <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
* </div>
* </pre>
*
* When the current state is "admin.roles" the "active" class will be applied
* to both the <div> and <a> elements. It is important to note that the state
* names/globs passed to ui-sref-active shadow the state provided by ui-sref.
*/
/**
@@ -4272,53 +4383,98 @@ $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
return {
restrict: "A",
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
var states = [], activeClass;
controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) {
var states = [], activeClasses = {}, activeEqClass, uiSrefActive;
// There probably isn't much point in $observing this
// uiSrefActive and uiSrefActiveEq share the same directive object with some
// slight difference in logic routing
activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
try {
uiSrefActive = $scope.$eval($attrs.uiSrefActive);
} catch (e) {
// Do nothing. uiSrefActive is not a valid expression.
// Fall back to using $interpolate below
}
uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
if (isObject(uiSrefActive)) {
forEach(uiSrefActive, function(stateOrName, activeClass) {
if (isString(stateOrName)) {
var ref = parseStateRef(stateOrName, $state.current.name);
addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
}
});
}
// Allow uiSref to communicate with uiSrefActive[Equals]
this.$$addStateInfo = function (newState, newParams) {
var state = $state.get(newState, stateContext($element));
states.push({
state: state || { name: newState },
params: newParams
});
// we already got an explicit state provided by ui-sref-active, so we
// shadow the one that comes from ui-sref
if (isObject(uiSrefActive) && states.length > 0) {
return;
}
addState(newState, newParams, uiSrefActive);
update();
};
$scope.$on('$stateChangeSuccess', update);
function addState(stateName, stateParams, activeClass) {
var state = $state.get(stateName, stateContext($element));
var stateHash = createStateHash(stateName, stateParams);
states.push({
state: state || { name: stateName },
params: stateParams,
hash: stateHash
});
activeClasses[stateHash] = activeClass;
}
/**
* @param {string} state
* @param {Object|string} [params]
* @return {string}
*/
function createStateHash(state, params) {
if (!isString(state)) {
throw new Error('state should be a string');
}
if (isObject(params)) {
return state + toJson(params);
}
params = $scope.$eval(params);
if (isObject(params)) {
return state + toJson(params);
}
return state;
}
// Update route state
function update() {
if (anyMatch()) {
$element.addClass(activeClass);
} else {
$element.removeClass(activeClass);
}
}
function anyMatch() {
for (var i = 0; i < states.length; i++) {
if (isMatch(states[i].state, states[i].params)) {
return true;
if (anyMatch(states[i].state, states[i].params)) {
addClass($element, activeClasses[states[i].hash]);
} else {
removeClass($element, activeClasses[states[i].hash]);
}
if (exactMatch(states[i].state, states[i].params)) {
addClass($element, activeEqClass);
} else {
removeClass($element, activeEqClass);
}
}
return false;
}
function isMatch(state, params) {
if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
return $state.is(state.name, params);
} else {
return $state.includes(state.name, params);
}
}
function addClass(el, className) { $timeout(function () { el.addClass(className); }); }
function removeClass(el, className) { el.removeClass(className); }
function anyMatch(state, params) { return $state.includes(state.name, params); }
function exactMatch(state, params) { return $state.is(state.name, params); }
update();
}]
};
}
@@ -4326,7 +4482,8 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) {
angular.module('ui.router.state')
.directive('uiSref', $StateRefDirective)
.directive('uiSrefActive', $StateRefActiveDirective)
.directive('uiSrefActiveEq', $StateRefActiveDirective);
.directive('uiSrefActiveEq', $StateRefActiveDirective)
.directive('uiState', $StateRefDynamicDirective);
/**
* @ngdoc filter
@@ -4339,8 +4496,8 @@ angular.module('ui.router.state')
*/
$IsStateFilter.$inject = ['$state'];
function $IsStateFilter($state) {
var isFilter = function (state) {
return $state.is(state);
var isFilter = function (state, params) {
return $state.is(state, params);
};
isFilter.$stateful = true;
return isFilter;
@@ -4357,8 +4514,8 @@ function $IsStateFilter($state) {
*/
$IncludedByStateFilter.$inject = ['$state'];
function $IncludedByStateFilter($state) {
var includesFilter = function (state) {
return $state.includes(state);
var includesFilter = function (state, params, options) {
return $state.includes(state, params, options);
};
includesFilter.$stateful = true;
return includesFilter;