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