Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/object/object.js
4509 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for manipulating objects/maps/hashes.
9
*/
10
goog.module('goog.object');
11
goog.module.declareLegacyNamespace();
12
13
const utils = goog.require('goog.utils');
14
15
/**
16
* Calls a function for each element in an object/map/hash.
17
* @param {?Object<K,V>} obj The object over which to iterate.
18
* @param {function(this:T,V,?,?Object<K,V>):?} f The function to call for every
19
* element. This function takes 3 arguments (the value, the key and the
20
* object) and the return value is ignored.
21
* @param {T=} opt_obj This is used as the 'this' object within f.
22
* @return {void}
23
* @template T,K,V
24
*/
25
function forEach(obj, f, opt_obj) {
26
for (const key in obj) {
27
f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
28
}
29
}
30
31
/**
32
* Calls a function for each element in an object/map/hash. If that call returns
33
* true, adds the element to a new object.
34
* @param {?Object<K,V>} obj The object over which to iterate.
35
* @param {function(this:T,V,?,?Object<K,V>):boolean} f The function to call for
36
* every element. This function takes 3 arguments (the value, the key and
37
* the object) and should return a boolean. If the return value is true the
38
* element is added to the result object. If it is false the element is not
39
* included.
40
* @param {T=} opt_obj This is used as the 'this' object within f.
41
* @return {!Object<K,V>} a new object in which only elements that passed the
42
* test are present.
43
* @template T,K,V
44
*/
45
function filter(obj, f, opt_obj) {
46
const res = {};
47
for (const key in obj) {
48
if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
49
res[key] = obj[key];
50
}
51
}
52
return res;
53
}
54
55
/**
56
* For every element in an object/map/hash calls a function and inserts the
57
* result into a new object.
58
* @param {?Object<K,V>} obj The object over which to iterate.
59
* @param {function(this:T,V,?,?Object<K,V>):R} f The function to call for every
60
* element. This function takes 3 arguments (the value, the key and the
61
* object) and should return something. The result will be inserted into a
62
* new object.
63
* @param {T=} opt_obj This is used as the 'this' object within f.
64
* @return {!Object<K,R>} a new object with the results from f.
65
* @template T,K,V,R
66
*/
67
function map(obj, f, opt_obj) {
68
const res = {};
69
for (const key in obj) {
70
res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
71
}
72
return res;
73
}
74
75
/**
76
* Calls a function for each element in an object/map/hash. If any
77
* call returns true, returns true (without checking the rest). If
78
* all calls return false, returns false.
79
* @param {?Object<K,V>} obj The object to check.
80
* @param {function(this:T,V,?,?Object<K,V>):boolean} f The function to call for
81
* every element. This function takes 3 arguments (the value, the key and
82
* the object) and should return a boolean.
83
* @param {T=} opt_obj This is used as the 'this' object within f.
84
* @return {boolean} true if any element passes the test.
85
* @template T,K,V
86
*/
87
function some(obj, f, opt_obj) {
88
for (const key in obj) {
89
if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
90
return true;
91
}
92
}
93
return false;
94
}
95
96
/**
97
* Calls a function for each element in an object/map/hash. If
98
* all calls return true, returns true. If any call returns false, returns
99
* false at this point and does not continue to check the remaining elements.
100
* @param {?Object<K,V>} obj The object to check.
101
* @param {?function(this:T,V,?,?Object<K,V>):boolean} f The function to call
102
* for every element. This function takes 3 arguments (the value, the key
103
* and the object) and should return a boolean.
104
* @param {T=} opt_obj This is used as the 'this' object within f.
105
* @return {boolean} false if any element fails the test.
106
* @template T,K,V
107
*/
108
function every(obj, f, opt_obj) {
109
for (const key in obj) {
110
if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
111
return false;
112
}
113
}
114
return true;
115
}
116
117
/**
118
* Returns the number of key-value pairs in the object map.
119
* @param {?Object} obj The object for which to get the number of key-value
120
* pairs.
121
* @return {number} The number of key-value pairs in the object map.
122
*/
123
function getCount(obj) {
124
let rv = 0;
125
for (const key in obj) {
126
rv++;
127
}
128
return rv;
129
}
130
131
/**
132
* Returns one key from the object map, if any exists.
133
* For map literals the returned key will be the first one in most of the
134
* browsers (a know exception is Konqueror).
135
* @param {?Object} obj The object to pick a key from.
136
* @return {string|undefined} The key or undefined if the object is empty.
137
*/
138
function getAnyKey(obj) {
139
for (const key in obj) {
140
return key;
141
}
142
}
143
144
/**
145
* Returns one value from the object map, if any exists.
146
* For map literals the returned value will be the first one in most of the
147
* browsers (a know exception is Konqueror).
148
* @param {?Object<K,V>} obj The object to pick a value from.
149
* @return {V|undefined} The value or undefined if the object is empty.
150
* @template K,V
151
*/
152
function getAnyValue(obj) {
153
for (const key in obj) {
154
return obj[key];
155
}
156
}
157
158
/**
159
* Whether the object/hash/map contains the given object as a value.
160
* An alias for containsValue(obj, val).
161
* @param {?Object<K,V>} obj The object in which to look for val.
162
* @param {V} val The object for which to check.
163
* @return {boolean} true if val is present.
164
* @template K,V
165
*/
166
function contains(obj, val) {
167
return containsValue(obj, val);
168
}
169
170
/**
171
* Returns the values of the object/map/hash.
172
* @param {?Object<K,V>} obj The object from which to get the values.
173
* @return {!Array<V>} The values in the object/map/hash.
174
* @template K,V
175
*/
176
function getValues(obj) {
177
const res = [];
178
let i = 0;
179
for (const key in obj) {
180
res[i++] = obj[key];
181
}
182
return res;
183
}
184
185
/**
186
* Returns the keys of the object/map/hash.
187
* @param {?Object} obj The object from which to get the keys.
188
* @return {!Array<string>} Array of property keys.
189
*/
190
function getKeys(obj) {
191
const res = [];
192
let i = 0;
193
for (const key in obj) {
194
res[i++] = key;
195
}
196
return res;
197
}
198
199
/**
200
* Get a value from an object multiple levels deep. This is useful for
201
* pulling values from deeply nested objects, such as JSON responses.
202
* Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
203
* @param {?Object} obj An object to get the value from. Can be array-like.
204
* @param {...(string|number|!IArrayLike<number|string>)} var_args A number of
205
* keys (as strings, or numbers, for array-like objects). Can also be
206
* specified as a single array of keys.
207
* @return {*} The resulting value. If, at any point, the value for a key in the
208
* current object is null or undefined, returns undefined.
209
*/
210
function getValueByKeys(obj, var_args) {
211
const isArrayLike = utils.isArrayLike(var_args);
212
const keys = isArrayLike ?
213
/** @type {!IArrayLike<number|string>} */ (var_args) :
214
arguments;
215
216
// Start with the 2nd parameter for the variable parameters syntax.
217
for (let i = isArrayLike ? 0 : 1; i < keys.length; i++) {
218
if (obj == null) return undefined;
219
obj = obj[keys[i]];
220
}
221
222
return obj;
223
}
224
225
/**
226
* Whether the object/map/hash contains the given key.
227
* @param {?Object} obj The object in which to look for key.
228
* @param {?} key The key for which to check.
229
* @return {boolean} true If the map contains the key.
230
*/
231
function containsKey(obj, key) {
232
return obj !== null && key in obj;
233
}
234
235
/**
236
* Whether the object/map/hash contains the given value. This is O(n).
237
* @param {?Object<K,V>} obj The object in which to look for val.
238
* @param {V} val The value for which to check.
239
* @return {boolean} true If the map contains the value.
240
* @template K,V
241
*/
242
function containsValue(obj, val) {
243
for (const key in obj) {
244
if (obj[key] == val) {
245
return true;
246
}
247
}
248
return false;
249
}
250
251
/**
252
* Searches an object for an element that satisfies the given condition and
253
* returns its key.
254
* @param {?Object<K,V>} obj The object to search in.
255
* @param {function(this:T,V,string,?Object<K,V>):boolean} f The function to
256
* call for every element. Takes 3 arguments (the value, the key and the
257
* object) and should return a boolean.
258
* @param {T=} thisObj An optional "this" context for the function.
259
* @return {string|undefined} The key of an element for which the function
260
* returns true or undefined if no such element is found.
261
* @template T,K,V
262
*/
263
function findKey(obj, f, thisObj = undefined) {
264
for (const key in obj) {
265
if (f.call(/** @type {?} */ (thisObj), obj[key], key, obj)) {
266
return key;
267
}
268
}
269
return undefined;
270
}
271
272
/**
273
* Searches an object for an element that satisfies the given condition and
274
* returns its value.
275
* @param {?Object<K,V>} obj The object to search in.
276
* @param {function(this:T,V,string,?Object<K,V>):boolean} f The function to
277
* call for every element. Takes 3 arguments (the value, the key and the
278
* object) and should return a boolean.
279
* @param {T=} thisObj An optional "this" context for the function.
280
* @return {V} The value of an element for which the function returns true or
281
* undefined if no such element is found.
282
* @template T,K,V
283
*/
284
function findValue(obj, f, thisObj = undefined) {
285
const key = findKey(obj, f, thisObj);
286
return key && obj[key];
287
}
288
289
/**
290
* Whether the object/map/hash is empty.
291
* @param {?Object} obj The object to test.
292
* @return {boolean} true if obj is empty.
293
*/
294
function isEmpty(obj) {
295
for (const key in obj) {
296
return false;
297
}
298
return true;
299
}
300
301
/**
302
* Removes all key value pairs from the object/map/hash.
303
* @param {?Object} obj The object to clear.
304
* @return {void}
305
*/
306
function clear(obj) {
307
for (const i in obj) {
308
delete obj[i];
309
}
310
}
311
312
/**
313
* Removes a key-value pair based on the key.
314
* @param {?Object} obj The object from which to remove the key.
315
* @param {?} key The key to remove.
316
* @return {boolean} Whether an element was removed.
317
*/
318
function remove(obj, key) {
319
let rv;
320
if (rv = key in /** @type {!Object} */ (obj)) {
321
delete obj[key];
322
}
323
return rv;
324
}
325
326
/**
327
* Adds a key-value pair to the object. Throws an exception if the key is
328
* already in use. Use set if you want to change an existing pair.
329
* @param {?Object<K,V>} obj The object to which to add the key-value pair.
330
* @param {string} key The key to add.
331
* @param {V} val The value to add.
332
* @return {void}
333
* @template K,V
334
*/
335
function add(obj, key, val) {
336
if (obj !== null && key in obj) {
337
throw new Error(`The object already contains the key "${key}"`);
338
}
339
set(obj, key, val);
340
}
341
342
/**
343
* Returns the value for the given key.
344
* @param {?Object<K,V>} obj The object from which to get the value.
345
* @param {string} key The key for which to get the value.
346
* @param {R=} val The value to return if no item is found for the given key
347
* (default is undefined).
348
* @return {V|R|undefined} The value for the given key.
349
* @template K,V,R
350
*/
351
function get(obj, key, val = undefined) {
352
if (obj !== null && key in obj) {
353
return obj[key];
354
}
355
return val;
356
}
357
358
/**
359
* Adds a key-value pair to the object/map/hash.
360
* @param {?Object<K,V>} obj The object to which to add the key-value pair.
361
* @param {string} key The key to add.
362
* @param {V} value The value to add.
363
* @template K,V
364
* @return {void}
365
*/
366
function set(obj, key, value) {
367
obj[key] = value;
368
}
369
370
/**
371
* Adds a key-value pair to the object/map/hash if it doesn't exist yet.
372
* @param {?Object<K,V>} obj The object to which to add the key-value pair.
373
* @param {string} key The key to add.
374
* @param {V} value The value to add if the key wasn't present.
375
* @return {V} The value of the entry at the end of the function.
376
* @template K,V
377
*/
378
function setIfUndefined(obj, key, value) {
379
return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);
380
}
381
382
/**
383
* Sets a key and value to an object if the key is not set. The value will be
384
* the return value of the given function. If the key already exists, the
385
* object will not be changed and the function will not be called (the function
386
* will be lazily evaluated -- only called if necessary).
387
* This function is particularly useful when used with an `Object` which is
388
* acting as a cache.
389
* @param {?Object<K,V>} obj The object to which to add the key-value pair.
390
* @param {string} key The key to add.
391
* @param {function():V} f The value to add if the key wasn't present.
392
* @return {V} The value of the entry at the end of the function.
393
* @template K,V
394
*/
395
function setWithReturnValueIfNotSet(obj, key, f) {
396
if (key in obj) {
397
return obj[key];
398
}
399
400
const val = f();
401
obj[key] = val;
402
return val;
403
}
404
405
/**
406
* Compares two objects for equality using === on the values.
407
* @param {!Object<K,V>} a
408
* @param {!Object<K,V>} b
409
* @return {boolean}
410
* @template K,V
411
*/
412
function equals(a, b) {
413
for (const k in a) {
414
if (!(k in b) || a[k] !== b[k]) {
415
return false;
416
}
417
}
418
for (const k in b) {
419
if (!(k in a)) {
420
return false;
421
}
422
}
423
return true;
424
}
425
426
/**
427
* Returns a shallow clone of the object.
428
* @param {?Object<K,V>} obj Object to clone.
429
* @return {!Object<K,V>} Clone of the input object.
430
* @template K,V
431
*/
432
function clone(obj) {
433
const res = {};
434
for (const key in obj) {
435
res[key] = obj[key];
436
}
437
return res;
438
}
439
440
/**
441
* Clones a value. The input may be an Object, Array, or basic type. Objects and
442
* arrays will be cloned recursively.
443
* WARNINGS:
444
* <code>unsafeClone</code> does not detect reference loops. Objects
445
* that refer to themselves will cause infinite recursion.
446
* <code>unsafeClone</code> is unaware of unique identifiers, and
447
* copies UIDs created by <code>getUid</code> into cloned results.
448
* @param {T} obj The value to clone.
449
* @return {T} A clone of the input value.
450
* @template T
451
*/
452
function unsafeClone(obj) {
453
if (!obj || typeof obj !== 'object') return obj;
454
if (typeof obj.clone === 'function') return obj.clone();
455
if (typeof Map !== 'undefined' && obj instanceof Map) {
456
return new Map(obj);
457
} else if (typeof Set !== 'undefined' && obj instanceof Set) {
458
return new Set(obj);
459
} else if (obj instanceof Date) {
460
return new Date(obj.getTime());
461
}
462
const clone = Array.isArray(obj) ? [] :
463
typeof ArrayBuffer === 'function' &&
464
typeof ArrayBuffer.isView === 'function' && ArrayBuffer.isView(obj) &&
465
!(obj instanceof DataView) ?
466
new obj.constructor(obj.length) :
467
{};
468
for (const key in obj) {
469
clone[key] = unsafeClone(obj[key]);
470
}
471
return clone;
472
}
473
474
/**
475
* Returns a new object in which all the keys and values are interchanged
476
* (keys become values and values become keys). If multiple keys map to the
477
* same value, the chosen transposed value is implementation-dependent.
478
* @param {?Object} obj The object to transpose.
479
* @return {!Object} The transposed object.
480
*/
481
function transpose(obj) {
482
const transposed = {};
483
for (const key in obj) {
484
transposed[obj[key]] = key;
485
}
486
return transposed;
487
}
488
489
/**
490
* The names of the fields that are defined on Object.prototype.
491
* @type {!Array<string>}
492
*/
493
const PROTOTYPE_FIELDS = [
494
'constructor',
495
'hasOwnProperty',
496
'isPrototypeOf',
497
'propertyIsEnumerable',
498
'toLocaleString',
499
'toString',
500
'valueOf',
501
];
502
503
/**
504
* Extends an object with another object.
505
* This operates 'in-place'; it does not create a new Object.
506
* Example:
507
* var o = {};
508
* extend(o, {a: 0, b: 1});
509
* o; // {a: 0, b: 1}
510
* extend(o, {b: 2, c: 3});
511
* o; // {a: 0, b: 2, c: 3}
512
* @param {?Object} target The object to modify. Existing properties will be
513
* overwritten if they are also present in one of the objects in `var_args`.
514
* @param {...(?Object|undefined)} var_args The objects from which values
515
* will be copied.
516
* @return {void}
517
* @deprecated Prefer Object.assign
518
*/
519
function extend(target, var_args) {
520
let key;
521
let source;
522
for (let i = 1; i < arguments.length; i++) {
523
source = arguments[i];
524
for (key in source) {
525
target[key] = source[key];
526
}
527
528
// For IE the for-in-loop does not contain any properties that are not
529
// enumerable on the prototype object (for example isPrototypeOf from
530
// Object.prototype) and it will also not include 'replace' on objects that
531
// extend String and change 'replace' (not that it is common for anyone to
532
// extend anything except Object).
533
534
for (let j = 0; j < PROTOTYPE_FIELDS.length; j++) {
535
key = PROTOTYPE_FIELDS[j];
536
if (Object.prototype.hasOwnProperty.call(source, key)) {
537
target[key] = source[key];
538
}
539
}
540
}
541
}
542
543
/**
544
* Creates a new object built from the key-value pairs provided as arguments.
545
* @param {...*} var_args If only one argument is provided and it is an array
546
* then this is used as the arguments, otherwise even arguments are used as
547
* the property names and odd arguments are used as the property values.
548
* @return {!Object} The new object.
549
* @throws {!Error} If there are uneven number of arguments or there is only one
550
* non array argument.
551
*/
552
function create(var_args) {
553
const argLength = arguments.length;
554
if (argLength == 1 && Array.isArray(arguments[0])) {
555
return create.apply(null, arguments[0]);
556
}
557
558
if (argLength % 2) {
559
throw new Error('Uneven number of arguments');
560
}
561
562
const rv = {};
563
for (let i = 0; i < argLength; i += 2) {
564
rv[arguments[i]] = arguments[i + 1];
565
}
566
return rv;
567
}
568
569
/**
570
* Creates a new object where the property names come from the arguments but
571
* the value is always set to true
572
* @param {...*} var_args If only one argument is provided and it is an array
573
* then this is used as the arguments, otherwise the arguments are used as
574
* the property names.
575
* @return {!Object} The new object.
576
*/
577
function createSet(var_args) {
578
const argLength = arguments.length;
579
if (argLength == 1 && Array.isArray(arguments[0])) {
580
return createSet.apply(null, arguments[0]);
581
}
582
583
const rv = {};
584
for (let i = 0; i < argLength; i++) {
585
rv[arguments[i]] = true;
586
}
587
return rv;
588
}
589
590
/**
591
* Creates an immutable view of the underlying object, if the browser
592
* supports immutable objects.
593
* In default mode, writes to this view will fail silently. In strict mode,
594
* they will throw an error.
595
* @param {!Object<K,V>} obj An object.
596
* @return {!Object<K,V>} An immutable view of that object, or the original
597
* object if this browser does not support immutables.
598
* @template K,V
599
*/
600
function createImmutableView(obj) {
601
let result = obj;
602
if (Object.isFrozen && !Object.isFrozen(obj)) {
603
result = Object.create(obj);
604
Object.freeze(result);
605
}
606
return result;
607
}
608
609
/**
610
* @param {!Object} obj An object.
611
* @return {boolean} Whether this is an immutable view of the object.
612
*/
613
function isImmutableView(obj) {
614
return !!Object.isFrozen && Object.isFrozen(obj);
615
}
616
617
/**
618
* Get all properties names on a given Object regardless of enumerability.
619
* <p> If the browser does not support `Object.getOwnPropertyNames` nor
620
* `Object.getPrototypeOf` then this is equivalent to using
621
* `getKeys`
622
* @param {?Object} obj The object to get the properties of.
623
* @param {boolean=} includeObjectPrototype Whether properties defined on
624
* `Object.prototype` should be included in the result.
625
* @param {boolean=} includeFunctionPrototype Whether properties defined on
626
* `Function.prototype` should be included in the result.
627
* @return {!Array<string>}
628
* @public
629
*/
630
function getAllPropertyNames(
631
obj, includeObjectPrototype = undefined,
632
includeFunctionPrototype = undefined) {
633
if (!obj) {
634
return [];
635
}
636
637
// Naively use a for..in loop to get the property names if the browser doesn't
638
// support any other APIs for getting it.
639
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
640
return getKeys(obj);
641
}
642
643
const visitedSet = {};
644
645
// Traverse the prototype chain and add all properties to the visited set.
646
let proto = obj;
647
while (proto && (proto !== Object.prototype || !!includeObjectPrototype) &&
648
(proto !== Function.prototype || !!includeFunctionPrototype)) {
649
const names = Object.getOwnPropertyNames(proto);
650
for (let i = 0; i < names.length; i++) {
651
visitedSet[names[i]] = true;
652
}
653
proto = Object.getPrototypeOf(proto);
654
}
655
656
return getKeys(visitedSet);
657
}
658
659
/**
660
* Given a ES5 or ES6 class reference, return its super class / super
661
* constructor.
662
* This should be used in rare cases where you need to walk up the inheritance
663
* tree (this is generally a bad idea). But this work with ES5 and ES6 classes,
664
* unlike relying on the superClass_ property.
665
* Note: To start walking up the hierarchy from an instance call this with its
666
* `constructor` property; e.g. `getSuperClass(instance.constructor)`.
667
* @param {function(new: ?)} constructor
668
* @return {?Object}
669
*/
670
function getSuperClass(constructor) {
671
const proto = Object.getPrototypeOf(constructor.prototype);
672
return proto && proto.constructor;
673
}
674
675
exports = {
676
add,
677
clear,
678
clone,
679
contains,
680
containsKey,
681
containsValue,
682
create,
683
createImmutableView,
684
createSet,
685
equals,
686
every,
687
extend,
688
filter,
689
findKey,
690
findValue,
691
forEach,
692
get,
693
getAllPropertyNames,
694
getAnyKey,
695
getAnyValue,
696
getCount,
697
getKeys,
698
getSuperClass,
699
getValueByKeys,
700
getValues,
701
isEmpty,
702
isImmutableView,
703
map,
704
remove,
705
set,
706
setIfUndefined,
707
setWithReturnValueIfNotSet,
708
some,
709
transpose,
710
unsafeClone,
711
};
712
713