diff --git a/src/common.js b/src/common.js index d530331cd..323d31e03 100644 --- a/src/common.js +++ b/src/common.js @@ -44,6 +44,24 @@ function ancestors(first, second) { return path; } +/** + * IE8-safe wrapper for `Object.keys()`. + * + * @param {Object} object A JavaScript object. + * @return {Array} Returns the keys of the object as an array. + */ +function keys(object) { + if (Object.keys) { + return Object.keys(object); + } + var result = []; + + angular.forEach(object, function(val, key) { + result.push(key); + }); + return result; +} + /** * IE8-safe wrapper for `Array.prototype.indexOf()`. * @@ -91,6 +109,61 @@ function inheritParams(currentParams, newParams, $current, $to) { return extend({}, inherited, newParams); } +/** + * Normalizes a set of values to string or `null`, filtering them by a list of keys. + * + * @param {Array} keys The list of keys to normalize/return. + * @param {Object} values An object hash of values to normalize. + * @return {Object} Returns an object hash of normalized string values. + */ +function normalize(keys, values) { + var normalized = {}; + + forEach(keys, function (name) { + var value = values[name]; + normalized[name] = (value != null) ? String(value) : null; + }); + return normalized; +} + +/** + * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. + * + * @param {Object} a The first object. + * @param {Object} b The second object. + * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, + * it defaults to the list of keys in `a`. + * @return {Boolean} Returns `true` if the keys match, otherwise `false`. + */ +function equalForKeys(a, b, keys) { + if (!keys) { + keys = []; + for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility + } + + for (var i=0; iContacts' + } + } + }).state('contacts.item', { + url: '/:id', + }).state('contacts.item.detail', { + url: '/detail/:foo' + }); + })); + + beforeEach(inject(function($document) { + document = $document[0]; + })); + + it('should update class for sibling uiSref', inject(function($rootScope, $q, $compile, $state) { + el = angular.element('
Contacts
'); + template = $compile(el)($rootScope); + $rootScope.$digest(); + + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); + $state.transitionTo('contacts'); + $q.flush(); + + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active'); + + $state.transitionTo('contacts.item', { id: 5 }); + $q.flush(); + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); + })); + + it('should match state\'s parameters', inject(function($rootScope, $q, $compile, $state) { + el = angular.element('
Contacts
'); + template = $compile(el)($rootScope); + $rootScope.$digest(); + + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); + $state.transitionTo('contacts.item.detail', { id: 5, foo: 'bar' }); + $q.flush(); + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active'); + + $state.transitionTo('contacts.item.detail', { id: 5, foo: 'baz' }); + $q.flush(); + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe(''); + })); + + it('should resolve relative state refs', inject(function($rootScope, $q, $compile, $state) { + el = angular.element('
'); + template = $compile(el)($rootScope); + $rootScope.$digest(); + + $state.transitionTo('contacts'); + $q.flush(); + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope'); + + $state.transitionTo('contacts.item', { id: 6 }); + $q.flush(); + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope active'); + + $state.transitionTo('contacts.item', { id: 5 }); + $q.flush(); + expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope'); + })); +}); \ No newline at end of file