<commit_before>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.
 */

/*
 * Debug.js
 *
 * This file contains refinements and replacements designed to make
 * FOAM apps easier to debug. Things like more informative toString() methods
 * .describe() on various types of objects, extra type checking, warnings,
 * and asserts, etc. Many of these features may negatively affect performance,
 * so while this file should be loaded during your day-to-day development,
 * it should not be included in production.
 */

foam.assert = function assert(cond /*, args */) {
  if ( ! cond ) {
    var msg = Array.prototype.slice.call(arguments, 1).join(' ');
    console.assert(false, msg);
  }
};

/* Validating a Model should also validate all of its Axioms. */
foam.CLASS({
  refines: 'foam.core.Model',

  methods: [
    function validate() {
      this.SUPER();

      if ( this.hasOwnProperty('extends') && this.refines )
        throw this.id + ': "extends" and "refines" are mutually exclusive.';

      for ( var i = 0 ; i < this.axioms_.length ; i++ )
        this.axioms_[i].validate && this.axioms_[i].validate(this);
    }
  ]
});

/* Validating a Model should also validate all of its Axioms. */
foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    function validate(model) {
      this.SUPER();

      // TODO: Have work with 'extends'.
      var mName = model ? model.id + '.' : '';

      // List of properties which are hidden by other properties.
      var es = [
        [ 'setter',     [ 'adapt', 'preSet', 'postSet' ] ],
        [ 'getter',     [ 'factory', 'expression', 'value' ] ],
        [ 'factory',    [ 'expression', 'value' ] ],
        [ 'expression', [ 'value' ] ]
      ];

      for ( var i = 0 ; i < es.length ; i++ ) {
        var e = es[i];
        for ( var j = 0 ; j < e.length ; j++ ) {
          if ( this[e[0]] && this.hasOwnProperty(e[1][j]) ) {
            console.warn('Property ' + mName + this.name + ' "' + e[1][j] + '" hidden by "' + e[0] + '"');
          }
        }
      }
    },

    function validateClass(cls) {
      // Validate that expressions only depend on known Axioms with Slots
      if ( this.expression ) {
        var expression = this.expression;
        var pName = cls.id + '.' + this.name + '.expression: ';

        var argNames = foam.Function.argsArray(expression);
        for ( var i = 0 ; i < argNames.length ; i++ ) {
          var name  = argNames[i];
          var axiom = cls.getAxiomByName(name);

          console.assert(
              axiom,
              'Unknown argument "', name, '" in ', pName, expression);
          console.assert(
              axiom.toSlot,
              'Non-Slot argument "', name, '" in ', pName, expression);
        }
      }
    }
  ]
});

/* Add describe() support to classes. */
foam.AbstractClass.describe = function(opt_name) {
  console.log('CLASS:  ', this.name);
  console.log('extends:', this.model_.extends);
  console.log('Axiom Type           Source Class   Name');
  console.log('----------------------------------------------------');
  for ( var key in this.axiomMap_ ) {
    var a = this.axiomMap_[key];
    console.log(
      foam.String.pad(a.cls_ ? a.cls_.name : 'anonymous', 20),
      foam.String.pad(a.sourceCls_.name, 14),
      a.name);
  }
  console.log('\n');
};

// Decorate installModel() to verify that axiom names aren't duplicated.
foam.AbstractClass.installModel = function() {
  var superInstallModel = foam.AbstractClass.installModel;

  return function(m) {
    var names = {};

    for ( var i = 0 ; i < m.axioms_.length ; i++ ) {
      var a = m.axioms_[i];

      foam.assert(
        ! names.hasOwnProperty(a.name),
        'Axiom name conflict in', m.id || m.refines, ':', a.name);

      var prevA    = this.getAxiomByName(a.name);
      var Property = foam.core.Property;
      if ( prevA && prevA.cls_ !== a.cls_ &&
          ! ( a.cls_ === Property && Property.isSubClass(prevA.cls_) )
      ) {
        var prevCls = prevA.cls_ ? prevA.cls_.id : 'anonymous';
        var aCls    = a.cls_     ? a.cls_.id     : 'anonymous';

        if ( Property.isSubClass(prevA.cls_) && ! Property.isSubClass(a.cls_) ) {
          throw 'Illegal to change Property to non-Property: ' +
            this.id + '.' +
            a.name +
            ' changed to ' +
            aCls;
        } else {
          console.warn(
            'Change of Axiom ' +
            this.id + '.' +
            a.name +
            ' type from ' +
            prevCls +
            ' to ' +
            aCls);
        }
      }

      names[a.name] = a;
    }

    superInstallModel.call(this, m);
  };
}();

foam.AbstractClass.validate = function() {
  for ( var key in this.axiomMap_ ) {
    var a = this.axiomMap_[key];
    a.validateClass && a.validateClass(this);
  }
}

/* Add describe() support to objects. */
foam.CLASS({
  refines: 'foam.core.FObject',

  methods: [
    function unknownArg(key, value) {
      if ( key == 'class' ) return;
      console.warn('Unknown property ' + this.cls_.id + '.' + key + ':', value);
    },

    function describe(opt_name) {
      console.log('Instance of', this.cls_.name);
      console.log('Axiom Type           Name           Value');
      console.log('----------------------------------------------------');
      var ps = this.cls_.getAxiomsByClass(foam.core.Property);
      for ( var i = 0 ; i < ps.length ; i++ ) {
        var p = ps[i];
        console.log(
          foam.String.pad(p.cls_ ? p.cls_.name : 'anonymous', 20),
          foam.String.pad(p.name, 14),
          this[p.name]);
      }
      console.log('\n');
    }
  ]
});

/* Add describe support to contexts. */
foam.X.describe = function() {
  console.log('Context:', this.hasOwnProperty('NAME') ? this.NAME : ('anonymous ' + this.$UID));
  console.log('KEY                  Type           Value');
  console.log('----------------------------------------------------');
  for ( var key in this ) {
    var value = this[key];
    var type = foam.core.FObject.isInstance(value) ? value.cls_.name : typeof value;
    console.log(
      foam.String.pad(key,  20),
      foam.String.pad(type, 14),
      typeof value === 'string' || typeof value === 'number' ? value : '');
  }
  console.log('\n');
};

foam.CLASS({
  package: 'foam.debug',
  name: 'Window',

  // documentation: 'Decorated merged() and framed() to have debug friendly toString() methods.',

  exports: [ 'merged', 'framed' ],

  methods: [
    function merged(l, opt_delay) {
      var f = this.X.merged(l, opt_delay);
      f.toString = function() {
        return 'MERGED(' + delay + ', ' + listener.$UID + ', ' + listener + ')';
      };
      return f;
    },
    function framed(l) {
      var f = this.X.framed(l);
      f.toString = function() {
        return 'ANIMATE(' + l.$UID + ', ' + l + ')';
      };
      return f;
    }
  ]
});

foam.X = foam.debug.Window.create(null, foam.X).Y;

foam.CLASS({
  package: 'foam.core',
  name: 'Argument',

  // documentation: "Describes one argument of a function.",

  constants: {
    PREFIX: 'Argument',
  },

  properties: [
    {
      /** The name of the argument */
      name: 'name'
    },
    {
      /** The string name of the type (either a model name or javascript typeof name) */
      name: 'typeName'
    },
    {
      /** If set, this holds the actual Model represented by typeName. */
      name: 'type'
    },
    {
      /** If true, indicates that this argument is optional. */
      name: 'optional', value: false
    },
    {
      /** The index of the argument (the first argument is at index 0). */
      name: 'index', value: -1
    },
    {
      /** The documentation associated with the argument (denoted by a // ) */
      name: 'documentation', value: ''
    }
  ],

  methods: [
    /** Validates the given argument against this type information.
        If any type checks are failed, a TypeError is thrown.
         */
    function validate(/* any // the argument value to validate. */ arg) {
      i = ( this.index >= 0 ) ? ' '+this.index+', ' : ', ';
      // optional check
      if ( ( ! arg ) && typeof arg === 'undefined' ) {
        if ( ! this.optional ) {
          throw new TypeError(this.PREFIX + i + this.name+', is not optional, but was undefined in a function call');
        } else {
          return; // value is undefined, but ok with that
        }
      }
      // type this for non-modelled types (no model, but a type name specified)
      if ( ! this.type ) {
        if ( this.typeName
            && typeof arg !== this.typeName
            && ! ( this.typeName === 'array' && Array.isArray(arg) ) ) {
          throw new TypeError(this.PREFIX + i + this.name + ', expected type ' + this.typeName + ' but passed ' + (typeof arg));
        } // else no this: no type, no typeName
      } else {
        // have a modelled type
        if ( ! arg.cls_ || ! this.type.isInstance(arg) ) {   // TODO: .cls_ check in isInstance() instead?
          var gotType = (arg.cls_) ? arg.cls_.name : typeof arg;
          throw new TypeError(this.PREFIX + i + this.name + ', expected type ' + this.typeName + ' but passed ' + gotType);
        }
      }
    }
  ]
});

/** Describes a function return type. */
foam.CLASS({
  package: 'foam.core',
  name: 'ReturnValue',
  extends: 'foam.core.Argument',

  constants: {
    PREFIX: 'Return'
  }
});

/** The types library deals with type safety. */
foam.LIB({
  name: 'foam.types',

  methods: [
    function getFunctionArgs(fn) {
      /** Extracts the arguments and their types from the given function.
        * @arg fn The function to extract from. The toString() of the function must be
        *     accurate.
        * @return An array of Argument objects.
        */
      // strip newlines and find the function(...) declaration
      var args = foam.Function.argsStr(fn);
      if ( ! args ) return [];
      args += ','; // easier matching

      var ret = [];
      // check each arg for types
      // Optional commented type(incl. dots for packages), argument name, optional commented return type
      // ws [/* ws package.type? ws */] ws argname ws [/* ws retType ws */]
      //console.log('-----------------');
      var argIdx = 0;
      var argMatcher = /(\s*\/\*\s*([\w._$]+)(\?)?\s*(\/\/\s*(.*?))?\s*\*\/)?\s*([\w_$]+)\s*(\/\*\s*([\w._$]+)\s*\*\/)?\s*\,+/g;
      var typeMatch;
      while ( typeMatch = argMatcher.exec(args) ) {
        // if can't match from start of string, fail
        if ( argIdx == 0 && typeMatch.index > 0 ) break;

        ret.push(foam.core.Argument.create({
          name:          typeMatch[6],
          typeName:      typeMatch[2],
          type:          global[typeMatch[2]],
          optional:      typeMatch[3] == '?',
          index:        argIdx++,
          documentation: typeMatch[5],
        }));
        // TODO: this is only valid on the last arg
        if ( typeMatch[6] ) {
          ret.returnType = foam.core.ReturnValue.create({
            typeName: typeMatch[8],
            type: global[typeMatch[8]]
          });
        }
      }
      if ( argIdx == 0 ) {
        // check for bare return type with no args
        typeMatch = args.match(/^\s*\/\*\s*([\w._$]+)\s*\*\/\s*/);
        if ( typeMatch && typeMatch[1] ) {
          ret.returnType = foam.core.ReturnValue.create({
            typeName: typeMatch[1],
            type: global[typeMatch[1]]
          });
        } else {
          throw "foam.types.getFunctionArgs argument parsing error: " + args.toString();
        }
      }

      return ret;
    },

    /** Decorates the given function with a runtime type checker.
      * Types should be denoted before each argument:
      * function(\/\*TypeA\*\/ argA, \/\*string\*\/ argB) { ... }
      * Types are either the names of Models (i.e. declared with CLASS), or
      * javascript primitives as returned by 'typeof'. In addition, 'array'
      * is supported as a special case, corresponding to an Array.isArray() check.
      * @fn The function to decorate. The toString() of the function must be
      *     accurate.
      * @return A new function that will throw errors if arguments
      *         doesn't match the declared types, run the original function,
      *         then type check the returned value.
      */
    function typeCheck(fn) {
      // parse out the arguments and their types
      var args = foam.types.getFunctionArgs(fn);
      var ret = function() {
        // check each declared argument, arguments[i] can be undefined for missing optional args,
        // extra arguments are ok
        for ( var i = 0 ; i < args.length ; i++ )
          args[i].validate(arguments[i]);

        // If nothing threw an exception, we are free to run the function
        var retVal = fn.apply(this, arguments);

        // check the return value
        if ( args.returnType ) args.returnType.validate(retVal);

        return retVal;
      }

      // keep the old value of toString (hide the decorator)
      ret.toString = function() { return fn.toString(); }

      return ret;
    }
  ]
});
<commit_msg>Don't warn about upgrading an Axiom from anonymous.<commit_after>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.
 */

