@@ -58,6 +58,21 @@ export const SPECIAL_TOKENS = {
58
58
'NaN' : NAN
59
59
}
60
60
61
+ export function specialTokenToString ( value ) {
62
+ if ( value === null ) {
63
+ return 'null'
64
+ } else if ( value === UNDEFINED ) {
65
+ return 'undefined'
66
+ } else if ( value === NAN ) {
67
+ return 'NaN'
68
+ } else if ( value === INFINITY ) {
69
+ return 'Infinity'
70
+ } else if ( value === NEGATIVE_INFINITY ) {
71
+ return '-Infinity'
72
+ }
73
+ return false
74
+ }
75
+
61
76
/**
62
77
* Needed to prevent stack overflow
63
78
* while replacing complex objects
@@ -313,45 +328,116 @@ function isPrimitive (data) {
313
328
)
314
329
}
315
330
331
+ /**
332
+ * Searches a key or value in the object, with a maximum deepness
333
+ * @param {* } obj Search target
334
+ * @param {string } searchTerm Search string
335
+ * @returns {boolean } Search match
336
+ */
316
337
export function searchDeepInObject ( obj , searchTerm ) {
317
- var match = false
338
+ const seen = new Map ( )
339
+ const result = internalSearchObject ( obj , searchTerm . toLowerCase ( ) , seen , 0 )
340
+ seen . clear ( )
341
+ return result
342
+ }
343
+
344
+ const SEARCH_MAX_DEPTH = 10
345
+
346
+ /**
347
+ * Executes a search on each field of the provided object
348
+ * @param {* } obj Search target
349
+ * @param {string } searchTerm Search string
350
+ * @param {Map<any,boolean> } seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
351
+ * @param {number } depth Deep search depth level, which is capped to prevent performance issues
352
+ * @returns {boolean } Search match
353
+ */
354
+ function internalSearchObject ( obj , searchTerm , seen , depth ) {
355
+ if ( depth > SEARCH_MAX_DEPTH ) {
356
+ return false
357
+ }
358
+ let match = false
318
359
const keys = Object . keys ( obj )
360
+ let key , value
319
361
for ( let i = 0 ; i < keys . length ; i ++ ) {
320
- const key = keys [ i ]
321
- const value = obj [ key ]
322
- if ( compare ( key , searchTerm ) || compare ( value , searchTerm ) ) {
323
- match = true
362
+ key = keys [ i ]
363
+ value = obj [ key ]
364
+ match = interalSearchCheck ( searchTerm , key , value , seen , depth + 1 )
365
+ if ( match ) {
324
366
break
325
367
}
326
- if ( isPlainObject ( value ) ) {
327
- match = searchDeepInObject ( value , searchTerm )
328
- if ( match ) {
329
- break
330
- }
331
- }
332
368
}
333
369
return match
334
370
}
335
371
336
- function compare ( mixedValue , stringValue ) {
337
- if ( Array . isArray ( mixedValue ) && searchInArray ( mixedValue , stringValue . toLowerCase ( ) ) ) {
338
- return true
372
+ /**
373
+ * Executes a search on each value of the provided array
374
+ * @param {* } array Search target
375
+ * @param {string } searchTerm Search string
376
+ * @param {Map<any,boolean> } seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
377
+ * @param {number } depth Deep search depth level, which is capped to prevent performance issues
378
+ * @returns {boolean } Search match
379
+ */
380
+ function internalSearchArray ( array , searchTerm , seen , depth ) {
381
+ if ( depth > SEARCH_MAX_DEPTH ) {
382
+ return false
339
383
}
340
- if ( ( '' + mixedValue ) . toLowerCase ( ) . indexOf ( stringValue . toLowerCase ( ) ) !== - 1 ) {
341
- return true
384
+ let match = false
385
+ let value
386
+ for ( let i = 0 ; i < array . length ; i ++ ) {
387
+ value = array [ i ]
388
+ match = interalSearchCheck ( searchTerm , null , value , seen , depth + 1 )
389
+ if ( match ) {
390
+ break
391
+ }
342
392
}
343
- return false
393
+ return match
344
394
}
345
395
346
- function searchInArray ( arr , searchTerm ) {
347
- let found = false
348
- for ( let i = 0 ; i < arr . length ; i ++ ) {
349
- if ( ( '' + arr [ i ] ) . toLowerCase ( ) . indexOf ( searchTerm ) !== - 1 ) {
350
- found = true
351
- break
352
- }
396
+ /**
397
+ * Checks if the provided field matches the search terms
398
+ * @param {string } searchTerm Search string
399
+ * @param {string } key Field key (null if from array)
400
+ * @param {* } value Field value
401
+ * @param {Map<any,boolean> } seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
402
+ * @param {number } depth Deep search depth level, which is capped to prevent performance issues
403
+ * @returns {boolean } Search match
404
+ */
405
+ function interalSearchCheck ( searchTerm , key , value , seen , depth ) {
406
+ let match = false
407
+ let result
408
+ if ( key === '_custom' ) {
409
+ key = value . display
410
+ value = value . value
353
411
}
354
- return found
412
+ ( result = specialTokenToString ( value ) ) && ( value = result )
413
+ if ( key && compare ( key , searchTerm ) ) {
414
+ match = true
415
+ seen . set ( value , true )
416
+ } else if ( seen . has ( value ) ) {
417
+ match = seen . get ( value )
418
+ } else if ( Array . isArray ( value ) ) {
419
+ seen . set ( value , null )
420
+ match = internalSearchArray ( value , searchTerm , seen , depth )
421
+ seen . set ( value , match )
422
+ } else if ( isPlainObject ( value ) ) {
423
+ seen . set ( value , null )
424
+ match = internalSearchObject ( value , searchTerm , seen , depth )
425
+ seen . set ( value , match )
426
+ } else if ( compare ( value , searchTerm ) ) {
427
+ match = true
428
+ seen . set ( value , true )
429
+ }
430
+ return match
431
+ }
432
+
433
+ /**
434
+ * Compares two values
435
+ * @param {* } value Mixed type value that will be cast to string
436
+ * @param {string } searchTerm Search string
437
+ * @returns {boolean } Search match
438
+ */
439
+ function compare ( value , searchTerm ) {
440
+ return ( '' + value ) . toLowerCase ( ) . indexOf ( searchTerm ) !== - 1
355
441
}
356
442
357
443
export function sortByKey ( state ) {
0 commit comments