@@ -119,104 +119,85 @@ function filterFilter() {
119119 return function ( array , expression , comparator ) {
120120 if ( ! isArray ( array ) ) return array ;
121121
122- var comparatorType = typeof ( comparator ) ,
123- predicates = [ ] ;
124-
125- predicates . check = function ( value , index ) {
126- for ( var j = 0 ; j < predicates . length ; j ++ ) {
127- if ( ! predicates [ j ] ( value , index ) ) {
128- return false ;
129- }
130- }
131- return true ;
132- } ;
133-
134- if ( comparatorType !== 'function' ) {
135- if ( comparatorType === 'boolean' && comparator ) {
136- comparator = function ( obj , text ) {
137- return angular . equals ( obj , text ) ;
138- } ;
139- } else {
140- comparator = function ( obj , text ) {
141- if ( obj && text && typeof obj === 'object' && typeof text === 'object' ) {
142- for ( var objKey in obj ) {
143- if ( objKey . charAt ( 0 ) !== '$' && hasOwnProperty . call ( obj , objKey ) &&
144- comparator ( obj [ objKey ] , text [ objKey ] ) ) {
145- return true ;
146- }
147- }
148- return false ;
149- }
150- text = ( '' + text ) . toLowerCase ( ) ;
151- return ( '' + obj ) . toLowerCase ( ) . indexOf ( text ) > - 1 ;
152- } ;
153- }
154- }
122+ var predicateFn ;
155123
156- var search = function ( obj , text ) {
157- if ( typeof text === 'string' && text . charAt ( 0 ) === '!' ) {
158- return ! search ( obj , text . substr ( 1 ) ) ;
159- }
160- switch ( typeof obj ) {
161- case 'boolean' :
162- case 'number' :
163- case 'string' :
164- return comparator ( obj , text ) ;
165- case 'object' :
166- switch ( typeof text ) {
167- case 'object' :
168- return comparator ( obj , text ) ;
169- default :
170- for ( var objKey in obj ) {
171- if ( objKey . charAt ( 0 ) !== '$' && search ( obj [ objKey ] , text ) ) {
172- return true ;
173- }
174- }
175- break ;
176- }
177- return false ;
178- case 'array' :
179- for ( var i = 0 ; i < obj . length ; i ++ ) {
180- if ( search ( obj [ i ] , text ) ) {
181- return true ;
182- }
183- }
184- return false ;
185- default :
186- return false ;
187- }
188- } ;
189124 switch ( typeof expression ) {
125+ case 'object' :
126+ // Replace `{$: 'xyz'}` with `'xyz'` and fall through
127+ var keys = Object . keys ( expression ) ;
128+ if ( ( keys . length === 1 ) && ( keys [ 0 ] === '$' ) ) expression = expression . $ ;
129+ // jshint -W086
190130 case 'boolean' :
191131 case 'number' :
192132 case 'string' :
193- // Set up expression object and fall through
194- expression = { $ :expression } ;
195- // jshint -W086
196- case 'object' :
197133 // jshint +W086
198- for ( var key in expression ) {
199- ( function ( path ) {
200- if ( typeof expression [ path ] === 'undefined' ) return ;
201- predicates . push ( function ( value ) {
202- return search ( path == '$' ? value : ( value && value [ path ] ) , expression [ path ] ) ;
203- } ) ;
204- } ) ( key ) ;
205- }
134+ predicateFn = createPredicateFn ( expression , comparator ) ;
206135 break ;
207136 case 'function' :
208- predicates . push ( expression ) ;
137+ predicateFn = expression ;
209138 break ;
210139 default :
211140 return array ;
212141 }
213- var filtered = [ ] ;
214- for ( var j = 0 ; j < array . length ; j ++ ) {
215- var value = array [ j ] ;
216- if ( predicates . check ( value , j ) ) {
217- filtered . push ( value ) ;
218- }
219- }
220- return filtered ;
142+
143+ return array . filter ( predicateFn ) ;
221144 } ;
222145}
146+
147+ // Helper functions for `filterFilter`
148+ function createPredicateFn ( expression , comparator ) {
149+ var predicateFn ;
150+
151+ if ( comparator === true ) {
152+ comparator = equals ;
153+ } else if ( ! isFunction ( comparator ) ) {
154+ comparator = function ( actual , expected ) {
155+ actual = ( '' + actual ) . toLowerCase ( ) ;
156+ expected = ( '' + expected ) . toLowerCase ( ) ;
157+ return actual . indexOf ( expected ) !== - 1 ;
158+ } ;
159+ }
160+
161+ predicateFn = function ( item ) {
162+ return deepCompare ( item , expression , comparator ) ;
163+ } ;
164+
165+ return predicateFn ;
166+ }
167+
168+ function deepCompare ( actual , expected , comparator ) {
169+ var actualType = typeof actual ;
170+ var expectedType = typeof expected ;
171+
172+ if ( expectedType === 'function' ) {
173+ return expected ( actual ) ;
174+ } else if ( ( expectedType === 'string' ) && ( expected . charAt ( 0 ) === '!' ) ) {
175+ return ! deepCompare ( actual , expected . substring ( 1 ) , comparator ) ;
176+ } else if ( actualType === 'array' ) {
177+ return actual . some ( function ( item ) {
178+ return deepCompare ( item , expected , comparator ) ;
179+ } ) ;
180+ }
181+
182+ switch ( actualType ) {
183+ case 'object' :
184+ if ( expectedType === 'object' ) {
185+ return Object . keys ( expected ) . every ( function ( key ) {
186+ var actualVal = ( key === '$' ) ? actual : actual [ key ] ;
187+ var expectedVal = expected [ key ] ;
188+ return deepCompare ( actualVal , expectedVal , comparator ) ;
189+ } ) ;
190+ } else {
191+ return Object . keys ( actual ) . some ( function ( key ) {
192+ return ( key . charAt ( 0 ) !== '$' ) && deepCompare ( actual [ key ] , expected , comparator ) ;
193+ } ) ;
194+ }
195+ break ;
196+ default :
197+ if ( expectedType === 'object' ) {
198+ return true ;
199+ }
200+
201+ return comparator ( actual , expected ) ;
202+ }
203+ }
0 commit comments