/*
 * Debug.js
 *
 * This file contains refinements and replacements designed to make
 * FOAM apps easier to debug. Things like more informative toString() methods
 * .describe() on various types of objects, extra type checking, warnings,
 * and asserts, etc. Many of these features may negatively affect performance,
 * so while this file should be loaded during your day-to-day development,
 * it should not be included in production.
 */

foam.assert = function assert(cond /*, args */) {
  if ( ! cond ) {
    var msg = Array.prototype.slice.call(arguments, 1).join(' ');
    console.assert(false, msg);
  }
};

/* Validating a Model should also validate all of its Axioms. */
foam.CLASS({
  refines: 'foam.core.Model',

  methods: [
    function validate() {
      this.SUPER();

      if ( this.hasOwnProperty('extends') && this.refines )
        throw this.id + ': "extends" and "refines" are mutually exclusive.';

      for ( var i = 0 ; i < this.axioms_.length ; i++ )
        this.axioms_[i].validate && this.axioms_[i].validate(this);
    }
  ]
});

/* Validating a Model should also validate all of its Axioms. */
foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    function validate(model) {
      this.SUPER();

      // TODO: Have work with 'extends'.
      var mName = model ? model.id + '.' : '';

      // List of properties which are hidden by other properties.
      var es = [
        [ 'setter',     [ 'adapt', 'preSet', 'postSet' ] ],
        [ 'getter',     [ 'factory', 'expression', 'value' ] ],
        [ 'factory',    [ 'expression', 'value' ] ],
        [ 'expression', [ 'value' ] ]
      ];

      for ( var i = 0 ; i < es.length ; i++ ) {
        var e = es[i];
        for ( var j = 0 ; j < e.length ; j++ ) {
          if ( this[e[0]] && this.hasOwnProperty(e[1][j]) ) {
            console.warn('Property ' + mName + this.name + ' "' + e[1][j] + '" hidden by "' + e[0] + '"');
          }
        }
      }
    },

    function validateClass(cls) {
      // Validate that expressions only depend on known Axioms with Slots
      if ( this.expression ) {
        var expression = this.expression;
        var pName = cls.id + '.' + this.name + '.expression: ';

        var argNames = foam.Function.argsArray(expression);
        for ( var i = 0 ; i < argNames.length ; i++ ) {
          var name  = argNames[i];
          var axiom = cls.getAxiomByName(name);

          console.assert(
              axiom,
              'Unknown argument "', name, '" in ', pName, expression);
          console.assert(
              axiom.toSlot,
              'Non-Slot argument "', name, '" in ', pName, expression);
        }
      }
    }
  ]
});

/* Add describe() support to classes. */
foam.AbstractClass.describe = function(opt_name) {
  console.log('CLASS:  ', this.name);
  console.log('extends:', this.model_.extends);
  console.log('Axiom Type           Source Class   Name');
  console.log('----------------------------------------------------');
  for ( var key in this.axiomMap_ ) {
    var a = this.axiomMap_[key];
    console.log(
      foam.String.pad(a.cls_ ? a.cls_.name : 'anonymous', 20),
      foam.String.pad(a.sourceCls_.name, 14),
      a.name);
  }
  console.log('\n');
};

// Decorate installModel() to verify that axiom names aren't duplicated.
foam.AbstractClass.installModel = function() {
  var superInstallModel = foam.AbstractClass.installModel;

  return function(m) {
    var names = {};

    for ( var i = 0 ; i < m.axioms_.length ; i++ ) {
      var a = m.axioms_[i];

      foam.assert(
        ! names.hasOwnProperty(a.name),
        'Axiom name conflict in', m.id || m.refines, ':', a.name);

      var prevA    = this.getAxiomByName(a.name);
      var Property = foam.core.Property;
      if ( prevA && prevA.cls_ !== a.cls_ &&
          ! ( a.cls_ === Property && Property.isSubClass(prevA.cls_) )
      ) {
        var prevCls = prevA.cls_ ? prevA.cls_.id : 'anonymous';
        var aCls    = a.cls_     ? a.cls_.id     : 'anonymous';

        if ( Property.isSubClass(prevA.cls_) && ! Property.isSubClass(a.cls_) ) {
          throw 'Illegal to change Property to non-Property: ' +
            this.id + '.' +
            a.name +
            ' changed to ' +
            aCls;
        } else if ( prevA.cls_ ) {
          console.warn(
            'Change of Axiom ' +
            this.id + '.' +
            a.name +
            ' type from ' +
            prevCls +
            ' to ' +
            aCls);
        }
      }

      names[a.name] = a;
    }

    superInstallModel.call(this, m);
  };
}();

foam.AbstractClass.validate = function() {
  for ( var key in this.axiomMap_ ) {
    var a = this.axiomMap_[key];
    a.validateClass && a.validateClass(this);
  }
}

/* Add describe() support to objects. */
foam.CLASS({
  refines: 'foam.core.FObject',

  methods: [
    function unknownArg(key, value) {
      if ( key == 'class' ) return;
      console.warn('Unknown property ' + this.cls_.id + '.' + key + ':', value);
    },

    function describe(opt_name) {
      console.log('Instance of', this.cls_.name);
      console.log('Axiom Type           Name           Value');
      console.log('----------------------------------------------------');
      var ps = this.cls_.getAxiomsByClass(foam.core.Property);
      for ( var i = 0 ; i < ps.length ; i++ ) {
        var p = ps[i];
        console.log(
          foam.String.pad(p.cls_ ? p.cls_.name : 'anonymous', 20),
          foam.String.pad(p.name, 14),
          this[p.name]);
      }
      console.log('\n');
    }
  ]
});

/* Add describe support to contexts. */
foam.X.describe = function() {
  console.log('Context:', this.hasOwnProperty('NAME') ? this.NAME : ('anonymous ' + this.$UID));
  console.log('KEY                  Type           Value');
  console.log('----------------------------------------------------');
  for ( var key in this ) {
    var value = this[key];
    var type = foam.core.FObject.isInstance(value) ? value.cls_.name : typeof value;
    console.log(
      foam.String.pad(key,  20),
      foam.String.pad(type, 14),
      typeof value === 'string' || typeof value === 'number' ? value : '');
  }
  console.log('\n');
};

foam.CLASS({
  package: 'foam.debug',
  name: 'Window',

  // documentation: 'Decorated merged() and framed() to have debug friendly toString() methods.',

  exports: [ 'merged', 'framed' ],

  methods: [
    function merged(l, opt_delay) {
      var f = this.X.merged(l, opt_delay);
      f.toString = function() {
        return 'MERGED(' + delay + ', ' + listener.$UID + ', ' + listener + ')';
      };
      return f;
    },
    function framed(l) {
      var f = this.X.framed(l);
      f.toString = function() {
        return 'ANIMATE(' + l.$UID + ', ' + l + ')';
      };
      return f;
    }
  ]
});

foam.X = foam.debug.Window.create(null, foam.X).Y;

foam.CLASS({
  package: 'foam.core',
  name: 'Argument',

  // documentation: "Describes one argument of a function.",

  constants: {
    PREFIX: 'Argument',
  },

  properties: [
    {
      /** The name of the argument */
      name: 'name'
    },
    {
      /** The string name of the type (either a model name or javascript typeof name) */
      name: 'typeName'
    },
    {
      /** If set, this holds the actual Model represented by typeName. */
      name: 'type'
    },
    {
      /** If true, indicates that this argument is optional. */
      name: 'optional', value: false
    },
    {
      /** The index of the argument (the first argument is at index 0). */
      name: 'index', value: -1
    },
    {
      /** The documentation associated with the argument (denoted by a // ) */
      name: 'documentation', value: ''
    }
  ],

  methods: [
    /** Validates the given argument against this type information.
        If any type checks are failed, a TypeError is thrown.
         */
    function validate(/* any // the argument value to validate. */ arg) {
      i = ( this.index >= 0 ) ? ' '+this.index+', ' : ', ';
      // optional check
      if ( ( ! arg ) && typeof arg === 'undefined' ) {
        if ( ! this.optional ) {
          throw new TypeError(this.PREFIX + i + this.name+', is not optional, but was undefined in a function call');
        } else {
          return; // value is undefined, but ok with that
        }
      }
      // type this for non-modelled types (no model, but a type name specified)
      if ( ! this.type ) {
        if ( this.typeName
            && typeof arg !== this.typeName
            && ! ( this.typeName === 'array' && Array.isArray(arg) ) ) {
          throw new TypeError(this.PREFIX + i + this.name + ', expected type ' + this.typeName + ' but passed ' + (typeof arg));
        } // else no this: no type, no typeName
      } else {
        // have a modelled type
        if ( ! arg.cls_ || ! this.type.isInstance(arg) ) {   // TODO: .cls_ check in isInstance() instead?
          var gotType = (arg.cls_) ? arg.cls_.name : typeof arg;
          throw new TypeError(this.PREFIX + i + this.name + ', expected type ' + this.typeName + ' but passed ' + gotType);
        }
      }
    }
  ]
});

/** Describes a function return type. */
foam.CLASS({
  package: 'foam.core',
  name: 'ReturnValue',
  extends: 'foam.core.Argument',

  constants: {
    PREFIX: 'Return'
  }
});

