1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/trace", 5 "firebug/lib/string", 6 "firebug/lib/deprecated", 7 "firebug/lib/function", 8 ], 9 function(FBTrace, Str, Deprecated, Func) { 10 "use strict"; 11 // xxxFlorent: TODO add that specific tag in jsdoc... 12 13 // ********************************************************************************************* // 14 // Constants 15 16 var Cu = Components.utils; 17 18 /** 19 * @name Obj 20 * @lib Utility for objects 21 */ 22 var Obj = {}; 23 24 // ********************************************************************************************* // 25 26 // xxxFlorent: TODO: [REST] 27 28 Obj.bind = Deprecated.deprecated("Use either (depending on the case): Func.bindRight, "+ 29 "Func.bindFixed or Function.prototype.bind instead", Func.bindRight); 30 31 Obj.bindFixed = Deprecated.deprecated("Please, use Func.bindFixed instead now", Func.bindFixed); 32 33 // xxxFlorent: [ES6-REST] 34 /** 35 * Clones and extend the object (first parameters) with other objects (following parameters). 36 * 37 * @param {Object} parentObject The object to clone and extend 38 * @param {Object} ...extensions the extensions 39 * 40 * @example 41 * var parentObj = {foo: "foo" }; 42 * var newObj = Obj.extend(parentObj, {bar: "bar"}); // => {foo: "foo", bar: "bar"} 43 */ 44 Obj.extend = function(parentObject/*, ...extensions*/) 45 { 46 if (arguments.length < 2) 47 { 48 FBTrace.sysout("object.extend; ERROR", arguments); 49 throw new Error("Obj.extend on undefined object"); 50 } 51 52 var extensions = Array.prototype.slice.call(arguments, 1); 53 var newOb = Object.create(parentObject); 54 55 for (var i = 0, len = arguments.length; i < len; ++i) 56 { 57 for (var prop in arguments[i]) 58 newOb[prop] = arguments[i][prop]; 59 } 60 61 return newOb; 62 }; 63 64 /** 65 * Creates a new instance inheriting from a parent "class". 66 * That class is then extended with child properties. 67 * 68 * @param {Object} protototypeParent The parent "class" prototype 69 * @param {Object} childProperties The properties extending the new object 70 * 71 * @return {Object} the new object 72 */ 73 Obj.descend = function(prototypeParent, childProperties) 74 { 75 function protoSetter() {}; 76 protoSetter.prototype = prototypeParent; 77 var newOb = new protoSetter(); 78 for (var n in childProperties) 79 newOb[n] = childProperties[n]; 80 return newOb; 81 }; 82 83 // ************************************************************************************************ 84 85 /** 86 * Returns true if the passed object has any properties, otherwise returns false. 87 * 88 * @param {Object} ob Inspected object 89 * @param {Object} nonEnumProps If set to true, check also non-enumerable properties (optional) 90 * @param {Object} ownPropsOnly If set to true, only check own properties not inherited (optional) 91 */ 92 Obj.hasProperties = function(ob, nonEnumProps, ownPropsOnly) 93 { 94 try 95 { 96 if (!ob) 97 return false; 98 99 var obString = Str.safeToString(ob); 100 if (obString === "[xpconnect wrapped native prototype]") 101 { 102 return true; 103 } 104 105 // The default case (both options false) is relatively simple. 106 // Just use for..in loop. 107 if (!nonEnumProps && !ownPropsOnly) 108 { 109 for (var name in ob) 110 { 111 // Try to access the property before declaring existing properties. 112 // It's because some properties can't be read see: 113 // issue 3843, https://bugzilla.mozilla.org/show_bug.cgi?id=455013 114 var value = ob[name]; 115 return true; 116 } 117 return false; 118 } 119 120 var type = typeof(ob); 121 if (type == "string" && ob.length) 122 return true; 123 124 if (type === "number" || type === "boolean" || type === "undefined" || ob === null) 125 return false; 126 127 if (nonEnumProps) 128 props = Object.getOwnPropertyNames(ob); 129 else 130 props = Object.keys(ob); 131 132 if (props.length) 133 { 134 // Try to access the property before declaring existing properties. 135 // It's because some properties can't be read see: 136 // issue 3843, https://bugzilla.mozilla.org/show_bug.cgi?id=455013 137 var value = ob[props[0]]; 138 return true; 139 } 140 141 // Not interested in inherited properties, bail out. 142 if (ownPropsOnly) 143 return false; 144 145 // Climb prototype chain. 146 var inheritedProps = []; 147 var parent = Object.getPrototypeOf(ob); 148 if (parent) 149 return this.hasProperties(parent, nonEnumProps, ownPropsOnly); 150 } 151 catch (exc) 152 { 153 // Primitive (non string) objects will throw an exception when passed into 154 // Object.keys or Object.getOwnPropertyNames APIs. 155 // There are also many "security error" exceptions I guess none of which are really 156 // necessary to display in the FBTrace console, so, remove the tracing for now. 157 // if (FBTrace.DBG_ERRORS) 158 // FBTrace.sysout("lib.hasProperties(" + Str.safeToString(ob) + ") ERROR " + exc, exc); 159 160 // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=648560 161 if (ob.wrappedJSObject) 162 return true; 163 } 164 165 return false; 166 }; 167 168 /** 169 * Returns the prototype of an object, or null if the function fails to do so. 170 * 171 * @deprecated use <code>myObj.prototype</code> instead (plus clever checks before) 172 */ 173 Obj.getPrototype = Deprecated.deprecated("use myObj.prototype instead (+ clever checks before)", 174 function(ob) 175 { 176 try 177 { 178 return ob.prototype; 179 } catch (exc) {} 180 return null; 181 }); 182 183 /** 184 * Returns a unique ID (random integer between 0 and 65536) 185 * 186 * @return {Number} the random number 187 */ 188 Obj.getUniqueId = function() 189 { 190 return this.getRandomInt(0,65536); 191 } 192 193 /** 194 * Returns a random integer between min and max 195 * 196 * @param {Number} min The minimum 197 * @param {Number} max The maximum 198 * 199 * @return {Number} the random number 200 */ 201 Obj.getRandomInt = function(min, max) 202 { 203 return Math.floor(Math.random() * (max - min + 1) + min); 204 } 205 206 // xxxFlorent: not sure it is true... But I couldn't find any case where `instanceof` 207 // didn't work correctly cross-window 208 /** 209 * Cross Window instanceof 210 * 211 * @param {Object} obj The object to test 212 * @param {*} type The type (local to this window) 213 * 214 * @returns {Boolean} true if the test succeeded, false otherwise 215 * 216 * @deprecated use <code>instanceof</code> instead 217 */ 218 Obj.XW_instanceof = Deprecated.deprecated("use `instanceof` instead", function(obj, type) 219 { 220 if (obj instanceof type) 221 return true; // within-window test 222 223 if (!type) 224 return false; 225 226 if (!obj) 227 return (type == "undefined"); 228 229 // compare strings: obj constructor.name to type.name. 230 // This is not perfect, we should compare type.prototype to object.__proto__, 231 // but mostly code does not change the constructor object. 232 do 233 { 234 // then the function that constructed us is the argument 235 if (obj.constructor && obj.constructor.name == type.name) 236 return true; 237 } 238 while(obj = obj.__proto__); // walk the prototype chain. 239 240 return false; 241 242 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Property_Inheritance_Revisited 243 // /Determining_Instance_Relationships 244 }); 245 246 /** 247 * Tells if the given property of the provided object is a non-native getter or not. 248 * This method depends on PropertyPanel.jsm module available in Firefox 5+ 249 * isNonNativeGetter has been introduced in Firefox 7 250 * The method has been moved to WebConsoleUtils.jsm in Fx 18 251 * 252 * @param {object} obj The object that contains the property. 253 * @param {string} propName The property you want to check if it is a getter or not. 254 * @return {boolean} True if the given property is a getter, false otherwise. 255 */ 256 Obj.isNonNativeGetter = function(obj, propName) 257 { 258 try 259 { 260 var scope = {}; 261 Cu.import("resource://gre/modules/devtools/WebConsoleUtils.jsm", scope); 262 263 if (scope.WebConsoleUtils.isNonNativeGetter) 264 { 265 Obj.isNonNativeGetter = function(obj, propName) 266 { 267 return scope.WebConsoleUtils.isNonNativeGetter(obj, propName); 268 } 269 270 return Obj.isNonNativeGetter(obj, propName); 271 } 272 } 273 catch (err) 274 { 275 if (FBTrace.DBG_ERRORS) 276 FBTrace.sysout("Obj.isNonNativeGetter; EXCEPTION " + err, err); 277 } 278 279 // OK, the method isn't available let's use an empty implementation 280 Obj.isNonNativeGetter = function() 281 { 282 if (FBTrace.DBG_ERRORS) 283 FBTrace.sysout("Obj.isNonNativeGetter; ERROR built-in method not found!"); 284 return true; 285 } 286 287 return true; 288 } 289 290 // ********************************************************************************************* // 291 292 return Obj; 293 294 // ********************************************************************************************* // 295 }); 296