From bbd967d8eccee89651bd776b05e8f6a748e05562 Mon Sep 17 00:00:00 2001 From: Evan Jacobs Date: Sat, 1 Mar 2014 19:55:36 -0500 Subject: [PATCH 001/102] feat(filterFilter): support filterFilter for object literal collection filterFilter was previously only available to array/array-like collections, but ngRepeat supports iterating over object literals. This PR enables filtering functionality over object literal properties. Fixes #6490 --- src/ng/filter/filter.js | 49 ++-- test/ng/filter/filterSpec.js | 427 +++++++++++++++++++++++++---------- 2 files changed, 331 insertions(+), 145 deletions(-) diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index 5c6bf2d79e7e..54147d37ef46 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -6,32 +6,33 @@ * @function * * @description - * Selects a subset of items from `array` and returns it as a new array. + * Returns a subset of items from `collection` that pass a supplied truth test. * - * @param {Array} array The source array. + * @param {Array|Object} collection The source collection. * @param {string|Object|function()} expression The predicate to be used for selecting items from - * `array`. + * `collection`. * * Can be one of: * - * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against - * the contents of the `array`. All strings or objects with string properties in `array` that contain this string - * will be returned. The predicate can be negated by prefixing the string with `!`. + * - `string`: The string is evaluated as an expression and the resulting value is used for + * substring match against the contents of the `collection`. All strings or objects with string + * properties in `collection` that contain this string will be returned. The predicate can be + * negated by prefixing the string with `!`. * * - `Object`: A pattern object can be used to filter specific properties on objects contained - * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items - * which have property `name` containing "M" and property `phone` containing "1". A special - * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * by `collection`. For example `{name:"M", phone:"1"}` predicate will return an collection of + * items which have property `name` containing "M" and property `phone` containing "1". A + * special property name `$` can be used (as in `{$:"text"}`) to accept a match against any * property of the object. That's equivalent to the simple substring match with a `string` * as described above. * - * - `function(value)`: A predicate function can be used to write arbitrary filters. The function is - * called for each element of `array`. The final result is an array of those elements that - * the predicate returned true for. + * - `function(value)`: A predicate function can be used to write arbitrary filters. The function + * is called for each element of `collection`. The final result is a collection of those + * elements of which the predicate returned true. * * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in * determining if the expected value (from the filter expression) and actual value (from - * the object in the array) should be considered a match. + * the object in the collection) should be considered a match. * * Can be one of: * @@ -114,10 +115,10 @@ */ function filterFilter() { - return function(array, expression, comparator) { - if (!isArray(array)) return array; + return function(collection, expression, comparator) { - var comparatorType = typeof(comparator), + var collectionIsArray = isArray(collection), + comparatorType = typeof(comparator), predicates = []; predicates.check = function(value) { @@ -206,15 +207,15 @@ function filterFilter() { predicates.push(expression); break; default: - return array; - } - var filtered = []; - for ( var j = 0; j < array.length; j++) { - var value = array[j]; - if (predicates.check(value)) { - filtered.push(value); - } + return collection; } + var filtered = collectionIsArray ? [] : {}; + forEach(collection, function(item, key){ + if (predicates.check(item)) { + if (collectionIsArray) filtered.push(item); + else filtered[key] = item; + } + }); return filtered; }; } diff --git a/test/ng/filter/filterSpec.js b/test/ng/filter/filterSpec.js index 7679136ac9f9..70fbc7638813 100644 --- a/test/ng/filter/filterSpec.js +++ b/test/ng/filter/filterSpec.js @@ -7,158 +7,343 @@ describe('Filter: filter', function() { filter = $filter('filter'); })); - it('should filter by string', function() { - var items = ['MIsKO', {name: 'shyam'}, ['adam'], 1234]; - expect(filter(items, '').length).toBe(4); - expect(filter(items, undefined).length).toBe(4); + describe('on an array collection', function() { - expect(filter(items, 'iSk').length).toBe(1); - expect(filter(items, 'isk')[0]).toBe('MIsKO'); + it('should filter by string', function() { + var items = ['MIsKO', {name: 'shyam'}, ['adam'], 1234]; + expect(filter(items, '').length).toBe(4); + expect(filter(items, undefined).length).toBe(4); - expect(filter(items, 'yam').length).toBe(1); - expect(filter(items, 'yam')[0]).toEqual(items[1]); + expect(filter(items, 'iSk').length).toBe(1); + expect(filter(items, 'isk')[0]).toBe('MIsKO'); - expect(filter(items, 'da').length).toBe(1); - expect(filter(items, 'da')[0]).toEqual(items[2]); + expect(filter(items, 'yam').length).toBe(1); + expect(filter(items, 'yam')[0]).toEqual(items[1]); - expect(filter(items, '34').length).toBe(1); - expect(filter(items, '34')[0]).toBe(1234); + expect(filter(items, 'da').length).toBe(1); + expect(filter(items, 'da')[0]).toEqual(items[2]); - expect(filter(items, "I don't exist").length).toBe(0); - }); + expect(filter(items, '34').length).toBe(1); + expect(filter(items, '34')[0]).toBe(1234); - it('should not read $ properties', function() { - expect(''.charAt(0)).toBe(''); // assumption + expect(filter(items, "I don't exist").length).toBe(0); + }); - var items = [{$name: 'misko'}]; - expect(filter(items, 'misko').length).toBe(0); - }); + it('should not read $ properties', function() { + expect(''.charAt(0)).toBe(''); // assumption - it('should filter on specific property', function() { - var items = [{ignore: 'a', name: 'a'}, {ignore: 'a', name: 'abc'}]; - expect(filter(items, {}).length).toBe(2); + var items = [{$name: 'misko'}]; + expect(filter(items, 'misko').length).toBe(0); + }); - expect(filter(items, {name: 'a'}).length).toBe(2); + it('should filter on specific property', function() { + var items = [{ignore: 'a', name: 'a'}, {ignore: 'a', name: 'abc'}]; + expect(filter(items, {}).length).toBe(2); - expect(filter(items, {name: 'b'}).length).toBe(1); - expect(filter(items, {name: 'b'})[0].name).toBe('abc'); - }); + expect(filter(items, {name: 'a'}).length).toBe(2); - it('should take function as predicate', function() { - var items = [{name: 'a'}, {name: 'abc', done: true}]; - expect(filter(items, function(i) {return i.done;}).length).toBe(1); - }); + expect(filter(items, {name: 'b'}).length).toBe(1); + expect(filter(items, {name: 'b'})[0].name).toBe('abc'); + }); - it('should take object as perdicate', function() { - var items = [{first: 'misko', last: 'hevery'}, - {first: 'adam', last: 'abrons'}]; + it('should take function as predicate', function() { + var items = [{name: 'a'}, {name: 'abc', done: true}]; + expect(filter(items, function(i) {return i.done;}).length).toBe(1); + }); - expect(filter(items, {first:'', last:''}).length).toBe(2); - expect(filter(items, {first:'', last:'hevery'}).length).toBe(1); - expect(filter(items, {first:'adam', last:'hevery'}).length).toBe(0); - expect(filter(items, {first:'misko', last:'hevery'}).length).toBe(1); - expect(filter(items, {first:'misko', last:'hevery'})[0]).toEqual(items[0]); - }); + it('should take object as predicate', function() { + var items = [{first: 'misko', last: 'hevery'}, + {first: 'adam', last: 'abrons'}]; + expect(filter(items, {first:'', last:''}).length).toBe(2); + expect(filter(items, {first:'', last:'hevery'}).length).toBe(1); + expect(filter(items, {first:'adam', last:'hevery'}).length).toBe(0); + expect(filter(items, {first:'misko', last:'hevery'}).length).toBe(1); + expect(filter(items, {first:'misko', last:'hevery'})[0]).toEqual(items[0]); + }); - it('should support predicat object with dots in the name', function() { - var items = [{'first.name': 'misko', 'last.name': 'hevery'}, - {'first.name': 'adam', 'last.name': 'abrons'}]; - expect(filter(items, {'first.name':'', 'last.name':''}).length).toBe(2); - expect(filter(items, {'first.name':'misko', 'last.name':''})).toEqual([items[0]]); - }); + it('should support predicate object with dots in the name', function() { + var items = [{'first.name': 'misko', 'last.name': 'hevery'}, + {'first.name': 'adam', 'last.name': 'abrons'}]; + expect(filter(items, {'first.name':'', 'last.name':''}).length).toBe(2); + expect(filter(items, {'first.name':'misko', 'last.name':''})).toEqual([items[0]]); + }); - it('should support deep predicate objects', function() { - var items = [{person: {name: 'John'}}, - {person: {name: 'Rita'}}, - {person: {name: 'Billy'}}, - {person: {name: 'Joan'}}]; - expect(filter(items, {person: {name: 'Jo'}}).length).toBe(2); - expect(filter(items, {person: {name: 'Jo'}})).toEqual([ - {person: {name: 'John'}}, {person: {name: 'Joan'}}]); - }); + it('should support deep predicate objects', function() { + var items = [{person: {name: 'John'}}, + {person: {name: 'Rita'}}, + {person: {name: 'Billy'}}, + {person: {name: 'Joan'}}]; + expect(filter(items, {person: {name: 'Jo'}}).length).toBe(2); + expect(filter(items, {person: {name: 'Jo'}})).toEqual([ + {person: {name: 'John'}}, {person: {name: 'Joan'}}]); + }); - it('should match any properties for given "$" property', function() { - var items = [{first: 'tom', last: 'hevery'}, - {first: 'adam', last: 'hevery', alias: 'tom', done: false}, - {first: 'john', last: 'clark', middle: 'tommy'}]; - expect(filter(items, {$: 'tom'}).length).toBe(3); - expect(filter(items, {$: 'a'}).length).toBe(2); - expect(filter(items, {$: false}).length).toBe(1); - expect(filter(items, {$: 10}).length).toBe(0); - expect(filter(items, {$: 'hevery'})[0]).toEqual(items[0]); - }); - it('should support boolean properties', function() { - var items = [{name: 'tom', current: true}, - {name: 'demi', current: false}, - {name: 'sofia'}]; + it('should match any properties for given "$" property', function() { + var items = [{first: 'tom', last: 'hevery'}, + {first: 'adam', last: 'hevery', alias: 'tom', done: false}, + {first: 'john', last: 'clark', middle: 'tommy'}]; + expect(filter(items, {$: 'tom'}).length).toBe(3); + expect(filter(items, {$: 'a'}).length).toBe(2); + expect(filter(items, {$: false}).length).toBe(1); + expect(filter(items, {$: 10}).length).toBe(0); + expect(filter(items, {$: 'hevery'})[0]).toEqual(items[0]); + }); - expect(filter(items, {current:true}).length).toBe(1); - expect(filter(items, {current:true})[0].name).toBe('tom'); - expect(filter(items, {current:false}).length).toBe(1); - expect(filter(items, {current:false})[0].name).toBe('demi'); - }); + it('should support boolean properties', function() { + var items = [{name: 'tom', current: true}, + {name: 'demi', current: false}, + {name: 'sofia'}]; + + expect(filter(items, {current:true}).length).toBe(1); + expect(filter(items, {current:true})[0].name).toBe('tom'); + expect(filter(items, {current:false}).length).toBe(1); + expect(filter(items, {current:false})[0].name).toBe('demi'); + }); + + it('should support negation operator', function() { + var items = ['misko', 'adam']; + + expect(filter(items, '!isk').length).toBe(1); + expect(filter(items, '!isk')[0]).toEqual(items[1]); + }); - it('should support negation operator', function() { - var items = ['misko', 'adam']; + describe('should support comparator', function() { + + it('as equality when true', function() { + var items = ['misko', 'adam', 'adamson']; + var expr = 'adam'; + expect(filter(items, expr, true)).toEqual([items[1]]); + expect(filter(items, expr, false)).toEqual([items[1], items[2]]); + + var items = [ + {key: 'value1', nonkey: 1}, + {key: 'value2', nonkey: 2}, + {key: 'value12', nonkey: 3}, + {key: 'value1', nonkey:4}, + {key: 'Value1', nonkey:5} + ]; + var expr = {key: 'value1'}; + expect(filter(items, expr, true)).toEqual([items[0], items[3]]); + + var items = [ + {key: 1, nonkey: 1}, + {key: 2, nonkey: 2}, + {key: 12, nonkey: 3}, + {key: 1, nonkey:4} + ]; + var expr = { key: 1 }; + expect(filter(items, expr, true)).toEqual([items[0], items[3]]); + + var expr = 12; + expect(filter(items, expr, true)).toEqual([items[2]]); + }); + + it('and use the function given to compare values', function() { + var items = [ + {key: 1, nonkey: 1}, + {key: 2, nonkey: 2}, + {key: 12, nonkey: 3}, + {key: 1, nonkey:14} + ]; + var expr = {key: 10}; + var comparator = function (obj,value) { + return obj > value; + }; + expect(filter(items, expr, comparator)).toEqual([items[2]]); + + expr = 10; + expect(filter(items, expr, comparator)).toEqual([items[2], items[3]]); + + }); + + }); - expect(filter(items, '!isk').length).toBe(1); - expect(filter(items, '!isk')[0]).toEqual(items[1]); }); - describe('should support comparator', function() { - - it('as equality when true', function() { - var items = ['misko', 'adam', 'adamson']; - var expr = 'adam'; - expect(filter(items, expr, true)).toEqual([items[1]]); - expect(filter(items, expr, false)).toEqual([items[1], items[2]]); - - var items = [ - {key: 'value1', nonkey: 1}, - {key: 'value2', nonkey: 2}, - {key: 'value12', nonkey: 3}, - {key: 'value1', nonkey:4}, - {key: 'Value1', nonkey:5} - ]; - var expr = {key: 'value1'}; - expect(filter(items, expr, true)).toEqual([items[0], items[3]]); - - var items = [ - {key: 1, nonkey: 1}, - {key: 2, nonkey: 2}, - {key: 12, nonkey: 3}, - {key: 1, nonkey:4} - ]; - var expr = { key: 1 }; - expect(filter(items, expr, true)).toEqual([items[0], items[3]]); - - var expr = 12; - expect(filter(items, expr, true)).toEqual([items[2]]); - }); - - it('and use the function given to compare values', function() { - var items = [ - {key: 1, nonkey: 1}, - {key: 2, nonkey: 2}, - {key: 12, nonkey: 3}, - {key: 1, nonkey:14} - ]; - var expr = {key: 10}; - var comparator = function (obj,value) { - return obj > value; + describe('on an object literal collection', function() { + + /* + You can't effectively test equality on object instances, so they will be compared below + via stringification. + */ + + // Helper to verify "lengths" of returned object literals + var getObjKeys = Object.keys || function(obj) { + var keys = []; + for (prop in obj) { + if (obj.hasOwnProperty(prop)) keys.push(prop); } - expect(filter(items, expr, comparator)).toEqual([items[2]]); + return keys; + }; + + // Helper to return the specified keys of an object as a new object (for test verification) + var stringifiedProps = function(obj, array) { + var props = {}; + forEach(array, function(key) { if (obj[key]) props[key] = obj[key]; }); + return JSON.stringify(props); + }; + + it('should filter by string', function() { + var items = {'0':'MIsKO', '1':{name: 'shyam'}, '2':['adam'], '3':1234}; + expect(getObjKeys(filter(items, '')).length).toBe(4); + + expect(getObjKeys(filter(items, 'iSk')).length).toBe(1); + expect(JSON.stringify(filter(items, 'isk'))).toBe(stringifiedProps(items,['0'])); - expr = 10; - expect(filter(items, expr, comparator)).toEqual([items[2], items[3]]); + expect(getObjKeys(filter(items, 'yam')).length).toBe(1); + expect(JSON.stringify(filter(items, 'yam'))).toEqual(stringifiedProps(items,['1'])); + expect(getObjKeys(filter(items, 'da')).length).toBe(1); + expect(JSON.stringify(filter(items, 'da'))).toEqual(stringifiedProps(items,['2'])); + + expect(getObjKeys(filter(items, '34')).length).toBe(1); + expect(JSON.stringify(filter(items, '34'))).toBe(stringifiedProps(items,['3'])); + + expect(getObjKeys(filter(items, "I don't exist")).length).toBe(0); + }); + + it('should not read $ properties', function() { + expect(''.charAt(0)).toBe(''); // assumption + + var items = {'0':{$name: 'misko'}}; + expect(getObjKeys(filter(items, 'misko')).length).toBe(0); }); + it('should filter on specific property', function() { + var items = {'0':{ignore: 'a', name: 'a'}, '1':{ignore: 'a', name: 'abc'}}; + expect(getObjKeys(filter(items, {})).length).toBe(2); + + expect(getObjKeys(filter(items, {name: 'a'})).length).toBe(2); + + expect(getObjKeys(filter(items, {name: 'b'})).length).toBe(1); + expect(JSON.stringify(filter(items, {name: 'b'}))).toBe(stringifiedProps(items,['1'])); + }); + + it('should take function as predicate', function() { + var items = {'0':{name: 'a'}, '1':{name: 'abc', done: true}}; + expect(getObjKeys(filter(items, function(i) {return i.done;})).length).toBe(1); + }); + + it('should take object as predicate', function() { + var items = {'0':{first: 'misko', last: 'hevery'},'1':{first: 'adam', last: 'abrons'}}; + + expect(getObjKeys(filter(items, {first:'', last:''})).length).toBe(2); + expect(getObjKeys(filter(items, {first:'', last:'hevery'})).length).toBe(1); + expect(getObjKeys(filter(items, {first:'adam', last:'hevery'})).length).toBe(0); + expect(getObjKeys(filter(items, {first:'misko', last:'hevery'})).length).toBe(1); + expect(JSON.stringify(filter(items, {first:'misko', last:'hevery'}))) + .toEqual(stringifiedProps(items,['0'])); + }); + + it('should support predicate object with dots in the name', function() { + var items = {'0':{'first.name': 'misko', 'last.name': 'hevery'}, + '1':{'first.name': 'adam', 'last.name': 'abrons'}}; + + expect(getObjKeys(filter(items, {'first.name':'', 'last.name':''})).length).toBe(2); + expect(JSON.stringify(filter(items, {'first.name':'misko', 'last.name':''}))) + .toEqual(stringifiedProps(items,['0'])); + }); + + it('should support deep predicate objects', function() { + var items = {'0':{person: {name: 'John'}}, + '1':{person: {name: 'Rita'}}, + '2':{person: {name: 'Billy'}}, + '3':{person: {name: 'Joan'}}}; + expect(getObjKeys(filter(items, {person: {name: 'Jo'}})).length).toBe(2); + expect(JSON.stringify(filter(items, {person: {name: 'Jo'}}))) + .toEqual(stringifiedProps(items,['0','3'])); + }); + + it('should match any properties for given "$" property', function() { + var items = {'0':{first: 'tom', last: 'hevery'}, + '1':{first: 'adam', last: 'hevery', alias: 'tom', done: false}, + '2':{first: 'john', last: 'clark', middle: 'tommy'}}; + expect(getObjKeys(filter(items, {$: 'tom'})).length).toBe(3); + expect(getObjKeys(filter(items, {$: 'a'})).length).toBe(2); + expect(getObjKeys(filter(items, {$: false})).length).toBe(1); + expect(getObjKeys(filter(items, {$: 10})).length).toBe(0); + expect(JSON.stringify(filter(items, {$: 'hevery'}))) + .toEqual(stringifiedProps(items,['0','1'])); + }); + + it('should support boolean properties', function() { + var items = {'0':{name: 'tom', current: true}, + '1':{name: 'demi', current: false}, + '2':{name: 'sofia'}}; + + expect(getObjKeys(filter(items, {current:true})).length).toBe(1); + expect(JSON.stringify(filter(items, {current:true}))).toBe(stringifiedProps(items,['0'])); + expect(getObjKeys(filter(items, {current:false})).length).toBe(1); + expect(JSON.stringify(filter(items, {current:false}))).toBe(stringifiedProps(items,['1'])); + }); + + it('should support negation operator', function() { + var items = {'0':'misko', '1':'adam'}; + + expect(getObjKeys(filter(items, '!isk')).length).toBe(1); + expect(JSON.stringify(filter(items, '!isk'))).toEqual(stringifiedProps(items,['1'])); + }); + + describe('should support comparator', function() { + + it('as equality when true', function() { + var items = {'0':'misko', '1':'adam', '2':'adamson'}; + var expr = 'adam'; + expect(JSON.stringify(filter(items, expr, true))).toEqual(stringifiedProps(items,['1'])); + expect(JSON.stringify(filter(items, expr, false))) + .toEqual(stringifiedProps(items,['1','2'])); + + var items = { + '0':{key: 'value1', nonkey: 1}, + '1':{key: 'value2', nonkey: 2}, + '2':{key: 'value12', nonkey: 3}, + '3':{key: 'value1', nonkey: 4}, + '4':{key: 'Value1', nonkey: 5} + }; + var expr = {key: 'value1'}; + expect(JSON.stringify(filter(items, expr, true))) + .toEqual(stringifiedProps(items,['0','3'])); + + var items = { + '0':{key: 1, nonkey: 1}, + '1':{key: 2, nonkey: 2}, + '2':{key: 12, nonkey: 3}, + '3':{key: 1, nonkey:4} + }; + var expr = { key: 1 }; + expect(JSON.stringify(filter(items, expr, true))) + .toEqual(stringifiedProps(items,['0','3'])); + + var expr = 12; + expect(JSON.stringify(filter(items, expr, true))).toEqual(stringifiedProps(items,['2'])); + }); + + it('and use the function given to compare values', function() { + var items = { + '0':{key: 1, nonkey: 1}, + '1':{key: 2, nonkey: 2}, + '2':{key: 12, nonkey: 3}, + '3':{key: 1, nonkey:14} + }; + var expr = {key: 10}; + var comparator = function (obj,value) { + return obj > value; + }; + expect(JSON.stringify(filter(items, expr, comparator))) + .toEqual(stringifiedProps(items,['2'])); + + expr = 10; + expect(JSON.stringify(filter(items, expr, comparator))) + .toEqual(stringifiedProps(items,['2','3'])); + + }); + + }); }); From 1d7c261aa73d0861bbd47e0a493c97550303b34b Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Sat, 1 Mar 2014 09:51:19 -0800 Subject: [PATCH 002/102] docs(changelog): release notes for 1.2.14 feisty-cryokinesis --- CHANGELOG.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c85115a801..6b795f33b9ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,77 @@ + +# 1.2.14 feisty-cryokinesis (2014-03-01) + + +## Bug Fixes + +- **$animate:** + - delegate down to addClass/removeClass if setClass is not found + ([18c41af0](https://github.com/angular/angular.js/commit/18c41af065006a804a3d38eecca7ae184103ece9), + [#6463](https://github.com/angular/angular.js/issues/6463)) + - ensure all comment nodes are removed during a leave animation + ([f4f1f43d](https://github.com/angular/angular.js/commit/f4f1f43d5140385bbf070510975f72b65196e08a), + [#6403](https://github.com/angular/angular.js/issues/6403)) + - only block keyframes if a stagger is set to occur + ([e71e7b6c](https://github.com/angular/angular.js/commit/e71e7b6cae57f25c5837dda98551c8e0a5cb720d), + [#4225](https://github.com/angular/angular.js/issues/4225)) + - ensure that animateable directives cancel expired leave animations + ([e9881991](https://github.com/angular/angular.js/commit/e9881991ca0a5019d3a4215477738ed247898ba0), + [#5886](https://github.com/angular/angular.js/issues/5886)) + - ensure all animated elements are taken care of during the closing timeout + ([99720fb5](https://github.com/angular/angular.js/commit/99720fb5ab7259af37f708bc4eeda7cbbe790a69), + [#6395](https://github.com/angular/angular.js/issues/6395)) + - fix for TypeError Cannot call method 'querySelectorAll' in cancelChildAnimations + ([c914cd99](https://github.com/angular/angular.js/commit/c914cd99b3aaf932e3c0e2a585eead7b76621f1b), + [#6205](https://github.com/angular/angular.js/issues/6205)) +- **$http:** + - do not add trailing question + ([c8e03e34](https://github.com/angular/angular.js/commit/c8e03e34b27a8449d8e1bfe0e3801d6a67ae2c49), + [#6342](https://github.com/angular/angular.js/issues/6342)) + - send GET requests by default + ([267b2173](https://github.com/angular/angular.js/commit/267b217376ed466e9f260ecfdfa15a8227c103ff), + [#5985](https://github.com/angular/angular.js/issues/5985), [#6401](https://github.com/angular/angular.js/issues/6401)) +- **$parse:** reduce false-positives in isElement tests + ([5fe1f39f](https://github.com/angular/angular.js/commit/5fe1f39f027c6f2c6a530975dd5389d788d3c0eb), + [#4805](https://github.com/angular/angular.js/issues/4805), [#5675](https://github.com/angular/angular.js/issues/5675)) +- **input:** use ValidityState to determine validity + ([c2d447e3](https://github.com/angular/angular.js/commit/c2d447e378dd72d1b955f476bd5bf249625b4dab), + [#4293](https://github.com/angular/angular.js/issues/4293), [#2144](https://github.com/angular/angular.js/issues/2144), [#4857](https://github.com/angular/angular.js/issues/4857), [#5120](https://github.com/angular/angular.js/issues/5120), [#4945](https://github.com/angular/angular.js/issues/4945), [#5500](https://github.com/angular/angular.js/issues/5500), [#5944](https://github.com/angular/angular.js/issues/5944)) +- **isElement:** reduce false-positives in isElement tests + ([75515852](https://github.com/angular/angular.js/commit/75515852ea9742d3d84a0f463c2a2c61ef2b7323)) +- **jqLite:** + - properly toggle multiple classes + ([4e73c80b](https://github.com/angular/angular.js/commit/4e73c80b17bd237a8491782bcf9e19f1889e12ed), + [#4467](https://github.com/angular/angular.js/issues/4467), [#6448](https://github.com/angular/angular.js/issues/6448)) + - make jqLite('