/** The types library deals with type safety. */
foam.LIB({
  name: 'foam.types',

  methods: [
    function getFunctionArgs(fn) {
      /** Extracts the arguments and their types from the given function.
        * @arg fn The function to extract from. The toString() of the function must be
        *     accurate.
        * @return An array of Argument objects.
        */
      // strip newlines and find the function(...) declaration
      var args = foam.Function.argsStr(fn);
      if ( ! args ) return [];
      args += ','; // easier matching

      var ret = [];
      // check each arg for types
      // Optional commented type(incl. dots for packages), argument name, optional commented return type
      // ws [/* ws package.type? ws */] ws argname ws [/* ws retType ws */]
      //console.log('-----------------');
      var argIdx = 0;
      var argMatcher = /(\s*\/\*\s*([\w._$]+)(\?)?\s*(\/\/\s*(.*?))?\s*\*\/)?\s*([\w_$]+)\s*(\/\*\s*([\w._$]+)\s*\*\/)?\s*\,+/g;
      var typeMatch;
      while ( typeMatch = argMatcher.exec(args) ) {
        // if can't match from start of string, fail
        if ( argIdx == 0 && typeMatch.index > 0 ) break;

        ret.push(foam.core.Argument.create({
          name:          typeMatch[6],
          typeName:      typeMatch[2],
          type:          global[typeMatch[2]],
          optional:      typeMatch[3] == '?',
          index:        argIdx++,
          documentation: typeMatch[5],
        }));
        // TODO: this is only valid on the last arg
        if ( typeMatch[6] ) {
          ret.returnType = foam.core.ReturnValue.create({
            typeName: typeMatch[8],
            type: global[typeMatch[8]]
          });
        }
      }
      if ( argIdx == 0 ) {
        // check for bare return type with no args
        typeMatch = args.match(/^\s*\/\*\s*([\w._$]+)\s*\*\/\s*/);
        if ( typeMatch && typeMatch[1] ) {
          ret.returnType = foam.core.ReturnValue.create({
            typeName: typeMatch[1],
            type: global[typeMatch[1]]
          });
        } else {
          throw "foam.types.getFunctionArgs argument parsing error: " + args.toString();
        }
      }

      return ret;
    },

    /** Decorates the given function with a runtime type checker.
      * Types should be denoted before each argument:
      * function(\/\*TypeA\*\/ argA, \/\*string\*\/ argB) { ... }
      * Types are either the names of Models (i.e. declared with CLASS), or
      * javascript primitives as returned by 'typeof'. In addition, 'array'
      * is supported as a special case, corresponding to an Array.isArray() check.
      * @fn The function to decorate. The toString() of the function must be
      *     accurate.
      * @return A new function that will throw errors if arguments
      *         doesn't match the declared types, run the original function,
      *         then type check the returned value.
      */
    function typeCheck(fn) {
      // parse out the arguments and their types
      var args = foam.types.getFunctionArgs(fn);
      var ret = function() {
        // check each declared argument, arguments[i] can be undefined for missing optional args,
        // extra arguments are ok
        for ( var i = 0 ; i < args.length ; i++ )
          args[i].validate(arguments[i]);

        // If nothing threw an exception, we are free to run the function
        var retVal = fn.apply(this, arguments);

        // check the return value
        if ( args.returnType ) args.returnType.validate(retVal);

        return retVal;
      }

      // keep the old value of toString (hide the decorator)
      ret.toString = function() { return fn.toString(); }

      return ret;
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * 事件辅助类
 * @module zrender/core/event
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 */
define(function(require) {

    'use strict';

    var Eventful = require('../mixin/Eventful');

    var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
    /**
     * 如果存在第三方嵌入的一些dom触发的事件，或touch事件，需要转换一下事件坐标
     */
    function normalizeEvent(el, e) {

        e = e || window.event;

        if (e.zrX != null) {
            return e;
        }

        var eventType = e.type;
        var isTouch = eventType && eventType.indexOf('touch') >= 0;

        if (!isTouch) {
            // https://gist.github.com/electricg/4435259
            var mouseX = 0;
            var mouseY = 0;

            if (e.pageX || e.pageY) {
                mouseX = e.pageX;
                mouseY = e.pageY;
            }
            else {
                mouseX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
            }

            var elLeft = 0;
            var elTop = 0;
            while (el.offsetParent) {
                elLeft += el.offsetLeft;
                elTop += el.offsetTop;

                el = el.offsetParent;
            }

            e.zrX = mouseX - elLeft;
            e.zrY = mouseY - elTop;
            e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
        }
        else {
            var touch = eventType != 'touchend'
                            ? e.targetTouches[0]
                            : e.changedTouches[0];
            if (touch) {
                var rBounding = el.getBoundingClientRect();
                // touch事件坐标是全屏的~
                e.zrX = touch.clientX - rBounding.left;
                e.zrY = touch.clientY - rBounding.top;
            }
        }

        return e;
    }

    function addEventListener(el, name, handler) {
        if (isDomLevel2) {
            el.addEventListener(name, handler);
        }
        else {
            el.attachEvent('on' + name, handler);
        }
    }

    function removeEventListener(el, name, handler) {
        if (isDomLevel2) {
            el.removeEventListener(name, handler);
        }
        else {
            el.detachEvent('on' + name, handler);
        }
    }

    /**
     * 停止冒泡和阻止默认行为
     * @memberOf module:zrender/core/event
     * @method
     * @param {Event} e : event对象
     */
    var stop = isDomLevel2
        ? function (e) {
            e.preventDefault();
            e.stopPropagation();
            e.cancelBubble = true;
        }
        : function (e) {
            e.returnValue = false;
            e.cancelBubble = true;
        };

    return {
        normalizeEvent: normalizeEvent,
        addEventListener: addEventListener,
        removeEventListener: removeEventListener,

        stop: stop,
        // 做向上兼容
        Dispatcher: Eventful
    };
});
<commit_msg>Element position relative to page use jquery code<commit_after>/**
 * 事件辅助类
 * @module zrender/core/event
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 */
define(function(require) {

    'use strict';

    var Eventful = require('../mixin/Eventful');

    var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;

    function getBoundingClientRect(el) {
        // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
        return el.getBoundingClientRect ? el.getBoundingClientRect() : { left: 0, top: 0};
    }
    /**
     * 如果存在第三方嵌入的一些dom触发的事件，或touch事件，需要转换一下事件坐标
     */
    function normalizeEvent(el, e) {

        e = e || window.event;

        if (e.zrX != null) {
            return e;
        }

        var eventType = e.type;
        var isTouch = eventType && eventType.indexOf('touch') >= 0;

        if (!isTouch) {
            // https://gist.github.com/electricg/4435259
            var mouseX = 0;
            var mouseY = 0;

            if (e.pageX || e.pageY) {
                mouseX = e.pageX;
                mouseY = e.pageY;
            }
            else {
                mouseX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
            }

            var box = getBoundingClientRect(el);
            var top = box.top + (window.pageYOffset || el.scrollTop) - (el.clientTop || 0);
            var left = box.left + (window.pageXOffset || el.scrollLeft) - (el.clientLeft || 0);
            e.zrX = mouseX - left;
            e.zrY = mouseY - top;
            e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
        }
        else {
            var touch = eventType != 'touchend'
                            ? e.targetTouches[0]
                            : e.changedTouches[0];
            if (touch) {
                var rBounding = getBoundingClientRect(el);
                // touch事件坐标是全屏的~
                e.zrX = touch.clientX - rBounding.left;
                e.zrY = touch.clientY - rBounding.top;
            }
        }

        return e;
    }

    function addEventListener(el, name, handler) {
        if (isDomLevel2) {
            el.addEventListener(name, handler);
        }
        else {
            el.attachEvent('on' + name, handler);
        }
    }

    function removeEventListener(el, name, handler) {
        if (isDomLevel2) {
            el.removeEventListener(name, handler);
        }
        else {
            el.detachEvent('on' + name, handler);
        }
    }

    /**
     * 停止冒泡和阻止默认行为
     * @memberOf module:zrender/core/event
     * @method
     * @param {Event} e : event对象
     */
    var stop = isDomLevel2
        ? function (e) {
            e.preventDefault();
            e.stopPropagation();
            e.cancelBubble = true;
        }
        : function (e) {
            e.returnValue = false;
            e.cancelBubble = true;
        };

    return {
        normalizeEvent: normalizeEvent,
        addEventListener: addEventListener,
        removeEventListener: removeEventListener,

        stop: stop,
        // 做向上兼容
        Dispatcher: Eventful
    };
});
<|endoftext|>


<commit_before>(function() {

  function Queue() {

    // callback storage
    this._methods = [];

    // reference to the response
    this._response = null;

    // all queues start off unflushed
    this._flushed = false;

  };

  Queue.prototype = {

    // adds callbacks to the queue
    add: function(fn) {

      // if the queue had been flushed, return immediately
      if (this._flushed) {

        // otherwise push it on the queue
        fn(this._response);

      } else {
        this._methods.push(fn);
      }

    },

    flush: function(resp) {

      // flush only ever happens once
      if (this._flushed) {
        return;
      }

      // store the response for subsequent calls after flush()
      this._response = resp;

      // mark that it's been flushed
      this._flushed = true;

      // shift 'em out and call 'em back
      while (this._methods[0]) {
        this._methods.shift()(resp);
      }

    }

  };

  ImageModel = cdb.core.Model.extend({
    defaults: {
      format: "png",
      zoom: 10,
      center: [0, 0],
      size:  [320, 240],
      tiler_port: 80,
      tiler_domain: "cartodb.com"
    }
  });

  var Image = function() {

    Map.call(this, this);

    this.model = new ImageModel();

    this.supported_formats = ["png", "jpg"];

    this.defaults = {
      tiler_domain: "cartodb.com",
      tiler_port: "80"
    };

    this.available_basemaps = ["light_all", "light_nolabels", "dark_all", "dark_nolabels"];

  };

  Image.prototype = _.extend({}, Map.prototype, {

    load: function(vizjson, options) {

      _.bindAll(this, "_onVisLoaded");

      this.queue = new Queue;

      options = _.defaults(options, { vizjson: vizjson, temp_id: "s" + this._getUUID() }, this.model.defaults);

      this.model.set(options);

      cdb.vis.Loader.get(vizjson, this._onVisLoaded);

      return this;

    },

    loadLayerDefinition: function(layerDefinition) {

      var self = this;

      this.queue = new Queue;

      if (!layerDefinition.user_name) {
        cartodb.log.error("Please, specify the username");
        return;
      }

      this.options.user_name      = layerDefinition.user_name;
      this.options.tiler_protocol = layerDefinition.tiler_protocol;
      this.options.tiler_domain   = layerDefinition.tiler_domain;
      this.options.tiler_port     = layerDefinition.tiler_port;
      this.endPoint = "/api/v1/map";

      this.options.layers = layerDefinition;

      this._requestLayerGroupID();

    },

    _onVisLoaded: function(data) {

      if (data) {

        var layerDefinition;
        var baseLayer = data.layers[0];
        var dataLayer = data.layers[1];

        this._chooseBasemap(baseLayer.options);

        this.options.user_name      = dataLayer.options.user_name;
        this.options.tiler_protocol = dataLayer.options.tiler_protocol;
        this.options.tiler_domain   = dataLayer.options.tiler_domain;
        this.options.tiler_port     = dataLayer.options.tiler_port;

        this.endPoint = "/api/v1/map";

        this.model.set({
          zoom: data.zoom,
          center: JSON.parse(data.center),
          bounds: data.bounds
        });

        if (dataLayer.type === "namedmap") {
          this.options.layers = this._getNamedmapLayerDefinition(dataLayer.options);
        } else {
          this.options.layers = this._getLayergroupLayerDefinition(dataLayer.options);
        }

        this._requestLayerGroupID();

      }

    },

    toJSON: function(){
      return this.options.layers;
    },

    _requestLayerGroupID: function() {

      var self = this;

      this._requestPOST({}, function(data) {

        if (data) {
          self.model.set("layergroupid", data.layergroupid);
          self.queue.flush(this);
        }

      });

    },

    _getBasemapLayer: function() {

      return {
        type: "http",
        options: {
          urlTemplate: "http://{s}.basemaps.cartocdn.com/" + this.model.get("basemap") + "/{z}/{x}/{y}.png",
          subdomains: [ "a", "b", "c" ]
        }
      };

    },

    _getLayergroupLayerDefinition: function(options) {

      var layerDefinition = new LayerDefinition(options.layer_definition, options);

      var ld = layerDefinition.toJSON();

      ld.layers.unshift(this._getBasemapLayer());

      return ld;

    },

    _getNamedmapLayerDefinition: function(options) {

      var layerDefinition = new NamedMap(options.named_map, options);

      layerDefinition.options.type = "named";

      var layers  =  [
        this._getBasemapLayer(), {
        type: "named",
        options: {
          name: layerDefinition.named_map.name
        }
      }];

      var ld = {
        layers: layers
      };

      return ld;

    },

    _getUrl: function() {

      var username     = this.options.user_name;
      var zoom         = this.model.get("zoom");
      var bbox         = this.model.get("bbox");
      var lat          = this.model.get("center")[0];
      var lon          = this.model.get("center")[1];
      var width        = this.model.get("size")[0];
      var height       = this.model.get("size")[1];
      var layergroupid = this.model.get("layergroupid");
      var format       = this.model.get("format");

      var url = this._tilerHost() + this.endPoint;

      if (bbox) {
        return [url, "static/bbox" , layergroupid, bbox[0].join(",") + "," + bbox[1].join(","), width, height + "." + format].join("/");
      } else {
        return [url, "static/center" , layergroupid, zoom, lat, lon, width, height + "." + format].join("/");
      }

    },

    _chooseBasemap: function(basemap_layer) {

      if (this.model.get("basemap")) return;

      var type = basemap_layer.base_type;

      if (!_.include(this.available_basemaps, type)) {

        if (type && type.indexOf("toner") !== -1)      basemap = "dark_all";
        else if (type && type.indexOf("dark")  !== -1) basemap = "dark_all";
        else if (type && type.indexOf("night") !== -1) basemap = "dark_all";
        else if (type && type.indexOf("night") !== -1) basemap = "dark_all";
        else if (type && type.indexOf("light") !== -1) basemap = "light_all";
        else basemap = "light_all";

        this.model.set("basemap", basemap);

      }

    },

    // Generates a random string
    _getUUID: function() {
      var S4 = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
      };
      return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
    },

    /* Setters */
    _set: function(name, value) {

      var self = this;

      this.queue.add(function() {
        self.model.set(name,value);
      });

      return this;

    },

    zoom: function(zoom) {
      return this._set("zoom", zoom);
    },

    bbox: function(bbox) {
      return this._set("bbox", bbox);
    },

    center: function(center) {
      return this._set("center", center);
    },

    format: function(format) {
      return this._set("format", _.include(this.supported_formats, format) ? format : this.model.defaults.format);
    },

    size: function(width, height) {
      return this._set("size", [width, height === undefined ? width : height]);
    },

    /* Methods */

    /* Image.into(HTMLImageElement)
       inserts the image in the HTMLImageElement specified */
    into: function(img) {

      var self = this;

      if (!(img instanceof HTMLImageElement)) {
        cartodb.log.error("img should be an image");
        return;
      }

      this.model.set("size", [img.width, img.height]);

      this.queue.add(function(response) {
        img.src = self._getUrl();
      });

    },

    /* Image.getUrl(callback(err, url))
       gets the url for the image, err is null is there was no error */

    getUrl: function(callback) {

      var self = this;

      this.queue.add(function() {
        if (callback) {
          callback(null, self._getUrl()); // TODO: return the error
        }
      });

    },

    /* Image.write(attributes)
       adds a img tag in the same place script is executed */
      // TODO: document class, id and src attributes

    write: function(attributes) {

      var self = this;

      this.model.set("attributes", attributes);

      if (attributes && attributes.src) {
        document.write('');
      } else {
        document.write('');
      }

      this.queue.add(function() {

        var element = document.getElementById(self.model.get("temp_id"));

        element.src = self._getUrl();
        element.removeAttribute("temp_id");

        var attributes = self.model.get("attributes");

        if (attributes && attributes.class) { element.setAttribute("class", attributes.class); }
        if (attributes && attributes.id)    { element.setAttribute("id", attributes.id); }

      });

      return this;
    }

  })

  cdb.Image = function(data, options) {

    if (!options) options = {};

    var image = new Image();

    if (typeof data === 'string') {
      image.load(data, options);
    } else {
      image.loadLayerDefinition(data);
    }

    return image;

  };

})();
<commit_msg>we use the bounding box by default<commit_after>(function() {

  function Queue() {

    // callback storage
    this._methods = [];

    // reference to the response
    this._response = null;

    // all queues start off unflushed
    this._flushed = false;

  };

  Queue.prototype = {

    // adds callbacks to the queue
    add: function(fn) {

      // if the queue had been flushed, return immediately
      if (this._flushed) {

        // otherwise push it on the queue
        fn(this._response);

      } else {
        this._methods.push(fn);
      }

    },

    flush: function(resp) {

      // flush only ever happens once
      if (this._flushed) {
        return;
      }

      // store the response for subsequent calls after flush()
      this._response = resp;

      // mark that it's been flushed
      this._flushed = true;

      // shift 'em out and call 'em back
      while (this._methods[0]) {
        this._methods.shift()(resp);
      }

    }

  };

  ImageModel = cdb.core.Model.extend({
    defaults: {
      format: "png",
      zoom: 10,
      center: [0, 0],
      size:  [320, 240],
      tiler_port: 80,
      tiler_domain: "cartodb.com"
    }
  });

  var Image = function() {

    Map.call(this, this);

    this.model = new ImageModel();

    this.supported_formats = ["png", "jpg"];

    this.defaults = {
      tiler_domain: "cartodb.com",
      tiler_port: "80"
    };

    this.available_basemaps = ["light_all", "light_nolabels", "dark_all", "dark_nolabels"];

  };

  Image.prototype = _.extend({}, Map.prototype, {

    load: function(vizjson, options) {

      _.bindAll(this, "_onVisLoaded");

      this.queue = new Queue;

      options = _.defaults(options, { vizjson: vizjson, temp_id: "s" + this._getUUID() }, this.model.defaults);

      this.model.set(options);

      cdb.vis.Loader.get(vizjson, this._onVisLoaded);

      return this;

    },

    loadLayerDefinition: function(layerDefinition) {

      var self = this;

      this.queue = new Queue;

      if (!layerDefinition.user_name) {
        cartodb.log.error("Please, specify the username");
        return;
      }

      this.options.user_name      = layerDefinition.user_name;
      this.options.tiler_protocol = layerDefinition.tiler_protocol;
      this.options.tiler_domain   = layerDefinition.tiler_domain;
      this.options.tiler_port     = layerDefinition.tiler_port;
      this.endPoint = "/api/v1/map";

      this.options.layers = layerDefinition;

      this._requestLayerGroupID();

    },

    _onVisLoaded: function(data) {

      if (data) {

        var layerDefinition;
        var baseLayer = data.layers[0];
        var dataLayer = data.layers[1];

        this._chooseBasemap(baseLayer.options);

        this.options.user_name      = dataLayer.options.user_name;
        this.options.tiler_protocol = dataLayer.options.tiler_protocol;
        this.options.tiler_domain   = dataLayer.options.tiler_domain;
        this.options.tiler_port     = dataLayer.options.tiler_port;

        this.endPoint = "/api/v1/map";

        var bbox = [];

        bbox.push([data.bounds[0][1], data.bounds[0][0]]);
        bbox.push([data.bounds[1][1], data.bounds[1][0]]);

        this.model.set({
          zoom: data.zoom,
          center: JSON.parse(data.center),
          bbox: bbox,
          bounds: data.bounds
        });

        if (dataLayer.type === "namedmap") {
          this.options.layers = this._getNamedmapLayerDefinition(dataLayer.options);
        } else {
          this.options.layers = this._getLayergroupLayerDefinition(dataLayer.options);
        }

        this._requestLayerGroupID();

      }

    },

    toJSON: function(){
      return this.options.layers;
    },

    _requestLayerGroupID: function() {

      var self = this;

      this._requestPOST({}, function(data) {

        if (data) {
          self.model.set("layergroupid", data.layergroupid);
          self.queue.flush(this);
        }

      });

    },

    _getBasemapLayer: function() {

      return {
        type: "http",
        options: {
          urlTemplate: "http://{s}.basemaps.cartocdn.com/" + this.model.get("basemap") + "/{z}/{x}/{y}.png",
          subdomains: [ "a", "b", "c" ]
        }
      };

    },

    _getLayergroupLayerDefinition: function(options) {

      var layerDefinition = new LayerDefinition(options.layer_definition, options);

      var ld = layerDefinition.toJSON();

      // TODO: remove this
      for (var i = 0; i<ld.layers.length; i++) {
        delete ld.layers[i].options.interactivity
      }

      ld.layers.unshift(this._getBasemapLayer());

      return ld;

    },

    _getNamedmapLayerDefinition: function(options) {

      var layerDefinition = new NamedMap(options.named_map, options);

      layerDefinition.options.type = "named";

      var layers  =  [
        this._getBasemapLayer(), {
        type: "named",
        options: {
          name: layerDefinition.named_map.name
        }
      }];

      var ld = {
        layers: layers
      };

      return ld;

    },

    _getUrl: function() {

      var username     = this.options.user_name;
      var zoom         = this.model.get("zoom");
      var bbox         = this.model.get("bbox");
      var lat          = this.model.get("center")[0];
      var lon          = this.model.get("center")[1];
      var width        = this.model.get("size")[0];
      var height       = this.model.get("size")[1];
      var layergroupid = this.model.get("layergroupid");
      var format       = this.model.get("format");

      var url = this._tilerHost() + this.endPoint;

      if (bbox) {
        return [url, "static/bbox" , layergroupid, bbox[0].join(",") + "," + bbox[1].join(","), width, height + "." + format].join("/");
      } else {
        return [url, "static/center" , layergroupid, zoom, lat, lon, width, height + "." + format].join("/");
      }

    },

    _chooseBasemap: function(basemap_layer) {

      if (this.model.get("basemap")) return;

      var type = basemap_layer.base_type;

      if (!_.include(this.available_basemaps, type)) {

        if (type && type.indexOf("toner") !== -1)      basemap = "dark_all";
        else if (type && type.indexOf("dark")  !== -1) basemap = "dark_all";
        else if (type && type.indexOf("night") !== -1) basemap = "dark_all";
        else if (type && type.indexOf("night") !== -1) basemap = "dark_all";
        else if (type && type.indexOf("light") !== -1) basemap = "light_all";
        else basemap = "light_all";

        this.model.set("basemap", basemap);

      }

    },

    // Generates a random string
    _getUUID: function() {
      var S4 = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
      };
      return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
    },

    /* Setters */
    _set: function(name, value) {

      var self = this;

      this.queue.add(function() {
        self.model.set(name,value);
      });

      return this;

    },

    zoom: function(zoom) {
      return this._set("zoom", zoom);
    },

    bbox: function(bbox) {
      return this._set("bbox", bbox);
    },

    center: function(center) {
      return this._set("center", center);
    },

    format: function(format) {
      return this._set("format", _.include(this.supported_formats, format) ? format : this.model.defaults.format);
    },

    size: function(width, height) {
      return this._set("size", [width, height === undefined ? width : height]);
    },

    /* Methods */

    /* Image.into(HTMLImageElement)
       inserts the image in the HTMLImageElement specified */
    into: function(img) {

      var self = this;

      if (!(img instanceof HTMLImageElement)) {
        cartodb.log.error("img should be an image");
        return;
      }

      this.model.set("size", [img.width, img.height]);

      this.queue.add(function(response) {
        img.src = self._getUrl();
      });

    },

    /* Image.getUrl(callback(err, url))
       gets the url for the image, err is null is there was no error */

    getUrl: function(callback) {

      var self = this;

      this.queue.add(function() {
        if (callback) {
          callback(null, self._getUrl()); // TODO: return the error
        }
      });

    },

    /* Image.write(attributes)
       adds a img tag in the same place script is executed */
      // TODO: document class, id and src attributes

    write: function(attributes) {

      var self = this;

      this.model.set("attributes", attributes);

      if (attributes && attributes.src) {
        document.write('');
      } else {
        document.write('');
      }

      this.queue.add(function() {

        var element = document.getElementById(self.model.get("temp_id"));

        element.src = self._getUrl();
        element.removeAttribute("temp_id");

        var attributes = self.model.get("attributes");

        if (attributes && attributes.class) { element.setAttribute("class", attributes.class); }
        if (attributes && attributes.id)    { element.setAttribute("id", attributes.id); }

      });

      return this;
    }

  })

  cdb.Image = function(data, options) {

    if (!options) options = {};

    var image = new Image();

    if (typeof data === 'string') {
      image.load(data, options);
    } else {
      image.loadLayerDefinition(data);
    }

    return image;

  };

})();
<|endoftext|>


<commit_before>import Immutable, { List, Map, Set } from 'immutable';
import { isSmallScreen } from '../utils/media_utils';
import { endsWith } from '../utils/string_utils';
import { parseUrl } from '../utils/url_utils';
import t, { initI18n } from '../i18n'; // TODO: core should not depend on i18n
import trim from 'trim';
import * as gp from '../avatar/gravatar_provider';
import { dataFns } from '../utils/data_utils';

const {
  get,
  init,
  remove,
  reset,
  set,
  tget,
  tset,
  tremove
} = dataFns(["core"]);

export function setup(id, clientID, domain, options, logInCallback, hookRunner, emitEventFn) {
  let m = init(id, Immutable.fromJS({
    assetsUrl: extractAssetsUrlOption(options, domain),
    auth: extractAuthOptions(options),
    clientID: clientID,
    domain: domain,
    emitEventFn: emitEventFn,
    hookRunner: hookRunner,
    logInCallback: logInCallback,
    allowedConnections: Immutable.fromJS(options.allowedConnections || []),
    ui: extractUIOptions(id, options)
  }));

  m = initI18n(m);

  return m;
}

export function id(m) {
  return m.get("id");
}

export function clientID(m) {
  return get(m, "clientID");
}

export function domain(m) {
  return get(m, "domain");
}

export function assetsUrl(m) {
  return get(m, "assetsUrl");
}

export function setSubmitting(m, value, error = "") {
  m = tset(m, "submitting", value);
  m = clearGlobalSuccess(m);
  m = error && !value ? setGlobalError(m, error) : clearGlobalError(m);
  return m;
}

export function submitting(m) {
  return tget(m, "submitting", false);
}

export function setGlobalError(m, str) {
  return tset(m, "globalError", str);
}

export function globalError(m) {
  return tget(m, "globalError", "");
}

export function clearGlobalError(m) {
  return tremove(m, "globalError");
}

export function setGlobalSuccess(m, str) {
  return tset(m, "globalSuccess", str);
}

export function globalSuccess(m) {
  return tget(m, "globalSuccess", "");
}

export function clearGlobalSuccess(m) {
  return tremove(m, "globalSuccess");
}

export function rendering(m) {
  return tget(m, "render", false);
}

export function stopRendering(m) {
  return tremove(m, "render");
}

function extractUIOptions(id, options) {
  const closable = options.container ? false : undefined === options.closable ? true : !!options.closable;
  const theme = options.theme || {};
  const { logo, primaryColor } = theme;

  const avatar = options.avatar !== null;
  const customAvatarProvider = options.avatar
    && typeof options.avatar.url === "function"
    && typeof options.avatar.displayName === "function"
    && options.avatar;
  const avatarProvider = customAvatarProvider || gp;

  return new Immutable.fromJS({
    containerID: options.container || `auth0-lock-container-${id}`,
    appendContainer: !options.container,
    autoclose: undefined === options.autoclose ? false : closable && options.autoclose,
    autofocus: undefined === options.autofocus ? !(options.container || isSmallScreen()) : !!options.autofocus,
    avatar: avatar,
    avatarProvider: avatarProvider,
    logo: typeof logo === "string" ? logo : undefined,
    closable: closable,
    language: undefined === options.language ? "en" : trim(options.language || "").toLowerCase(),
    dict: typeof options.languageDictionary === "object" ? options.languageDictionary : {},
    disableWarnings: options.disableWarnings === undefined ? false : !!options.disableWarnings,
    mobile: undefined === options.mobile ? false : !!options.mobile,
    popupOptions: undefined === options.popupOptions ? {} : options.popupOptions,
    primaryColor: typeof primaryColor === "string" ? primaryColor : undefined,
    rememberLastLogin: undefined === options.rememberLastLogin ? true : !!options.rememberLastLogin
  });
}

const { get: getUIAttribute } = dataFns(["core", "ui"]);

export const ui = {
  containerID: lock => getUIAttribute(lock, "containerID"),
  appendContainer: lock => getUIAttribute(lock, "appendContainer"),
  autoclose: lock => getUIAttribute(lock, "autoclose"),
  autofocus: lock => getUIAttribute(lock, "autofocus"),
  avatar: lock => getUIAttribute(lock, "avatar"),
  avatarProvider: lock => getUIAttribute(lock, "avatarProvider"),
  closable: lock => getUIAttribute(lock, "closable"),
  dict: lock => getUIAttribute(lock, "dict"),
  disableWarnings: lock => getUIAttribute(lock, "disableWarnings"),
  t: (lock, keyPath, params) => t(lock, keyPath, params),
  language: lock => getUIAttribute(lock, "language"),
  logo: lock => getUIAttribute(lock, "logo"),
  mobile: lock => getUIAttribute(lock, "mobile"),
  popupOptions: lock => getUIAttribute(lock, "popupOptions"),
  primaryColor: lock => getUIAttribute(lock, "primaryColor"),
  rememberLastLogin: lock => getUIAttribute(lock, "rememberLastLogin")
};

const { get: getAuthAttribute } = dataFns(["core", "auth"]);

export const auth = {
  params: lock => getAuthAttribute(lock, "params"),
  redirect: lock => getAuthAttribute(lock, "redirect"),
  redirectUrl: lock => getAuthAttribute(lock, "redirectUrl"),
  responseType: lock => getAuthAttribute(lock, "responseType"),
  sso: lock => getAuthAttribute(lock, "sso")
};

function extractAuthOptions(options) {
  // TODO: shouldn't all options be namespased in authentication?
  let {
    params,
    redirect,
    redirectUrl,
    responseType,
    sso
  } = options.auth || {};

  params = typeof params === "object" ? params : {};
  redirectUrl = typeof redirectUrl === "string" && redirectUrl ? redirectUrl : undefined;
  redirect = typeof redirect === "boolean" ? redirect : true;
  responseType = typeof responseType === "string" ? responseType : redirectUrl ? "code" : "token";
  sso = typeof sso === "boolean" ? sso : true;

  if (trim(params.scope || "") === "openid profile") {
    warn(options, "Usage of scope 'openid profile' is not recommended. See https://auth0.com/docs/scopes for more details.");
  }

  return Immutable.fromJS({
    params,
    redirect,
    redirectUrl,
    responseType,
    sso
  });
}

export function withAuthOptions(m, opts) {
  return Immutable.fromJS(opts)
    .merge(get(m, "auth"))
    .toJS();
}

function extractAssetsUrlOption(opts, domain) {
  if (opts.assetsUrl && typeof opts.assetsUrl === "string") {
    return opts.assetsUrl;
  }

  const domainUrl = "https://" + domain;
  const hostname = parseUrl(domainUrl).hostname;
  const DOT_AUTH0_DOT_COM = ".auth0.com";
  const AUTH0_US_CDN_URL = "https://cdn.auth0.com";
  if (endsWith(hostname, DOT_AUTH0_DOT_COM)) {
    const parts = hostname.split(".");
    return parts.length > 3
      ? "https://cdn." + parts[parts.length - 3] + DOT_AUTH0_DOT_COM
      : AUTH0_US_CDN_URL;
  } else {
    return domainUrl;
  }
}

export function invokeLogInCallback(m, ...args) {
  get(m, "logInCallback").apply(undefined, args);
}

export function render(m) {
  return tset(m, "render", true);
}

export { reset };

export function setLoggedIn(m, value) {
  return tset(m, "loggedIn", value);
}

export function loggedIn(m) {
  return tget(m, "loggedIn", false);
}

export function warn(x, str) {
  const shouldOutput = Map.isMap(x)
    ? !ui.disableWarnings(x)
    : !x.disableWarnings;

  if (shouldOutput && console && console.warn) {
    console.warn(str);
  }
}

export function error(x, str) {
  const shouldOutput = Map.isMap(x)
    ? !ui.disableWarnings(x)
    : !x.disableWarnings;

  if (shouldOutput && console && console.error) {
    console.error(str);
  }
}

export function allowedConnections(m) {
  return get(m, "allowedConnections");
}

export function connections(m, type = undefined, ...strategies) {
  if (arguments.length === 1) {
    return get(m, "connections", Map())
      .filter((v, k) => k !== "unknown")
      .valueSeq()
      .flatten(true);
  }

  const xs = get(m, ["connections", type], List());
  return strategies.length > 0
    ? xs.filter(x => ~strategies.indexOf(x.get("strategy")))
    : xs;
}

export function connection(m, type = undefined, ...strategies) {
  return connections(m, type, ...strategies).get(0);
}

export function hasOneConnection(m, type = undefined) {
  const xs = connections(m);
  return xs.count() === 1 && (!type || xs.getIn([0, "type"]) === type);
}

export function hasOnlyConnections(m, type = undefined, ...strategies) {
  const all = connections(m).count();
  const filtered = connections(m, type, ...strategies).count();
  return all > 0 && all === filtered;
}

export function hasSomeConnections(m, type = undefined, ...strategies) {
  return connections(m, type, ...strategies).count() > 0;
}

export function findConnection(m, name) {
  return connections(m).find(m1 => m1.get("name") === name);
}

export function hasConnection(m, name) {
  return !!findConnection(m, name);
}

export function runHook(m, str, ...args) {
  get(m, "hookRunner")(str, ...args);
}

export function emitEvent(m, str, ...args) {
  get(m, "emitEventFn")(str, ...args);
}

export function loginErrorMessage(m, error) {
  // NOTE: previous version of lock checked for status codes and, at
  // some point, if the status code was 401 it defaults to an
  // "invalid_user_password" error (actually the
  // "wrongEmailPasswordErrorText" dict entry) instead of checking
  // explicitly. We should figure out if there was a reason for that.

  if (error.status === 0) {
    return ui.t(m, ["error", "login", "lock.network"], {__textOnly: true});
  }

  // Custom rule error (except blocked_user)
  if (error.code === "rule_error") {
    return error.description
      || ui.t(m, ["error", "login", "lock.fallback"], {__textOnly: true});
  }

  return ui.t(m, ["error", "login", error.error || error.code], {__textOnly: true})
    || ui.t(m, ["error", "login", "lock.fallback"], {__textOnly: true});
}

export function stop(m) {
  return set(m, "stopped", true);
}

export function hasStopped(m) {
  return get(m, "stopped");
}
<commit_msg>Use i18n module instead of ui.t in core/index<commit_after>import Immutable, { List, Map, Set } from 'immutable';
import { isSmallScreen } from '../utils/media_utils';
import { endsWith } from '../utils/string_utils';
import { parseUrl } from '../utils/url_utils';
import t, * as i18n from '../i18n';
import trim from 'trim';
import * as gp from '../avatar/gravatar_provider';
import { dataFns } from '../utils/data_utils';

const {
  get,
  init,
  remove,
  reset,
  set,
  tget,
  tset,
  tremove
} = dataFns(["core"]);

export function setup(id, clientID, domain, options, logInCallback, hookRunner, emitEventFn) {
  let m = init(id, Immutable.fromJS({
    assetsUrl: extractAssetsUrlOption(options, domain),
    auth: extractAuthOptions(options),
    clientID: clientID,
    domain: domain,
    emitEventFn: emitEventFn,
    hookRunner: hookRunner,
    logInCallback: logInCallback,
    allowedConnections: Immutable.fromJS(options.allowedConnections || []),
    ui: extractUIOptions(id, options)
  }));

  m = i18n.initI18n(m);

  return m;
}

export function id(m) {
  return m.get("id");
}

export function clientID(m) {
  return get(m, "clientID");
}

export function domain(m) {
  return get(m, "domain");
}

export function assetsUrl(m) {
  return get(m, "assetsUrl");
}

export function setSubmitting(m, value, error = "") {
  m = tset(m, "submitting", value);
  m = clearGlobalSuccess(m);
  m = error && !value ? setGlobalError(m, error) : clearGlobalError(m);
  return m;
}

export function submitting(m) {
  return tget(m, "submitting", false);
}

export function setGlobalError(m, str) {
  return tset(m, "globalError", str);
}

export function globalError(m) {
  return tget(m, "globalError", "");
}

export function clearGlobalError(m) {
  return tremove(m, "globalError");
}

export function setGlobalSuccess(m, str) {
  return tset(m, "globalSuccess", str);
}

export function globalSuccess(m) {
  return tget(m, "globalSuccess", "");
}

export function clearGlobalSuccess(m) {
  return tremove(m, "globalSuccess");
}

export function rendering(m) {
  return tget(m, "render", false);
}

export function stopRendering(m) {
  return tremove(m, "render");
}

function extractUIOptions(id, options) {
  const closable = options.container ? false : undefined === options.closable ? true : !!options.closable;
  const theme = options.theme || {};
  const { logo, primaryColor } = theme;

  const avatar = options.avatar !== null;
  const customAvatarProvider = options.avatar
    && typeof options.avatar.url === "function"
    && typeof options.avatar.displayName === "function"
    && options.avatar;
  const avatarProvider = customAvatarProvider || gp;

  return new Immutable.fromJS({
    containerID: options.container || `auth0-lock-container-${id}`,
    appendContainer: !options.container,
    autoclose: undefined === options.autoclose ? false : closable && options.autoclose,
    autofocus: undefined === options.autofocus ? !(options.container || isSmallScreen()) : !!options.autofocus,
    avatar: avatar,
    avatarProvider: avatarProvider,
    logo: typeof logo === "string" ? logo : undefined,
    closable: closable,
    language: undefined === options.language ? "en" : trim(options.language || "").toLowerCase(),
    dict: typeof options.languageDictionary === "object" ? options.languageDictionary : {},
    disableWarnings: options.disableWarnings === undefined ? false : !!options.disableWarnings,
    mobile: undefined === options.mobile ? false : !!options.mobile,
    popupOptions: undefined === options.popupOptions ? {} : options.popupOptions,
    primaryColor: typeof primaryColor === "string" ? primaryColor : undefined,
    rememberLastLogin: undefined === options.rememberLastLogin ? true : !!options.rememberLastLogin
  });
}

const { get: getUIAttribute } = dataFns(["core", "ui"]);

export const ui = {
  containerID: lock => getUIAttribute(lock, "containerID"),
  appendContainer: lock => getUIAttribute(lock, "appendContainer"),
  autoclose: lock => getUIAttribute(lock, "autoclose"),
  autofocus: lock => getUIAttribute(lock, "autofocus"),
  avatar: lock => getUIAttribute(lock, "avatar"),
  avatarProvider: lock => getUIAttribute(lock, "avatarProvider"),
  closable: lock => getUIAttribute(lock, "closable"),
  dict: lock => getUIAttribute(lock, "dict"),
  disableWarnings: lock => getUIAttribute(lock, "disableWarnings"),
  t: (lock, keyPath, params) => t(lock, keyPath, params),
  language: lock => getUIAttribute(lock, "language"),
  logo: lock => getUIAttribute(lock, "logo"),
  mobile: lock => getUIAttribute(lock, "mobile"),
  popupOptions: lock => getUIAttribute(lock, "popupOptions"),
  primaryColor: lock => getUIAttribute(lock, "primaryColor"),
  rememberLastLogin: lock => getUIAttribute(lock, "rememberLastLogin")
};

const { get: getAuthAttribute } = dataFns(["core", "auth"]);

export const auth = {
  params: lock => getAuthAttribute(lock, "params"),
  redirect: lock => getAuthAttribute(lock, "redirect"),
  redirectUrl: lock => getAuthAttribute(lock, "redirectUrl"),
  responseType: lock => getAuthAttribute(lock, "responseType"),
  sso: lock => getAuthAttribute(lock, "sso")
};

function extractAuthOptions(options) {
  // TODO: shouldn't all options be namespased in authentication?
  let {
    params,
    redirect,
    redirectUrl,
    responseType,
    sso
  } = options.auth || {};

  params = typeof params === "object" ? params : {};
  redirectUrl = typeof redirectUrl === "string" && redirectUrl ? redirectUrl : undefined;
  redirect = typeof redirect === "boolean" ? redirect : true;
  responseType = typeof responseType === "string" ? responseType : redirectUrl ? "code" : "token";
  sso = typeof sso === "boolean" ? sso : true;

  if (trim(params.scope || "") === "openid profile") {
    warn(options, "Usage of scope 'openid profile' is not recommended. See https://auth0.com/docs/scopes for more details.");
  }

  return Immutable.fromJS({
    params,
    redirect,
    redirectUrl,
    responseType,
    sso
  });
}

export function withAuthOptions(m, opts) {
  return Immutable.fromJS(opts)
    .merge(get(m, "auth"))
    .toJS();
}

function extractAssetsUrlOption(opts, domain) {
  if (opts.assetsUrl && typeof opts.assetsUrl === "string") {
    return opts.assetsUrl;
  }

  const domainUrl = "https://" + domain;
  const hostname = parseUrl(domainUrl).hostname;
  const DOT_AUTH0_DOT_COM = ".auth0.com";
  const AUTH0_US_CDN_URL = "https://cdn.auth0.com";
  if (endsWith(hostname, DOT_AUTH0_DOT_COM)) {
    const parts = hostname.split(".");
    return parts.length > 3
      ? "https://cdn." + parts[parts.length - 3] + DOT_AUTH0_DOT_COM
      : AUTH0_US_CDN_URL;
  } else {
    return domainUrl;
  }
}

export function invokeLogInCallback(m, ...args) {
  get(m, "logInCallback").apply(undefined, args);
}

export function render(m) {
  return tset(m, "render", true);
}

export { reset };

export function setLoggedIn(m, value) {
  return tset(m, "loggedIn", value);
}

export function loggedIn(m) {
  return tget(m, "loggedIn", false);
}

export function warn(x, str) {
  const shouldOutput = Map.isMap(x)
    ? !ui.disableWarnings(x)
    : !x.disableWarnings;

  if (shouldOutput && console && console.warn) {
    console.warn(str);
  }
}

export function error(x, str) {
  const shouldOutput = Map.isMap(x)
    ? !ui.disableWarnings(x)
    : !x.disableWarnings;

  if (shouldOutput && console && console.error) {
    console.error(str);
  }
}

export function allowedConnections(m) {
  return get(m, "allowedConnections");
}

export function connections(m, type = undefined, ...strategies) {
  if (arguments.length === 1) {
    return get(m, "connections", Map())
      .filter((v, k) => k !== "unknown")
      .valueSeq()
      .flatten(true);
  }

  const xs = get(m, ["connections", type], List());
  return strategies.length > 0
    ? xs.filter(x => ~strategies.indexOf(x.get("strategy")))
    : xs;
}

export function connection(m, type = undefined, ...strategies) {
  return connections(m, type, ...strategies).get(0);
}

export function hasOneConnection(m, type = undefined) {
  const xs = connections(m);
  return xs.count() === 1 && (!type || xs.getIn([0, "type"]) === type);
}

export function hasOnlyConnections(m, type = undefined, ...strategies) {
  const all = connections(m).count();
  const filtered = connections(m, type, ...strategies).count();
  return all > 0 && all === filtered;
}

export function hasSomeConnections(m, type = undefined, ...strategies) {
  return connections(m, type, ...strategies).count() > 0;
}

export function findConnection(m, name) {
  return connections(m).find(m1 => m1.get("name") === name);
}

export function hasConnection(m, name) {
  return !!findConnection(m, name);
}

export function runHook(m, str, ...args) {
  get(m, "hookRunner")(str, ...args);
}

export function emitEvent(m, str, ...args) {
  get(m, "emitEventFn")(str, ...args);
}

export function loginErrorMessage(m, error) {
  // NOTE: previous version of lock checked for status codes and, at
  // some point, if the status code was 401 it defaults to an
  // "invalid_user_password" error (actually the
  // "wrongEmailPasswordErrorText" dict entry) instead of checking
  // explicitly. We should figure out if there was a reason for that.

  if (error.status === 0) {
    return i18n.str(m, ["error", "login", "lock.network"]);
  }

  // Custom rule error (except blocked_user)
  if (error.code === "rule_error") {
    return error.description
      || i18n.str(m, ["error", "login", "lock.fallback"]);
  }

  return i18n.str(m, ["error", "login", error.error || error.code])
    || i18n.str(m, ["error", "login", "lock.fallback"]);
}

export function stop(m) {
  return set(m, "stopped", true);
}

export function hasStopped(m) {
  return get(m, "stopped");
}
<|endoftext|>


<commit_before>(function(){

var defaults = {
    name: false,
    timestep: 1000.0 / 60,
    maxSteps: 4,
    webworker: false, // to implement
    integrator: 'improved-euler'
};

var World = function World( cfg, fn ){

    if (!(this instanceof World)){
        return new World( cfg, fn );
    }

    this.init( cfg, fn );
};

World.prototype = {

    init: function( cfg, fn ){

        // prevent double initialization
        this.init = true;

        this._bodies = [];
        this._behaviorStack = [];
        this._integrator = null;
        this._paused = false;
        this._opts = {};

        // set options
        this.options( cfg );

        // apply the callback function
        if (typeof fn === 'function'){

            fn.apply(this, [ this ]);
        }
    },

    // get/set options
    options: function( cfg ){

        if (cfg){

            // extend the defaults
            Physics.util.extend(this._opts, defaults, cfg);
            // set timestep
            this.timeStep(this._opts.timestep);
            // add integrator
            this.add(Physics.integrator(this._opts.integrator));

            return this;
        }

        return Physics.util.extend({}, this._opts);
    },

    // add objects, integrators, behaviors...
    add: function( arg ){

        var i = 0
            ,len = arg && arg.length || 0
            ,thing = len ? arg[ 0 ] : arg
            ;

        // we'll either cycle through an array
        // or just run this on the arg itself
        do {
            switch (thing.type){

                case 'behavior':
                    this.addBehavior(thing);
                break; // end behavior

                case 'integrator':
                    this._integrator = thing;
                break; // end integrator

                // assume physical body
                default:
                    this.addBody(thing);
                break; // end default
            }
        } while ( ++i < len && (thing = arg[ i ]) )

        return this;
    },

    // add a behavior
    addBehavior: function( behavior ){

        // TODO more...
        this._behaviorStack.push( behavior );
        return this;
    },

    addBody: function( body ){

        this._bodies.push( body );
        return this;
    },

    applyBehaviors: function(){

    },

    // internal method
    substep: function(){

        this.applyBehaviors();
        this._integrator.integrate(dt, this._bodies)

        // this.doInteractions( 'beforeAccel', dt );
        // this.resolveAcceleration( dt );
        // this.doInteractions( 'afterAccel', dt );
        // this.resolveInertia( dt );
        // this.doInteractions( 'afterInertia', dt );
    },

    step: function( now ){

        if ( this._paused ){

            this._time = false;
            return this;
        }

        var time = this._time || (this._time = now)
            ,diff = now - time
            ;

        if ( !diff ) return this;

        // set some stats
        this.FPS = 1000/diff;
        this.nsteps = Math.ceil(diff/this._dt);

        // limit number of substeps in each step
        if ( diff > this._maxJump ){

            this._time = now - this._maxJump;
        }

        while ( this._time < now ){
            this._time += dt;
            this.substep();
        }

        return this;
    },

    pause: function(){

        this._paused = true;
        return this;
    },

    unpause: function(){

        this._paused = false;
        return this;
    },

    isPaused: function(){

        return !!this._paused;
    },

    timeStep: function( dt ){

        if ( dt ){

            this._dt = dt;
            // calculate the maximum jump in time over which to do substeps
            this._maxJump = dt * this._opts.maxSteps;

            return this;
        }

        return this._dt;
    },

    // TODO: find bodies
    // select: function( sel ){

    //     if (!sel){

    //         // fast array copy
    //         return this._bodies.splice(0);
    //     }

    //     // TODO
    // }

    getByClassName: function( klass ){

        var bodies = this._bodies
            ,obj
            ,ret = []
            ;

        for ( var i = 0, l = bodies.length; i < l; ++i ){

            obj = bodies[ i ];

            if ( obj.hasClass( klass ) ){

                ret.push( obj );
            }
        }

        return ret;
    }
};

Physics.world = World;

}());<commit_msg>changed add implementation. added render method<commit_after>(function(){

var defaults = {
    name: false,
    timestep: 1000.0 / 60,
    maxSteps: 4,
    webworker: false, // to implement
    integrator: 'improved-euler'
};

var World = function World( cfg, fn ){

    if (!(this instanceof World)){
        return new World( cfg, fn );
    }

    this.init( cfg, fn );
};

World.prototype = {

    init: function( cfg, fn ){

        // prevent double initialization
        this.init = true;

        this._stats = {}; // statistics (fps, etc)
        this._bodies = [];
        this._behaviorStack = [];
        this._integrator = null;
        this._renderer = null;
        this._paused = false;
        this._opts = {};

        // set options
        this.options( cfg );

        // apply the callback function
        if (typeof fn === 'function'){

            fn.apply(this, [ this ]);
        }
    },

    // get/set options
    options: function( cfg ){

        if (cfg){

            // extend the defaults
            Physics.util.extend(this._opts, defaults, cfg);
            // set timestep
            this.timeStep(this._opts.timestep);
            // add integrator
            this.add(Physics.integrator(this._opts.integrator));

            return this;
        }

        return Physics.util.extend({}, this._opts);
    },

    // add objects, integrators, behaviors...
    add: function( arg ){

        var i = 0
            ,len = arg && arg.length || 0
            ,thing = len ? arg[ 0 ] : arg
            ;

        // we'll either cycle through an array
        // or just run this on the arg itself
        do {
            switch (thing.type){

                case 'behavior':
                    this.addBehavior(thing);
                break; // end behavior

                case 'integrator':
                    this._integrator = thing;
                break; // end integrator

                case 'renderer':
                    this._renderer = thing;
                break; // end renderer

                case 'body':
                    this.addBody(thing);
                break; // end body

                default:
                    throw 'Error: failed to add item of unknown type to world';
                break; // end default
            }
        } while ( ++i < len && (thing = arg[ i ]) )

        return this;
    },

    // add a behavior
    addBehavior: function( behavior ){

        // TODO more...
        this._behaviorStack.push( behavior );
        return this;
    },

    addBody: function( body ){

        this._bodies.push( body );
        return this;
    },

    applyBehaviors: function(){

    },

    // internal method
    substep: function( dt ){

        this.applyBehaviors();
        this._integrator.integrate(dt, this._bodies)

        // this.doInteractions( 'beforeAccel', dt );
        // this.resolveAcceleration( dt );
        // this.doInteractions( 'afterAccel', dt );
        // this.resolveInertia( dt );
        // this.doInteractions( 'afterInertia', dt );
    },

    step: function( now ){

        if ( this._paused ){

            this._time = false;
            return this;
        }

        var time = this._time || (this._time = now)
            ,diff = now - time
            ,stats = this._stats
            ,dt = this._dt
            ;

        if ( !diff ) return this;

        // set some stats
        stats.fps = 1000/diff;
        stats.steps = Math.ceil(diff/this._dt);

        // limit number of substeps in each step
        if ( diff > this._maxJump ){

            this._time = now - this._maxJump;
        }

        while ( this._time < now ){
            this._time += dt;
            this.substep( dt );
        }

        return this;
    },

    render: function(){

        if ( !this._renderer ){
            throw "No renderer added to world";
        }

        this._renderer.render( this._bodies, this._stats );

        return this;
    },

    pause: function(){

        this._paused = true;
        return this;
    },

    unpause: function(){

        this._paused = false;
        return this;
    },

    isPaused: function(){

        return !!this._paused;
    },

    timeStep: function( dt ){

        if ( dt ){

            this._dt = dt;
            // calculate the maximum jump in time over which to do substeps
            this._maxJump = dt * this._opts.maxSteps;

            return this;
        }

        return this._dt;
    },

    // TODO: find bodies
    // select: function( sel ){

    //     if (!sel){

    //         // fast array copy
    //         return this._bodies.splice(0);
    //     }

    //     // TODO
    // }

    getByClassName: function( klass ){

        var bodies = this._bodies
            ,obj
            ,ret = []
            ;

        for ( var i = 0, l = bodies.length; i < l; ++i ){

            obj = bodies[ i ];

            if ( obj.hasClass( klass ) ){

                ret.push( obj );
            }
        }

        return ret;
    }
};

Physics.world = World;

}());<|endoftext|>


<commit_before>'use strict'

var Header = require('./Header')
var Footer = require('./Footer')
var Body = require('./Body')

var $ = window.jQuery

module.exports = HeaderFooterFactory

/**
 * HeaderFactory class. The aim of this class is to manage the document header and footer.
 * @constructor
 * @param {Editor} editor The current editor
 * @property {Editor} _editor The current editor
 * @property {Boolean} _hasHeader Tell if the document has a header or not
 * @property {Boolean} _hasBody Tell if the document has a body or not
 * @property {Boolean} _hasFooter Tell if the document has a fooer or not
 * @property {Header} header The current header if exists
 * @property {Footer} footer The current footer if exists
 */
function HeaderFooterFactory (editor) {
  this._editor = editor
  this._hasHeader = false
  this._hasBody = false
  this._hasFooter = false
}

/**
 * Load an existing header or footer depending of its nature, from its DOM element.
 * @method
 * @param {DOMElement} element
 * @returns void
 */
HeaderFooterFactory.prototype.loadElement = function (element) {
  var $el = $(element)
  if ($el.attr('data-headfoot-header')) {
    this._hasHeader = true
    this.header = new Header(this._editor, this._editor.getBody(), element)
  } else if ($el.attr('data-headfoot-footer')) {
    this._hasFooter = true
    this.footer = new Footer(this._editor, this._editor.getBody(), element)
  } else if ($el.attr('data-headfoot-body')) {
    this._hasBody = true
    this.body = new Body(this._editor, this._editor.getBody(), element, this.hasHeader(), this.hasFooter(), this.header)
  } else throw new Error('This element is not a header, footer neither a body element.')
}

/**
 * Insert a new header
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.insertHeader = function () {
  this.header = new Header(this._editor, this._editor.getBody())
  this._hasHeader = true
}

/**
 * Insert a new body
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.insertBody = function () {
  this.body = new Body(this._editor, this._editor.getBody(), this._hasHeader, this._hasFooter, this.header)
  this._hasBody = true
}

/**
 * Insert a new footer
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.insertFooter = function () {
  this.footer = new Footer(this._editor, this._editor.getBody())
  this._hasFooter = true
}

/**
 * Remove the current header
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.removeHeader = function () {
  // the header can be removed only if it exists
  if (!this.hasHeader()) throw new Error('No header available to remove')

  $(this.header.node).remove()
  this.header = null
  this._hasHeader = false
}

/**
 * Insert a new footer
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.removeFooter = function () {
  // the footer can be removed only if it exists
  if (!this.hasFooter()) throw new Error('No footer available to remove')

  $(this.footer.node).remove()
  this.footer = null
  this._hasFooter = false
}

/**
 * Check if the document has a header or not
 * @method
 * @returns {Boolean} true if the document has a header, false if not
 */
HeaderFooterFactory.prototype.hasHeader = function () {
  return this._hasHeader
}

/**
 * Check if the document has a body or not
 * @method
 * @returns {Boolean} true if the document has a body, false if not
 */
HeaderFooterFactory.prototype.hasBody = function () {
  return this._hasBody
}

/**
 * Check if the document has a footer or not
 * @method
 * @returns {Boolean} true if the document has a footer, false if not
 */
HeaderFooterFactory.prototype.hasFooter = function () {
  return this._hasFooter
}

HeaderFooterFactory.prototype.focusToEndOfBody = function () {
  var $body = $(this.body.node)
  var lastBodyChild = $body.children().last()[0]
  this.body.enterNode()
  this._editor.selection.setCursorLocation(lastBodyChild, lastBodyChild.childNodes.length)
}

HeaderFooterFactory.prototype.forceCursorToAllowedLocation = function (node, parents) {
  if (this.hasBody()) {
    if (!parents) {
      var $node = $(node)
      var allparents = $node.parents()
      parents = allparents.slice(0, -2)
    }

    var lastParent = parents[parents.length - 1]
    var allowedLocations = [this.body.node]

    if (this.hasHeader()) {
      allowedLocations.push(this.header.node)
    }
    if (this.hasFooter()) {
      allowedLocations.push(this.footer.node)
    }

    if (!~allowedLocations.indexOf(lastParent) && !~allowedLocations.indexOf(node)) {
      this.focusToEndOfBody()
    }
  }
}
<commit_msg>implements HeaderFooterFactory#getActiveSection()<commit_after>'use strict'

var Header = require('./Header')
var Footer = require('./Footer')
var Body = require('./Body')

var $ = window.jQuery

module.exports = HeaderFooterFactory

/**
 * HeaderFactory class. The aim of this class is to manage the document header and footer.
 * @constructor
 * @param {Editor} editor The current editor
 * @property {Editor} _editor The current editor
 * @property {Boolean} _hasHeader Tell if the document has a header or not
 * @property {Boolean} _hasBody Tell if the document has a body or not
 * @property {Boolean} _hasFooter Tell if the document has a fooer or not
 * @property {Header} header The current header if exists
 * @property {Footer} footer The current footer if exists
 */
function HeaderFooterFactory (editor) {
  this._editor = editor
  this._hasHeader = false
  this._hasBody = false
  this._hasFooter = false
}

/**
 * Load an existing header or footer depending of its nature, from its DOM element.
 * @method
 * @param {DOMElement} element
 * @returns void
 */
HeaderFooterFactory.prototype.loadElement = function (element) {
  var $el = $(element)
  if ($el.attr('data-headfoot-header')) {
    this._hasHeader = true
    this.header = new Header(this._editor, this._editor.getBody(), element)
  } else if ($el.attr('data-headfoot-footer')) {
    this._hasFooter = true
    this.footer = new Footer(this._editor, this._editor.getBody(), element)
  } else if ($el.attr('data-headfoot-body')) {
    this._hasBody = true
    this.body = new Body(this._editor, this._editor.getBody(), element, this.hasHeader(), this.hasFooter(), this.header)
  } else throw new Error('This element is not a header, footer neither a body element.')
}

/**
 * Insert a new header
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.insertHeader = function () {
  this.header = new Header(this._editor, this._editor.getBody())
  this._hasHeader = true
}

/**
 * Insert a new body
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.insertBody = function () {
  this.body = new Body(this._editor, this._editor.getBody(), this._hasHeader, this._hasFooter, this.header)
  this._hasBody = true
}

/**
 * Insert a new footer
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.insertFooter = function () {
  this.footer = new Footer(this._editor, this._editor.getBody())
  this._hasFooter = true
}

/**
 * Remove the current header
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.removeHeader = function () {
  // the header can be removed only if it exists
  if (!this.hasHeader()) throw new Error('No header available to remove')

  $(this.header.node).remove()
  this.header = null
  this._hasHeader = false
}

/**
 * Insert a new footer
 * @method
 * @returns void
 */
HeaderFooterFactory.prototype.removeFooter = function () {
  // the footer can be removed only if it exists
  if (!this.hasFooter()) throw new Error('No footer available to remove')

  $(this.footer.node).remove()
  this.footer = null
  this._hasFooter = false
}

/**
 * Check if the document has a header or not
 * @method
 * @returns {Boolean} true if the document has a header, false if not
 */
HeaderFooterFactory.prototype.hasHeader = function () {
  return this._hasHeader
}

/**
 * Check if the document has a body or not
 * @method
 * @returns {Boolean} true if the document has a body, false if not
 */
HeaderFooterFactory.prototype.hasBody = function () {
  return this._hasBody
}

/**
 * Check if the document has a footer or not
 * @method
 * @returns {Boolean} true if the document has a footer, false if not
 */
HeaderFooterFactory.prototype.hasFooter = function () {
  return this._hasFooter
}

HeaderFooterFactory.prototype.focusToEndOfBody = function () {
  var $body = $(this.body.node)
  var lastBodyChild = $body.children().last()[0]
  this.body.enterNode()
  this._editor.selection.setCursorLocation(lastBodyChild, lastBodyChild.childNodes.length)
}

HeaderFooterFactory.prototype.forceCursorToAllowedLocation = function (node, parents) {
  if (this.hasBody()) {
    if (!parents) {
      var $node = $(node)
      var allparents = $node.parents()
      parents = allparents.slice(0, -2)
    }

    var lastParent = parents[parents.length - 1]
    var allowedLocations = [this.body.node]

    if (this.hasHeader()) {
      allowedLocations.push(this.header.node)
    }
    if (this.hasFooter()) {
      allowedLocations.push(this.footer.node)
    }

    if (!~allowedLocations.indexOf(lastParent) && !~allowedLocations.indexOf(node)) {
      this.focusToEndOfBody()
    }
  }
}

HeaderFooterFactory.prototype.getActiveSection = function () {
  return [this.header, this.body, this.footer]
  .reduce(function (prev, section) {
    if (prev) {
      return prev
    } else {
      if (section.isActive()) {
        return section
      }
    }
  }, null)
}
<|endoftext|>


<commit_before>import React from 'react';
import { Table, Pagination, PaginationItem, PaginationLink } from 'reactstrap';
import createPagination from './createPagination';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'

import './DataTable.css';

class DataTable extends React.Component {

	render() {

		function renderCaret(dataField, sortBy, sortDir) {
			if(sortBy == dataField) {
				if(sortDir == 'asc') {
					return <FontAwesomeIcon icon={faSortUp} className="float-right" />
				}

				return <FontAwesomeIcon icon={faSortDown} className="float-right" />
			}
			return <FontAwesomeIcon icon={faSort} className="float-right" color="#cccccc" />;
		}

		return (

								<input
									onChange={e => {
										this.props.onSelectAll(e.target.checked, this.props.data);
									}}
									type="checkbox"
									ref={(input) => {
					          if (input != null) {
					          	input.indeterminate = (!!this.props.selected.length) && (this.props.selected.length < this.props.data.length);
					          }}
									}
									checked={this.props.selected.length == this.props.data.length}
								/>

							{this.props.columns.map((c,i) => {
								return  {
									this.props.onSort(c.dataField, this.props.sortDir);
								}} style={{cursor: 'pointer'}} key={i}>
									{c.text}
									{renderCaret(c.dataField, this.props.sortBy, this.props.sortDir)}

							})}

					{
						this.props.data.map((d,i) => {
							return (

									<input onChange={e => {
										this.props.onSelect(e.target.checked, d)
									}} checked={this.props.selected.indexOf(d[this.props.idField]) !== -1} type="checkbox" />
									{this.props.columns.map((c,i) => {
										if(typeof c.formatter == 'function') {
											return c.formatter(d[c.dataField], d);
										} else {
											return {d[c.dataField]}
										}
									})}

							);
						})
					}

				<Pagination className="float-right d-inline-block" size="sm" aria-label="Page navigation example">
					{createPagination(this.props.currentPage, this.props.totalPageCount).map((p,i) => {
						return (
							<PaginationItem key={i} disabled={isNaN(p)} active={this.props.currentPage == p}>
								{this.props.renderPaginationLink(p)}
							</PaginationItem>
						)
					})}
				</Pagination>

		);
	}
}

DataTable.defaultProps = {
	idField: "_id",
	data: [],
	selected: [],
	columns: [],
	currentPage: 1,
	totalPageCount: 1,
	onSort: function (dataField, sortDir) {

	},
	sortBy: null,
	sortDir: null,
	renderPaginationLink: function (p) {
		return (
			<PaginationLink className="page-link" href={p}>
				{p}
			</PaginationLink>
		)
	}
};

export default DataTable;<commit_msg>fix cell render<commit_after>import React from 'react';
import { Table, Pagination, PaginationItem, PaginationLink } from 'reactstrap';
import createPagination from './createPagination';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'

import './DataTable.css';

class DataTable extends React.Component {

	render() {

		function renderCaret(dataField, sortBy, sortDir) {
			if(sortBy == dataField) {
				if(sortDir == 'asc') {
					return <FontAwesomeIcon icon={faSortUp} className="float-right" />
				}

				return <FontAwesomeIcon icon={faSortDown} className="float-right" />
			}
			return <FontAwesomeIcon icon={faSort} className="float-right" color="#cccccc" />;
		}

		return (

								<input
									onChange={e => {
										this.props.onSelectAll(e.target.checked, this.props.data);
									}}
									type="checkbox"
									ref={(input) => {
					          if (input != null) {
					          	input.indeterminate = (!!this.props.selected.length) && (this.props.selected.length < this.props.data.length);
					          }}
									}
									checked={this.props.selected.length == this.props.data.length}
								/>

							{this.props.columns.map((c,i) => {
								return  {
									this.props.onSort(c.dataField, this.props.sortDir);
								}} style={{cursor: 'pointer'}} key={i}>
									{c.text}
									{renderCaret(c.dataField, this.props.sortBy, this.props.sortDir)}

							})}

					{
						this.props.data.map((d,i) => {
							return (

									<input onChange={e => {
										this.props.onSelect(e.target.checked, d)
									}} checked={this.props.selected.indexOf(d[this.props.idField]) !== -1} type="checkbox" />
									{this.props.columns.map((c,i) => {
										return (

												{(() => {
													if(typeof c.formatter == 'function') {
														return c.formatter(d[c.dataField], d);
													} else {
														return d[c.dataField]
													}
												})()}

										)
									})}

							);
						})
					}

				<Pagination className="float-right d-inline-block" size="sm" aria-label="Page navigation example">
					{createPagination(this.props.currentPage, this.props.totalPageCount).map((p,i) => {
						return (
							<PaginationItem key={i} disabled={isNaN(p)} active={this.props.currentPage == p}>
								{this.props.renderPaginationLink(p)}
							</PaginationItem>
						)
					})}
				</Pagination>

		);
	}
}

DataTable.defaultProps = {
	idField: "_id",
	data: [],
	selected: [],
	columns: [],
	currentPage: 1,
	totalPageCount: 1,
	onSort: function (dataField, sortDir) {

	},
	sortBy: null,
	sortDir: null,
	renderPaginationLink: function (p) {
		return (
			<PaginationLink className="page-link" href={p}>
				{p}
			</PaginationLink>
		)
	}
};

export default DataTable;<|endoftext|>