1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 var createClassWrapper = requireNative('utils').createClassWrapper; 6 var nativeDeepCopy = requireNative('utils').deepCopy; 7 var schemaRegistry = requireNative('schema_registry'); 8 var CHECK = requireNative('logging').CHECK; 9 var WARNING = requireNative('logging').WARNING; 10 11 /** 12 * An object forEach. Calls |f| with each (key, value) pair of |obj|, using 13 * |self| as the target. 14 * @param {Object} obj The object to iterate over. 15 * @param {function} f The function to call in each iteration. 16 * @param {Object} self The object to use as |this| in each function call. 17 */ 18 function forEach(obj, f, self) { 19 for (var key in obj) { 20 if ($Object.hasOwnProperty(obj, key)) 21 $Function.call(f, self, key, obj[key]); 22 } 23 } 24 25 /** 26 * Assuming |array_of_dictionaries| is structured like this: 27 * [{id: 1, ... }, {id: 2, ...}, ...], you can use 28 * lookup(array_of_dictionaries, 'id', 2) to get the dictionary with id == 2. 29 * @param {Array.<Object.<string, ?>>} array_of_dictionaries 30 * @param {string} field 31 * @param {?} value 32 */ 33 function lookup(array_of_dictionaries, field, value) { 34 var filter = function (dict) {return dict[field] == value;}; 35 var matches = array_of_dictionaries.filter(filter); 36 if (matches.length == 0) { 37 return undefined; 38 } else if (matches.length == 1) { 39 return matches[0] 40 } else { 41 throw new Error("Failed lookup of field '" + field + "' with value '" + 42 value + "'"); 43 } 44 } 45 46 function loadTypeSchema(typeName, defaultSchema) { 47 var parts = $String.split(typeName, '.'); 48 if (parts.length == 1) { 49 if (defaultSchema == null) { 50 WARNING('Trying to reference "' + typeName + '" ' + 51 'with neither namespace nor default schema.'); 52 return null; 53 } 54 var types = defaultSchema.types; 55 } else { 56 var schemaName = $Array.join($Array.slice(parts, 0, parts.length - 1), '.'); 57 var types = schemaRegistry.GetSchema(schemaName).types; 58 } 59 for (var i = 0; i < types.length; ++i) { 60 if (types[i].id == typeName) 61 return types[i]; 62 } 63 return null; 64 } 65 66 /** 67 * Takes a private class implementation |cls| and exposes a subset of its 68 * methods |functions| and properties |properties| and |readonly| in a public 69 * wrapper class that it returns. Within bindings code, you can access the 70 * implementation from an instance of the wrapper class using 71 * privates(instance).impl, and from the implementation class you can access 72 * the wrapper using this.wrapper (or implInstance.wrapper if you have another 73 * instance of the implementation class). 74 * @param {string} name The name of the exposed wrapper class. 75 * @param {Object} cls The class implementation. 76 * @param {{superclass: ?Function, 77 * functions: ?Array.<string>, 78 * properties: ?Array.<string>, 79 * readonly: ?Array.<string>}} exposed The names of properties on the 80 * implementation class to be exposed. |superclass| represents the 81 * constructor of the class to be used as the superclass of the exposed 82 * class; |functions| represents the names of functions which should be 83 * delegated to the implementation; |properties| are gettable/settable 84 * properties and |readonly| are read-only properties. 85 */ 86 function expose(name, cls, exposed) { 87 var publicClass = createClassWrapper(name, cls, exposed.superclass); 88 89 if ('functions' in exposed) { 90 $Array.forEach(exposed.functions, function(func) { 91 publicClass.prototype[func] = function() { 92 var impl = privates(this).impl; 93 return $Function.apply(impl[func], impl, arguments); 94 }; 95 }); 96 } 97 98 if ('properties' in exposed) { 99 $Array.forEach(exposed.properties, function(prop) { 100 $Object.defineProperty(publicClass.prototype, prop, { 101 enumerable: true, 102 get: function() { 103 return privates(this).impl[prop]; 104 }, 105 set: function(value) { 106 var impl = privates(this).impl; 107 delete impl[prop]; 108 impl[prop] = value; 109 } 110 }); 111 }); 112 } 113 114 if ('readonly' in exposed) { 115 $Array.forEach(exposed.readonly, function(readonly) { 116 $Object.defineProperty(publicClass.prototype, readonly, { 117 enumerable: true, 118 get: function() { 119 return privates(this).impl[readonly]; 120 }, 121 }); 122 }); 123 } 124 125 return publicClass; 126 } 127 128 /** 129 * Returns a deep copy of |value|. The copy will have no references to nested 130 * values of |value|. 131 */ 132 function deepCopy(value) { 133 return nativeDeepCopy(value); 134 } 135 136 exports.forEach = forEach; 137 exports.loadTypeSchema = loadTypeSchema; 138 exports.lookup = lookup; 139 exports.expose = expose; 140 exports.deepCopy = deepCopy; 141