addon-sdk/source/lib/sdk/deprecated/light-traits.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/deprecated/light-traits.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,599 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +module.metadata = {
    1.11 +  "stability": "deprecated"
    1.12 +};
    1.13 +
    1.14 +// `var` is being used in the module in order to make it reusable in
    1.15 +// environments in which `let` is not yet supported.
    1.16 +
    1.17 +// Shortcut to `Object.prototype.hasOwnProperty.call`.
    1.18 +// owns(object, name) would be the same as
    1.19 +// Object.prototype.hasOwnProperty.call(object, name);
    1.20 +var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
    1.21 +
    1.22 +/**
    1.23 + * Whether or not given property descriptors are equivalent. They are
    1.24 + * equivalent either if both are marked as 'conflict' or 'required' property
    1.25 + * or if all the properties of descriptors are equal.
    1.26 + * @param {Object} actual
    1.27 + * @param {Object} expected
    1.28 + */
    1.29 +function equivalentDescriptors(actual, expected) {
    1.30 +  return (actual.conflict && expected.conflict) ||
    1.31 +         (actual.required && expected.required) ||
    1.32 +         equalDescriptors(actual, expected);
    1.33 +}
    1.34 +/**
    1.35 + * Whether or not given property descriptors define equal properties.
    1.36 + */
    1.37 +function equalDescriptors(actual, expected) {
    1.38 +  return actual.get === expected.get &&
    1.39 +         actual.set === expected.set &&
    1.40 +         actual.value === expected.value &&
    1.41 +         !!actual.enumerable === !!expected.enumerable &&
    1.42 +         !!actual.configurable === !!expected.configurable &&
    1.43 +         !!actual.writable === !!expected.writable;
    1.44 +}
    1.45 +
    1.46 +// Utilities that throwing exceptions for a properties that are marked
    1.47 +// as "required" or "conflict" properties.
    1.48 +function throwConflictPropertyError(name) {
    1.49 +  throw new Error("Remaining conflicting property: `" + name + "`");
    1.50 +}
    1.51 +function throwRequiredPropertyError(name) {
    1.52 +  throw new Error("Missing required property: `" + name + "`");
    1.53 +}
    1.54 +
    1.55 +/**
    1.56 + * Generates custom **required** property descriptor. Descriptor contains
    1.57 + * non-standard property `required` that is equal to `true`.
    1.58 + * @param {String} name
    1.59 + *    property name to generate descriptor for.
    1.60 + * @returns {Object}
    1.61 + *    custom property descriptor
    1.62 + */
    1.63 +function RequiredPropertyDescriptor(name) {
    1.64 +  // Creating function by binding first argument to a property `name` on the
    1.65 +  // `throwConflictPropertyError` function. Created function is used as a
    1.66 +  // getter & setter of the created property descriptor. This way we ensure
    1.67 +  // that we throw exception late (on property access) if object with
    1.68 +  // `required` property was instantiated using built-in `Object.create`.
    1.69 +  var accessor = throwRequiredPropertyError.bind(null, name);
    1.70 +  return { get: accessor, set: accessor, required: true };
    1.71 +}
    1.72 +
    1.73 +/**
    1.74 + * Generates custom **conflicting** property descriptor. Descriptor contains
    1.75 + * non-standard property `conflict` that is equal to `true`.
    1.76 + * @param {String} name
    1.77 + *    property name to generate descriptor for.
    1.78 + * @returns {Object}
    1.79 + *    custom property descriptor
    1.80 + */
    1.81 +function ConflictPropertyDescriptor(name) {
    1.82 +  // For details see `RequiredPropertyDescriptor` since idea is same.
    1.83 +  var accessor = throwConflictPropertyError.bind(null, name);
    1.84 +  return { get: accessor, set: accessor, conflict: true };
    1.85 +}
    1.86 +
    1.87 +/**
    1.88 + * Tests if property is marked as `required` property.
    1.89 + */
    1.90 +function isRequiredProperty(object, name) {
    1.91 +  return !!object[name].required;
    1.92 +}
    1.93 +
    1.94 +/**
    1.95 + * Tests if property is marked as `conflict` property.
    1.96 + */
    1.97 +function isConflictProperty(object, name) {
    1.98 +  return !!object[name].conflict;
    1.99 +}
   1.100 +
   1.101 +/**
   1.102 + * Function tests whether or not method of the `source` object with a given
   1.103 + * `name` is inherited from `Object.prototype`.
   1.104 + */
   1.105 +function isBuiltInMethod(name, source) {
   1.106 +  var target = Object.prototype[name];
   1.107 +
   1.108 +  // If methods are equal then we know it's `true`.
   1.109 +  return target == source ||
   1.110 +  // If `source` object comes form a different sandbox `==` will evaluate
   1.111 +  // to `false`, in that case we check if functions names and sources match.
   1.112 +         (String(target) === String(source) && target.name === source.name);
   1.113 +}
   1.114 +
   1.115 +/**
   1.116 + * Function overrides `toString` and `constructor` methods of a given `target`
   1.117 + * object with a same-named methods of a given `source` if methods of `target`
   1.118 + * object are inherited / copied from `Object.prototype`.
   1.119 + * @see create
   1.120 + */
   1.121 +function overrideBuiltInMethods(target, source) {
   1.122 +  if (isBuiltInMethod("toString", target.toString)) {
   1.123 +    Object.defineProperty(target, "toString",  {
   1.124 +      value: source.toString,
   1.125 +      configurable: true,
   1.126 +      enumerable: false
   1.127 +    });
   1.128 +  }
   1.129 +
   1.130 +  if (isBuiltInMethod("constructor", target.constructor)) {
   1.131 +    Object.defineProperty(target, "constructor", {
   1.132 +      value: source.constructor,
   1.133 +      configurable: true,
   1.134 +      enumerable: false
   1.135 +    });
   1.136 +  }
   1.137 +}
   1.138 +
   1.139 +/**
   1.140 + * Composes new trait with the same own properties as the original trait,
   1.141 + * except that all property names appearing in the first argument are replaced
   1.142 + * by "required" property descriptors.
   1.143 + * @param {String[]} keys
   1.144 + *    Array of strings property names.
   1.145 + * @param {Object} trait
   1.146 + *    A trait some properties of which should be excluded.
   1.147 + * @returns {Object}
   1.148 + * @example
   1.149 + *    var newTrait = exclude(["name", ...], trait)
   1.150 + */
   1.151 +function exclude(names, trait) {
   1.152 +  var map = {};
   1.153 +
   1.154 +  Object.keys(trait).forEach(function(name) {
   1.155 +
   1.156 +    // If property is not excluded (the array of names does not contain it),
   1.157 +    // or it is a "required" property, copy it to the property descriptor `map`
   1.158 +    // that will be used for creation of resulting trait.
   1.159 +    if (!~names.indexOf(name) || isRequiredProperty(trait, name))
   1.160 +      map[name] = { value: trait[name], enumerable: true };
   1.161 +
   1.162 +    // For all the `names` in the exclude name array we create required
   1.163 +    // property descriptors and copy them to the `map`.
   1.164 +    else
   1.165 +      map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true };
   1.166 +  });
   1.167 +
   1.168 +  return Object.create(Trait.prototype, map);
   1.169 +}
   1.170 +
   1.171 +/**
   1.172 + * Composes new instance of `Trait` with a properties of a given `trait`,
   1.173 + * except that all properties whose name is an own property of `renames` will
   1.174 + * be renamed to `renames[name]` and a `"required"` property for name will be
   1.175 + * added instead.
   1.176 + *
   1.177 + * For each renamed property, a required property is generated. If
   1.178 + * the `renames` map two properties to the same name, a conflict is generated.
   1.179 + * If the `renames` map a property to an existing unrenamed property, a
   1.180 + * conflict is generated.
   1.181 + *
   1.182 + * @param {Object} renames
   1.183 + *    An object whose own properties serve as a mapping from old names to new
   1.184 + *    names.
   1.185 + * @param {Object} trait
   1.186 + *    A new trait with renamed properties.
   1.187 + * @returns {Object}
   1.188 + * @example
   1.189 + *
   1.190 + *    // Return trait with `bar` property equal to `trait.foo` and with
   1.191 + *    // `foo` and `baz` "required" properties.
   1.192 + *    var renamedTrait = rename({ foo: "bar", baz: null }), trait);
   1.193 + *
   1.194 + *    // t1 and t2 are equivalent traits
   1.195 + *    var t1 = rename({a: "b"}, t);
   1.196 + *    var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] });
   1.197 + */
   1.198 +function rename(renames, trait) {
   1.199 +  var map = {};
   1.200 +
   1.201 +  // Loop over all the properties of the given `trait` and copy them to a
   1.202 +  // property descriptor `map` that will be used for the creation of the
   1.203 +  // resulting trait.  Also, rename properties in the `map` as specified by
   1.204 +  // `renames`.
   1.205 +  Object.keys(trait).forEach(function(name) {
   1.206 +    var alias;
   1.207 +
   1.208 +    // If the property is in the `renames` map, and it isn't a "required"
   1.209 +    // property (which should never need to be aliased because "required"
   1.210 +    // properties never conflict), then we must try to rename it.
   1.211 +    if (owns(renames, name) && !isRequiredProperty(trait, name)) {
   1.212 +      alias = renames[name];
   1.213 +
   1.214 +      // If the `map` already has the `alias`, and it isn't a "required"
   1.215 +      // property, that means the `alias` conflicts with an existing name for a
   1.216 +      // provided trait (that can happen if >=2 properties are aliased to the
   1.217 +      // same name). In this case we mark it as a conflicting property.
   1.218 +      // Otherwise, everything is fine, and we copy property with an `alias`
   1.219 +      // name.
   1.220 +      if (owns(map, alias) && !map[alias].value.required) {
   1.221 +        map[alias] = {
   1.222 +          value: ConflictPropertyDescriptor(alias),
   1.223 +          enumerable: true
   1.224 +        };
   1.225 +      }
   1.226 +      else {
   1.227 +        map[alias] = {
   1.228 +          value: trait[name],
   1.229 +          enumerable: true
   1.230 +        };
   1.231 +      }
   1.232 +
   1.233 +      // Regardless of whether or not the rename was successful, we check to
   1.234 +      // see if the original `name` exists in the map (such a property
   1.235 +      // could exist if previous another property was aliased to this `name`).
   1.236 +      // If it isn't, we mark it as "required", to make sure the caller
   1.237 +      // provides another value for the old name, which methods of the trait
   1.238 +      // might continue to reference.
   1.239 +      if (!owns(map, name)) {
   1.240 +        map[name] = {
   1.241 +          value: RequiredPropertyDescriptor(name),
   1.242 +          enumerable: true
   1.243 +        };
   1.244 +      }
   1.245 +    }
   1.246 +
   1.247 +    // Otherwise, either the property isn't in the `renames` map (thus the
   1.248 +    // caller is not trying to rename it) or it is a "required" property.
   1.249 +    // Either way, we don't have to alias the property, we just have to copy it
   1.250 +    // to the map.
   1.251 +    else {
   1.252 +      // The property isn't in the map yet, so we copy it over.
   1.253 +      if (!owns(map, name)) {
   1.254 +        map[name] = { value: trait[name], enumerable: true };
   1.255 +      }
   1.256 +
   1.257 +      // The property is already in the map (that means another property was
   1.258 +      // aliased with this `name`, which creates a conflict if the property is
   1.259 +      // not marked as "required"), so we have to mark it as a "conflict"
   1.260 +      // property.
   1.261 +      else if (!isRequiredProperty(trait, name)) {
   1.262 +        map[name] = {
   1.263 +          value: ConflictPropertyDescriptor(name),
   1.264 +          enumerable: true
   1.265 +        };
   1.266 +      }
   1.267 +    }
   1.268 +  });
   1.269 +  return Object.create(Trait.prototype, map);
   1.270 +}
   1.271 +
   1.272 +/**
   1.273 + * Composes new resolved trait, with all the same properties as the original
   1.274 + * `trait`, except that all properties whose name is an own property of
   1.275 + * `resolutions` will be renamed to `resolutions[name]`.
   1.276 + *
   1.277 + * If `resolutions[name]` is `null`, the value is mapped to a property
   1.278 + * descriptor that is marked as a "required" property.
   1.279 + */
   1.280 +function resolve(resolutions, trait) {
   1.281 +    var renames = {};
   1.282 +    var exclusions = [];
   1.283 +
   1.284 +    // Go through each mapping in `resolutions` object and distribute it either
   1.285 +    // to `renames` or `exclusions`.
   1.286 +    Object.keys(resolutions).forEach(function(name) {
   1.287 +
   1.288 +      // If `resolutions[name]` is a truthy value then it's a mapping old -> new
   1.289 +      // so we copy it to `renames` map.
   1.290 +      if (resolutions[name])
   1.291 +        renames[name] = resolutions[name];
   1.292 +
   1.293 +      // Otherwise it's not a mapping but an exclusion instead in which case we
   1.294 +      // add it to the `exclusions` array.
   1.295 +      else
   1.296 +        exclusions.push(name);
   1.297 +    });
   1.298 +
   1.299 +    // First `exclude` **then** `rename` and order is important since
   1.300 +    // `exclude` and `rename` are not associative.
   1.301 +    return rename(renames, exclude(exclusions, trait));
   1.302 +}
   1.303 +
   1.304 +/**
   1.305 + * Create a Trait (a custom property descriptor map) that represents the given
   1.306 + * `object`'s own properties. Property descriptor map is a "custom", because it
   1.307 + * inherits from `Trait.prototype` and it's property descriptors may contain
   1.308 + * two attributes that is not part of the ES5 specification:
   1.309 + *
   1.310 + *  - "required" (this property must be provided by another trait
   1.311 + *    before an instance of this trait can be created)
   1.312 + *  - "conflict" (when the trait is composed with another trait,
   1.313 + *    a unique value for this property is provided by two or more traits)
   1.314 + *
   1.315 + * Data properties bound to the `Trait.required` singleton exported by
   1.316 + * this module will be marked as "required" properties.
   1.317 + *
   1.318 + * @param {Object} object
   1.319 + *    Map of properties to compose trait from.
   1.320 + * @returns {Trait}
   1.321 + *    Trait / Property descriptor map containing all the own properties of the
   1.322 + *    given argument.
   1.323 + */
   1.324 +function trait(object) {
   1.325 +  var map;
   1.326 +  var trait = object;
   1.327 +
   1.328 +  if (!(object instanceof Trait)) {
   1.329 +    // If the passed `object` is not already an instance of `Trait`, we create
   1.330 +    // a property descriptor `map` containing descriptors for the own properties
   1.331 +    // of the given `object`.  `map` is then used to create a `Trait` instance
   1.332 +    // after all properties are mapped.  Note that we can't create a trait and
   1.333 +    // then just copy properties into it since that will fail for inherited
   1.334 +    // read-only properties.
   1.335 +    map = {};
   1.336 +
   1.337 +    // Each own property of the given `object` is mapped to a data property
   1.338 +    // whose value is a property descriptor.
   1.339 +    Object.keys(object).forEach(function (name) {
   1.340 +
   1.341 +      // If property of an `object` is equal to a `Trait.required`, it means
   1.342 +      // that it was marked as "required" property, in which case we map it
   1.343 +      // to "required" property.
   1.344 +      if (Trait.required ==
   1.345 +          Object.getOwnPropertyDescriptor(object, name).value) {
   1.346 +        map[name] = {
   1.347 +          value: RequiredPropertyDescriptor(name),
   1.348 +          enumerable: true
   1.349 +        };
   1.350 +      }
   1.351 +      // Otherwise property is mapped to it's property descriptor.
   1.352 +      else {
   1.353 +        map[name] = {
   1.354 +          value: Object.getOwnPropertyDescriptor(object, name),
   1.355 +          enumerable: true
   1.356 +        };
   1.357 +      }
   1.358 +    });
   1.359 +
   1.360 +    trait = Object.create(Trait.prototype, map);
   1.361 +  }
   1.362 +  return trait;
   1.363 +}
   1.364 +
   1.365 +/**
   1.366 + * Compose a property descriptor map that inherits from `Trait.prototype` and
   1.367 + * contains property descriptors for all the own properties of the passed
   1.368 + * traits.
   1.369 + *
   1.370 + * If two or more traits have own properties with the same name, the returned
   1.371 + * trait will contain a "conflict" property for that name. Composition is a
   1.372 + * commutative and associative operation, and the order of its arguments is
   1.373 + * irrelevant.
   1.374 + */
   1.375 +function compose(trait1, trait2/*, ...*/) {
   1.376 +  // Create a new property descriptor `map` to which all the own properties
   1.377 +  // of the passed traits are copied.  This map will be used to create a `Trait`
   1.378 +  // instance that will be the result of this composition.
   1.379 +  var map = {};
   1.380 +
   1.381 +  // Properties of each passed trait are copied to the composition.
   1.382 +  Array.prototype.forEach.call(arguments, function(trait) {
   1.383 +    // Copying each property of the given trait.
   1.384 +    Object.keys(trait).forEach(function(name) {
   1.385 +
   1.386 +      // If `map` already owns a property with the `name` and it is not
   1.387 +      // marked "required".
   1.388 +      if (owns(map, name) && !map[name].value.required) {
   1.389 +
   1.390 +        // If the source trait's property with the `name` is marked as
   1.391 +        // "required", we do nothing, as the requirement was already resolved
   1.392 +        // by a property in the `map` (because it already contains a
   1.393 +        // non-required property with that `name`).  But if properties are just
   1.394 +        // different, we have a name clash and we substitute it with a property
   1.395 +        // that is marked "conflict".
   1.396 +        if (!isRequiredProperty(trait, name) &&
   1.397 +            !equivalentDescriptors(map[name].value, trait[name])
   1.398 +        ) {
   1.399 +          map[name] = {
   1.400 +            value: ConflictPropertyDescriptor(name),
   1.401 +            enumerable: true
   1.402 +          };
   1.403 +        }
   1.404 +      }
   1.405 +
   1.406 +      // Otherwise, the `map` does not have an own property with the `name`, or
   1.407 +      // it is marked "required".  Either way, the trait's property is copied to
   1.408 +      // the map (if the property of the `map` is marked "required", it is going
   1.409 +      // to be resolved by the property that is being copied).
   1.410 +      else {
   1.411 +        map[name] = { value: trait[name], enumerable: true };
   1.412 +      }
   1.413 +    });
   1.414 +  });
   1.415 +
   1.416 +  return Object.create(Trait.prototype, map);
   1.417 +}
   1.418 +
   1.419 +/**
   1.420 + *  `defineProperties` is like `Object.defineProperties`, except that it
   1.421 + *  ensures that:
   1.422 + *    - An exception is thrown if any property in a given `properties` map
   1.423 + *      is marked as "required" property and same named property is not
   1.424 + *      found in a given `prototype`.
   1.425 + *    - An exception is thrown if any property in a given `properties` map
   1.426 + *      is marked as "conflict" property.
   1.427 + * @param {Object} object
   1.428 + *    Object to define properties on.
   1.429 + * @param {Object} properties
   1.430 + *    Properties descriptor map.
   1.431 + * @returns {Object}
   1.432 + *    `object` that was passed as a first argument.
   1.433 + */
   1.434 +function defineProperties(object, properties) {
   1.435 +
   1.436 +  // Create a map into which we will copy each verified property from the given
   1.437 +  // `properties` description map. We use it to verify that none of the
   1.438 +  // provided properties is marked as a "conflict" property and that all
   1.439 +  // "required" properties are resolved by a property of an `object`, so we
   1.440 +  // can throw an exception before mutating object if that isn't the case.
   1.441 +  var verifiedProperties = {};
   1.442 +
   1.443 +  // Coping each property from a given `properties` descriptor map to a
   1.444 +  // verified map of property descriptors.
   1.445 +  Object.keys(properties).forEach(function(name) {
   1.446 +
   1.447 +    // If property is marked as "required" property and we don't have a same
   1.448 +    // named property in a given `object` we throw an exception. If `object`
   1.449 +    // has same named property just skip this property since required property
   1.450 +    // is was inherited and there for requirement was satisfied.
   1.451 +    if (isRequiredProperty(properties, name)) {
   1.452 +      if (!(name in object))
   1.453 +        throwRequiredPropertyError(name);
   1.454 +    }
   1.455 +
   1.456 +    // If property is marked as "conflict" property we throw an exception.
   1.457 +    else if (isConflictProperty(properties, name)) {
   1.458 +      throwConflictPropertyError(name);
   1.459 +    }
   1.460 +
   1.461 +    // If property is not marked neither as "required" nor "conflict" property
   1.462 +    // we copy it to verified properties map.
   1.463 +    else {
   1.464 +      verifiedProperties[name] = properties[name];
   1.465 +    }
   1.466 +  });
   1.467 +
   1.468 +  // If no exceptions were thrown yet, we know that our verified property
   1.469 +  // descriptor map has no properties marked as "conflict" or "required",
   1.470 +  // so we just delegate to the built-in `Object.defineProperties`.
   1.471 +  return Object.defineProperties(object, verifiedProperties);
   1.472 +}
   1.473 +
   1.474 +/**
   1.475 + *  `create` is like `Object.create`, except that it ensures that:
   1.476 + *    - An exception is thrown if any property in a given `properties` map
   1.477 + *      is marked as "required" property and same named property is not
   1.478 + *      found in a given `prototype`.
   1.479 + *    - An exception is thrown if any property in a given `properties` map
   1.480 + *      is marked as "conflict" property.
   1.481 + * @param {Object} prototype
   1.482 + *    prototype of the composed object
   1.483 + * @param {Object} properties
   1.484 + *    Properties descriptor map.
   1.485 + * @returns {Object}
   1.486 + *    An object that inherits form a given `prototype` and implements all the
   1.487 + *    properties defined by a given `properties` descriptor map.
   1.488 + */
   1.489 +function create(prototype, properties) {
   1.490 +
   1.491 +  // Creating an instance of the given `prototype`.
   1.492 +  var object = Object.create(prototype);
   1.493 +
   1.494 +  // Overriding `toString`, `constructor` methods if they are just inherited
   1.495 +  // from `Object.prototype` with a same named methods of the `Trait.prototype`
   1.496 +  // that will have more relevant behavior.
   1.497 +  overrideBuiltInMethods(object, Trait.prototype);
   1.498 +
   1.499 +  // Trying to define given `properties` on the `object`. We use our custom
   1.500 +  // `defineProperties` function instead of build-in `Object.defineProperties`
   1.501 +  // that behaves exactly the same, except that it will throw if any
   1.502 +  // property in the given `properties` descriptor is marked as "required" or
   1.503 +  // "conflict" property.
   1.504 +  return defineProperties(object, properties);
   1.505 +}
   1.506 +
   1.507 +/**
   1.508 + * Composes new trait. If two or more traits have own properties with the
   1.509 + * same name, the new trait will contain a "conflict" property for that name.
   1.510 + * "compose" is a commutative and associative operation, and the order of its
   1.511 + * arguments is not significant.
   1.512 + *
   1.513 + * **Note:** Use `Trait.compose` instead of calling this function with more
   1.514 + * than one argument. The multiple-argument functionality is strictly for
   1.515 + * backward compatibility.
   1.516 + *
   1.517 + * @params {Object} trait
   1.518 + *    Takes traits as an arguments
   1.519 + * @returns {Object}
   1.520 + *    New trait containing the combined own properties of all the traits.
   1.521 + * @example
   1.522 + *    var newTrait = compose(trait_1, trait_2, ..., trait_N)
   1.523 + */
   1.524 +function Trait(trait1, trait2) {
   1.525 +
   1.526 +  // If the function was called with one argument, the argument should be
   1.527 +  // an object whose properties are mapped to property descriptors on a new
   1.528 +  // instance of Trait, so we delegate to the trait function.
   1.529 +  // If the function was called with more than one argument, those arguments
   1.530 +  // should be instances of Trait or plain property descriptor maps
   1.531 +  // whose properties should be mixed into a new instance of Trait,
   1.532 +  // so we delegate to the compose function.
   1.533 +
   1.534 +  return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments);
   1.535 +}
   1.536 +
   1.537 +Object.freeze(Object.defineProperties(Trait.prototype, {
   1.538 +  toString: {
   1.539 +    value: function toString() {
   1.540 +      return "[object " + this.constructor.name + "]";
   1.541 +    }
   1.542 +  },
   1.543 +
   1.544 +  /**
   1.545 +   * `create` is like `Object.create`, except that it ensures that:
   1.546 +   *    - An exception is thrown if this trait defines a property that is
   1.547 +   *      marked as required property and same named property is not
   1.548 +   *      found in a given `prototype`.
   1.549 +   *    - An exception is thrown if this trait contains property that is
   1.550 +   *      marked as "conflict" property.
   1.551 +   * @param {Object}
   1.552 +   *    prototype of the compared object
   1.553 +   * @returns {Object}
   1.554 +   *    An object with all of the properties described by the trait.
   1.555 +   */
   1.556 +  create: {
   1.557 +    value: function createTrait(prototype) {
   1.558 +      return create(undefined === prototype ? Object.prototype : prototype,
   1.559 +                    this);
   1.560 +    },
   1.561 +    enumerable: true
   1.562 +  },
   1.563 +
   1.564 +  /**
   1.565 +   * Composes a new resolved trait, with all the same properties as the original
   1.566 +   * trait, except that all properties whose name is an own property of
   1.567 +   * `resolutions` will be renamed to the value of `resolutions[name]`. If
   1.568 +   * `resolutions[name]` is `null`, the property is marked as "required".
   1.569 +   * @param {Object} resolutions
   1.570 +   *   An object whose own properties serve as a mapping from old names to new
   1.571 +   *   names, or to `null` if the property should be excluded.
   1.572 +   * @returns {Object}
   1.573 +   *   New trait with the same own properties as the original trait but renamed.
   1.574 +   */
   1.575 +  resolve: {
   1.576 +    value: function resolveTrait(resolutions) {
   1.577 +      return resolve(resolutions, this);
   1.578 +    },
   1.579 +    enumerable: true
   1.580 +  }
   1.581 +}));
   1.582 +
   1.583 +/**
   1.584 + * @see compose
   1.585 + */
   1.586 +Trait.compose = Object.freeze(compose);
   1.587 +Object.freeze(compose.prototype);
   1.588 +
   1.589 +/**
   1.590 + * Constant singleton, representing placeholder for required properties.
   1.591 + * @type {Object}
   1.592 + */
   1.593 +Trait.required = Object.freeze(Object.create(Object.prototype, {
   1.594 +  toString: {
   1.595 +    value: Object.freeze(function toString() {
   1.596 +      return "<Trait.required>";
   1.597 +    })
   1.598 +  }
   1.599 +}));
   1.600 +Object.freeze(Trait.required.toString.prototype);
   1.601 +
   1.602 +exports.Trait = Object.freeze(Trait);

mercurial