text: // Copyright (C) 2010 Google Inc.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  // http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  // See http://code.google.com/p/es-lab/wiki/Traits
  // for background on traits and a description of this library
  
  var Trait = (function(){
  
    // == Ancillary functions ==
    
    var SUPPORTS_DEFINEPROP = (function() {
      try {
        var test = {};
        Object.defineProperty(test, 'x', {get: function() { return 0; } } );
        return test.x === 0;
      } catch(e) {
        return false;
      }
    })();
    
    // IE8 implements Object.defineProperty and Object.getOwnPropertyDescriptor
    // only for DOM objects. These methods don't work on plain objects.
    // Hence, we need a more elaborate feature-test to see whether the
    // browser truly supports these methods:
    function supportsGOPD() {
      try {
        if (Object.getOwnPropertyDescriptor) {
          var test = {x:0};
          return !!Object.getOwnPropertyDescriptor(test,'x');        
        }
      } catch(e) {}
      return false;
    };
    function supportsDP() {
      try {
        if (Object.defineProperty) {
          var test = {};
          Object.defineProperty(test,'x',{value:0});
          return test.x === 0;
        }
      } catch(e) {}
      return false;
    };
  
    var call = Function.prototype.call;
  
    /**
     * An ad hoc version of bind that only binds the 'this' parameter.
     */
    var bindThis = Function.prototype.bind ?
      function(fun, self) { return Function.prototype.bind.call(fun, self); } :
      function(fun, self) {
        function funcBound(var_args) {
          return fun.apply(self, arguments);
        }
        return funcBound;
      };
  
    var hasOwnProperty = bindThis(call, Object.prototype.hasOwnProperty);
    var slice = bindThis(call, Array.prototype.slice);
      
    // feature testing such that traits.js runs on both ES3 and ES5
    var forEach = Array.prototype.forEach ?
        bindThis(call, Array.prototype.forEach) :
        function(arr, fun) {
          for (var i = 0, len = arr.length; i < len; i++) { fun(arr[i]); }
        };
  //  var freezing = false;
  //  var freeze = freezing
  //      ? Object.freeze || function(obj) { return obj; }
  //      : function(obj) { return obj; };
      var freeze = function(obj) { return obj; };
    var getPrototypeOf = Object.getPrototypeOf || function(obj) { 
      return Object.prototype;
    };
    var getOwnPropertyNames = Object.getOwnPropertyNames ||
        function(obj) {
          var props = [];
          for (var p in obj) { if (hasOwnProperty(obj,p)) { props.push(p); } }
          return props;
        };
    var getOwnPropertyDescriptor = supportsGOPD() ?
        Object.getOwnPropertyDescriptor :
        function(obj, name) {
          return {
            value: obj[name],
            enumerable: true,
            writable: true,
            configurable: true
          };
        };
    var defineProperty = supportsDP() ? Object.defineProperty :
        function(obj, name, pd) {
          obj[name] = pd.value;
        };
    var defineProperties = Object.defineProperties ||
        function(obj, propMap) {
          for (var name in propMap) {
            if (hasOwnProperty(propMap, name)) {
              defineProperty(obj, name, propMap[name]);
            }
          }
        };
    var Object_create = Object.create ||
        function(proto, propMap) {
          var self;
          function dummy() {};
          dummy.prototype = proto || Object.prototype;
          self = new dummy();
          if (propMap) {
            defineProperties(self, propMap);          
          }
          return self;
        };
    var getOwnProperties = Object.getOwnProperties ||
        function(obj) {
          var map = {};
          forEach(getOwnPropertyNames(obj), function (name) {
            map[name] = getOwnPropertyDescriptor(obj, name);
          });
          return map;
        };
    
    // end of ES3 - ES5 compatibility functions
    
    function makeConflictAccessor(name) {
      var accessor = function(var_args) {
        throw new Error("Conflicting property: "+name);
      };
      freeze(accessor.prototype);
      return freeze(accessor);
    };
  
    function makeRequiredPropDesc(name) {
      return freeze({
        value: undefined,
        enumerable: false,
        required: true
      });
    }
    
    function makeConflictingPropDesc(name) {
      var conflict = makeConflictAccessor(name);
      if (SUPPORTS_DEFINEPROP) {
        return freeze({
         get: conflict,
         set: conflict,
         enumerable: false,
         conflict: true
        }); 
      } else {
        return freeze({
          value: conflict,
          enumerable: false,
          conflict: true
        });
      }
    }
    
    /**
     * Are x and y not observably distinguishable?
     */
    function identical(x, y) {
      if (x === y) {
        // 0 === -0, but they are not identical
        return x !== 0 || 1/x === 1/y;
      } else {
        // NaN !== NaN, but they are identical.
        // NaNs are the only non-reflexive value, i.e., if x !== x,
        // then x is a NaN.
        return x !== x && y !== y;
      }
    }
  
    // Note: isSameDesc should return true if both
    // desc1 and desc2 represent a 'required' property
    // (otherwise two composed required properties would be turned into
    // a conflict) 
    function isSameDesc(desc1, desc2) {
      // for conflicting properties, don't compare values because
      // the conflicting property values are never equal
      if (desc1.conflict && desc2.conflict) {
        return true;
      } else {
        return (   desc1.get === desc2.get
                && desc1.set === desc2.set
                && identical(desc1.value, desc2.value)
                && desc1.enumerable === desc2.enumerable
                && desc1.required === desc2.required
                && desc1.conflict === desc2.conflict); 
      }
    }
    
    function freezeAndBind(meth, self) {
      return freeze(bindThis(meth, self));
    }
  
    /* makeSet(['foo', ...]) => { foo: true, ...}
     *
     * makeSet returns an object whose own properties represent a set.
     *
     * Each string in the names array is added to the set.
     *
     * To test whether an element is in the set, perform:
     *   hasOwnProperty(set, element)
     */
    function makeSet(names) {
      var set = {};
      forEach(names, function (name) {
        set[name] = true;
      });
      return freeze(set);
    }
  
    // == singleton object to be used as the placeholder for a required
    // property == 
    
    var required = freeze({ 
      toString: function() { return '<Trait.required>'; } 
    });
  
    // == The public API methods ==
  
    /**
     * var newTrait = trait({ foo:required, ... })
     *
     * @param object an object record (in principle an object literal)
     * @returns a new trait describing all of the own properties of the object
     *          (both enumerable and non-enumerable)
     *
     * As a general rule, 'trait' should be invoked with an object
     * literal, since the object merely serves as a record
     * descriptor. Both its identity and its prototype chain are
     * irrelevant.
     * 
     * Data properties bound to function objects in the argument will be
     * flagged as 'method' properties. The prototype of these function
     * objects is frozen.
     * 
     * Data properties bound to the 'required' singleton exported by
     * this module will be marked as 'required' properties.
     *
     * The <tt>trait</tt> function is pure if no other code can witness
     * the side-effects of freezing the prototypes of the methods. If
     * <tt>trait</tt> is invoked with an object literal whose methods
     * are represented as in-place anonymous functions, this should
     * normally be the case.
     */
    function trait(obj) {
      var map = {};
      forEach(getOwnPropertyNames(obj), function (name) {
        var pd = getOwnPropertyDescriptor(obj, name);
        if (pd.value === required) {
          pd = makeRequiredPropDesc(name);
        } else if (typeof pd.value === 'function') {
          pd.method = true;
          if ('prototype' in pd.value) {
            freeze(pd.value.prototype);
          }
        } else {
          if (pd.get && pd.get.prototype) { freeze(pd.get.prototype); }
          if (pd.set && pd.set.prototype) { freeze(pd.set.prototype); }
        }
        map[name] = pd;
      });
      return map;
    }
  
    /**
     * var newTrait = compose(trait_1, trait_2, ..., trait_N)
     *
     * @param trait_i a trait object
     * @returns a new trait containing the combined own properties of
     *          all the trait_i.
     * 
     * If two or more traits have own properties with the same name, the new
     * trait will contain a 'conflict' property for that name. 'compose' is
     * a commutative and associative operation, and the order of its
     * arguments is not significant.
     *
     * If 'compose' is invoked with < 2 arguments, then:
     *   compose(trait_1) returns a trait equivalent to trait_1
     *   compose() returns an empty trait
     */
    function compose(var_args) {
      var traits = slice(arguments, 0);
      var newTrait = {};
      
      forEach(traits, function (trait) {
        forEach(getOwnPropertyNames(trait), function (name) {
          var pd = trait[name];
          if (hasOwnProperty(newTrait, name) &&
              !newTrait[name].required) {
            
            // a non-required property with the same name was previously
            // defined this is not a conflict if pd represents a
            // 'required' property itself:
            if (pd.required) {
              return; // skip this property, the required property is
     	            // now present 
            }
              
            if (!isSameDesc(newTrait[name], pd)) {
              // a distinct, non-required property with the same name
              // was previously defined by another trait => mark as
  	    // conflicting property
              newTrait[name] = makeConflictingPropDesc(name); 
            } // else,
            // properties are not in conflict if they refer to the same value
            
          } else {
            newTrait[name] = pd;
          }
        });
      });
      
      return freeze(newTrait);
    }
  
    /* var newTrait = exclude(['name', ...], trait)
     *
     * @param names a list of strings denoting property names.
     * @param trait a trait some properties of which should be excluded.
     * @returns a new trait with the same own properties as the original trait,
     *          except that all property names appearing in the first argument
     *          are replaced by required property descriptors.
     *
     * Note: exclude(A, exclude(B,t)) is equivalent to exclude(A U B, t)
     */
    function exclude(names, trait) {
      var exclusions = makeSet(names);
      var newTrait = {};
      
      forEach(getOwnPropertyNames(trait), function (name) {
        // required properties are not excluded but ignored
        if (!hasOwnProperty(exclusions, name) || trait[name].required) {
          newTrait[name] = trait[name];
        } else {
          // excluded properties are replaced by required properties
          newTrait[name] = makeRequiredPropDesc(name);
        }
      });
      
      return freeze(newTrait);
    }
  
    /**
     * var newTrait = override(trait_1, trait_2, ..., trait_N)
     *
     * @returns a new trait with all of the combined properties of the
     *          argument traits.  In contrast to 'compose', 'override'
     *          immediately resolves all conflicts resulting from this
     *          composition by overriding the properties of later
     *          traits. Trait priority is from left to right. I.e. the
     *          properties of the leftmost trait are never overridden.
     *
     *  override is associative:
     *    override(t1,t2,t3) is equivalent to override(t1, override(t2, t3)) or
     *    to override(override(t1, t2), t3)
     *  override is not commutative: override(t1,t2) is not equivalent
     *    to override(t2,t1)
     *
     * override() returns an empty trait
     * override(trait_1) returns a trait equivalent to trait_1
     */
    function override(var_args) {
      var traits = slice(arguments, 0);
      var newTrait = {};
      forEach(traits, function (trait) {
        forEach(getOwnPropertyNames(trait), function (name) {
          var pd = trait[name];
          // add this trait's property to the composite trait only if
          // - the trait does not yet have this property
          // - or, the trait does have the property, but it's a required property
          if (!hasOwnProperty(newTrait, name) || newTrait[name].required) {
            newTrait[name] = pd;
          }
        });
      });
      return freeze(newTrait);
    }
    
    /**
     * var newTrait = override(dominantTrait, recessiveTrait)
     *
     * @returns a new trait with all of the properties of dominantTrait
     *          and all of the properties of recessiveTrait not in dominantTrait
     *
     * Note: override is associative:
     *   override(t1, override(t2, t3)) is equivalent to
     *   override(override(t1, t2), t3) 
     */
    /*function override(frontT, backT) {
      var newTrait = {};
      // first copy all of backT's properties into newTrait
      forEach(getOwnPropertyNames(backT), function (name) {
        newTrait[name] = backT[name];
      });
      // now override all these properties with frontT's properties
      forEach(getOwnPropertyNames(frontT), function (name) {
        var pd = frontT[name];
        // frontT's required property does not override the provided property
        if (!(pd.required && hasOwnProperty(newTrait, name))) {
          newTrait[name] = pd; 
        }      
      });
      
      return freeze(newTrait);
    }*/
  
    /**
     * var newTrait = rename(map, trait)
     *
     * @param map an object whose own properties serve as a mapping from
              old names to new names.
     * @param trait a trait object
     * @returns a new trait with the same properties as the original trait,
     *          except that all properties whose name is an own property
     *          of map will be renamed to map[name], and a 'required' property
     *          for name will be added instead.
     *
     * rename({a: 'b'}, t) eqv compose(exclude(['a'],t),
     *                                 { a: { required: true },
     *                                   b: t[a] })
     *
     * For each renamed property, a required property is generated.  If
     * the map renames two properties to the same name, a conflict is
     * generated.  If the map renames a property to an existing
     * unrenamed property, a conflict is generated.
     *
     * Note: rename(A, rename(B, t)) is equivalent to rename(\n ->
     * A(B(n)), t) Note: rename({...},exclude([...], t)) is not eqv to
     * exclude([...],rename({...}, t))
     */
    function rename(map, trait) {
      var renamedTrait = {};
      forEach(getOwnPropertyNames(trait), function (name) {
        // required props are never renamed
        if (hasOwnProperty(map, name) && !trait[name].required) {
          var alias = map[name]; // alias defined in map
          if (hasOwnProperty(renamedTrait, alias) && 
  	    !renamedTrait[alias].required) {
            // could happen if 2 props are mapped to the same alias
            renamedTrait[alias] = makeConflictingPropDesc(alias);
          } else {
            // add the property under an alias
            renamedTrait[alias] = trait[name];
          }
          // add a required property under the original name
          // but only if a property under the original name does not exist
          // such a prop could exist if an earlier prop in the trait was
          // previously aliased to this name
          if (!hasOwnProperty(renamedTrait, name)) {
            renamedTrait[name] = makeRequiredPropDesc(name);     
          }
        } else { // no alias defined
          if (hasOwnProperty(renamedTrait, name)) {
            // could happen if another prop was previously aliased to name
            if (!trait[name].required) {
              renamedTrait[name] = makeConflictingPropDesc(name);            
            }
            // else required property overridden by a previously aliased
            // property and otherwise ignored
          } else {
            renamedTrait[name] = trait[name];
          }
        }
      });
      
      return freeze(renamedTrait);
    }
    
    /**
     * var newTrait = resolve({ oldName: 'newName', excludeName:
     * undefined, ... }, trait)
     *
     * This is a convenience function combining renaming and
     * exclusion. It can be implemented as <tt>rename(map,
     * exclude(exclusions, trait))</tt> where map is the subset of
     * mappings from oldName to newName and exclusions is an array of
     * all the keys that map to undefined (or another falsy value).
     *
     * @param resolutions an object whose own properties serve as a
              mapping from old names to new names, or to undefined if
              the property should be excluded
     * @param trait a trait object
     * @returns a resolved trait with the same own properties as the
     * original trait.
     *
     * In a resolved trait, all own properties whose name is an own property
     * of resolutions will be renamed to resolutions[name] if it is truthy,
     * or their value is changed into a required property descriptor if
     * resolutions[name] is falsy.
     *
     * Note, it's important to _first_ exclude, _then_ rename, since exclude
     * and rename are not associative, for example:
     * rename({a: 'b'}, exclude(['b'], trait({ a:1,b:2 }))) eqv trait({b:1})
     * exclude(['b'], rename({a: 'b'}, trait({ a:1,b:2 }))) eqv
     * trait({b:Trait.required}) 
     *
     * writing resolve({a:'b', b: undefined},trait({a:1,b:2})) makes it
     * clear that what is meant is to simply drop the old 'b' and rename
     * 'a' to 'b'
     */
    function resolve(resolutions, trait) {
      var renames = {};
      var exclusions = [];
      // preprocess renamed and excluded properties
      for (var name in resolutions) {
        if (hasOwnProperty(resolutions, name)) {
          if (resolutions[name]) { // old name -> new name
            renames[name] = resolutions[name];
          } else { // name -> undefined
            exclusions.push(name);
          }
        }
      }
      return rename(renames, exclude(exclusions, trait));
    }
  
    /**
     * var obj = create(proto, trait)
     *
     * @param proto denotes the prototype of the completed object
     * @param trait a trait object to be turned into a complete object
     * @returns an object with all of the properties described by the trait.
     * @throws 'Missing required property' the trait still contains a
     *         required property.
     * @throws 'Remaining conflicting property' if the trait still
     *         contains a conflicting property. 
     *
     * Trait.create is like Object.create, except that it generates
     * high-integrity or final objects. In addition to creating a new object
     * from a trait, it also ensures that:
     *    - an exception is thrown if 'trait' still contains required properties
     *    - an exception is thrown if 'trait' still contains conflicting
     *      properties 
     *    - the object is and all of its accessor and method properties are frozen
     *    - the 'this' pseudovariable in all accessors and methods of
     *      the object is bound to the composed object.
     *
     *  Use Object.create instead of Trait.create if you want to create
     *  abstract or malleable objects. Keep in mind that for such objects:
     *    - no exception is thrown if 'trait' still contains required properties
     *      (the properties are simply dropped from the composite object)
     *    - no exception is thrown if 'trait' still contains conflicting
     *      properties (these properties remain as conflicting
     *      properties in the composite object) 
     *    - neither the object nor its accessor and method properties are frozen
     *    - the 'this' pseudovariable in all accessors and methods of
     *      the object is left unbound.
     */
    function create(proto, trait) {
      var self = Object_create(proto);
      var properties = {};
    
      forEach(getOwnPropertyNames(trait), function (name) {
        var pd = trait[name];
        // check for remaining 'required' properties
        // Note: it's OK for the prototype to provide the properties
        if (pd.required) {
          if (!(name in proto)) {
            throw new Error('Missing required property: '+name);
          }
        } else if (pd.conflict) { // check for remaining conflicting properties
          throw new Error('Remaining conflicting property: '+name);
        } else if ('value' in pd) { // data property
          // freeze all function properties and their prototype
          if (pd.method) { // the property is meant to be used as a method
            // bind 'this' in trait method to the composite object
            properties[name] = {
              value: freezeAndBind(pd.value, self),
              enumerable: pd.enumerable,
              configurable: pd.configurable,
              writable: pd.writable
            };
          } else {
            properties[name] = pd;
          }
        } else { // accessor property
          properties[name] = {
            get: pd.get ? freezeAndBind(pd.get, self) : undefined,
            set: pd.set ? freezeAndBind(pd.set, self) : undefined,
            enumerable: pd.enumerable,
            configurable: pd.configurable
          };
        }
      });
  
      defineProperties(self, properties);
      return freeze(self);
    }
  
    /** A shorthand for create(Object.prototype, trait({...}), options) */
    function object(record, options) {
      return create(Object.prototype, trait(record), options);
    }
  
    /**
     * Tests whether two traits are equivalent. T1 is equivalent to T2 iff
     * both describe the same set of property names and for all property
     * names n, T1[n] is equivalent to T2[n]. Two property descriptors are
     * equivalent if they have the same value, accessors and attributes.
     *
     * @return a boolean indicating whether the two argument traits are
     *         equivalent.
     */
    function eqv(trait1, trait2) {
      var names1 = getOwnPropertyNames(trait1);
      var names2 = getOwnPropertyNames(trait2);
      var name;
      if (names1.length !== names2.length) {
        return false;
      }
      for (var i = 0; i < names1.length; i++) {
        name = names1[i];
        if (!trait2[name] || !isSameDesc(trait1[name], trait2[name])) {
          return false;
        }
      }
      return true;
    }
    
    // if this code is ran in ES3 without an Object.create function, this
    // library will define it on Object:
    if (!Object.create) {
      Object.create = Object_create;
    }
    // ES5 does not by default provide Object.getOwnProperties
    // if it's not defined, the Traits library defines this utility
    // function on Object 
    if(!Object.getOwnProperties) {
      Object.getOwnProperties = getOwnProperties;
    }
    
    // expose the public API of this module
    function Trait(record) {
      // calling Trait as a function creates a new atomic trait
      return trait(record);
    }
    Trait.required = freeze(required);
    Trait.compose = freeze(compose);
    Trait.resolve = freeze(resolve);
    Trait.override = freeze(override);
    Trait.create = freeze(create);
    Trait.eqv = freeze(eqv);
    Trait.object = freeze(object); // not essential, cf. create + trait
    return freeze(Trait);
    
  })();
  
  if (typeof exports !== "undefined") { // CommonJS module support
    exports.Trait = Trait;
  }
  module.exports = Trait
  