diff --git a/src/state.js b/src/state.js index 2552eb964..c49932935 100644 --- a/src/state.js +++ b/src/state.js @@ -218,6 +218,41 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return state; } + // Checks text to see if it looks like a glob. + function isGlob (text) { + return text.indexOf('*') > -1; + } + + // Returns true if glob matches current $state name. + function doesStateMatchGlob (glob) { + var globSegments = glob.split('.'), + segments = $state.$current.name.split('.'); + + //match greedy starts + if (globSegments[0] === '**') { + segments = segments.slice(segments.indexOf(globSegments[1])); + segments.unshift('**'); + } + //match greedy ends + if (globSegments[globSegments.length - 1] === '**') { + segments.splice(segments.indexOf(globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE); + segments.push('**'); + } + + if (globSegments.length != segments.length) { + return false; + } + + //match single stars + for (var i = 0, l = globSegments.length; i < l; i++) { + if (globSegments[i] === '*') { + segments[i] = '*'; + } + } + + return segments.join('') === globSegments.join(''); + } + // Implicit root state that is always active root = registerState({ @@ -970,6 +1005,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ * * @example *
+ * $state.$current.name = 'contacts.details.item'; + * * $state.includes("contacts"); // returns true * $state.includes("contacts.details"); // returns true * $state.includes("contacts.details.item"); // returns true @@ -977,12 +1014,37 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ * $state.includes("about"); // returns false ** - * @param {string|object} stateOrName A full or partial state name to be searched for within the current state name. - * @param {object=} params A param object, e.g. `{sectionId: section.id}`, + * @description + * Basic globing patterns will also work. + * + * @example + *
+ * $state.$current.name = 'contacts.details.item.url'; + * + * $state.includes("*.details.*.*"); // returns true + * $state.includes("*.details.**"); // returns true + * $state.includes("**.item.**"); // returns true + * $state.includes("*.details.item.url"); // returns true + * $state.includes("*.details.*.url"); // returns true + * $state.includes("*.details.*"); // returns false + * $state.includes("item.**"); // returns false + *+ * + * @param {string} stateOrName A partial name to be searched for within the current state name. + * @param {object} params A param object, e.g. `{sectionId: section.id}`, * that you'd like to test against the current active state. * @returns {boolean} Returns true if it does include the state */ + $state.includes = function includes(stateOrName, params) { + if (isString(stateOrName) && isGlob(stateOrName)) { + if (doesStateMatchGlob(stateOrName)) { + stateOrName = $state.$current.name; + } else { + return false; + } + } + var state = findState(stateOrName); if (!isDefined(state)) { return undefined; @@ -1001,6 +1063,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $ return validParams; }; + /** * @ngdoc function * @name ui.router.state.$state#href diff --git a/test/stateSpec.js b/test/stateSpec.js index d5bc60a96..d91c49d53 100644 --- a/test/stateSpec.js +++ b/test/stateSpec.js @@ -552,6 +552,21 @@ describe('state', function () { expect($state.includes('about.person', {person: 'bob'})).toBe(true); expect($state.includes('about.person', {person: 'steve'})).toBe(false); })); + + it('should return true when the current state is passed with partial glob patterns', inject(function ($state, $q) { + $state.transitionTo('about.person.item', {person: 'bob', id: 5}); $q.flush(); + expect($state.includes('*.person.*')).toBe(true); + expect($state.includes('*.person.**')).toBe(true); + expect($state.includes('**.item.*')).toBe(false); + expect($state.includes('**.item')).toBe(true); + expect($state.includes('**.stuff.*')).toBe(false); + expect($state.includes('*.*.*')).toBe(true); + expect($state.includes('about.*.*')).toBe(true); + expect($state.includes('about.**')).toBe(true); + expect($state.includes('*.about.*')).toBe(false); + expect($state.includes('about.*.*', {person: 'bob'})).toBe(true); + expect($state.includes('about.*.*', {person: 'shawn'})).toBe(false); + })); }); describe('.current', function () {