<commit_before>/**
 * Base service for mocking with Sequelize
 *
 * @module lib/sequelize-mocking
 * @exports SequelizeMocking
 * @version 0.1.0
 * @since 0.1.0
 * @author Julien Roche
 */

'use strict';

// Imports
const Sequelize = require('sequelize');
const _ = require('lodash');
const sequelizeFixtures = require('sequelize-fixtures');

// Constants and variables
const FAKE_DATABASE_NAME = 'test-database';
const AFTER_DEFINE_EVENT = 'afterDefine';
const AFTER_DEFINE_EVENT_NAME = 'sequelizeMockAfterDefine';
const SQLITE_SEQUELIZE_OPTIONS = {
    'dialect': 'sqlite',
    'storage': ':memory:'
};

/**
 * @class SequelizeMockingOptions
 * @property {boolean} [logging=true]
 * @property {Function} [transformFixtureDataFn] Allow an external caller to do some transforms to the data. See https://github.com/domasx2/sequelize-fixtures/commit/cffbfb1c67c8e05d5099b4455b99ac3aadd0089d
 */

/**
 * Sequelize mocking service
 */
class SequelizeMocking {
    /**
     * @param {Sequelize} sequelizeInstance
     * @param {SequelizeMockingOptions} [options]
     * @returns {Sequelize} Mocked Sequelize object
     */
    static adaptSequelizeOptions(sequelizeInstance, options) {
        let optionsExtended = _.merge(
            { },
            sequelizeInstance.options,
            SQLITE_SEQUELIZE_OPTIONS,
            { 'logging': options && !options.logging ? false : console.log }
        );

        return optionsExtended;
    }

    /**
     * @param {Sequelize} sequelizeInstance
     * @param {Sequelize.Model} model
     * @returns {Sequelize.Model}
     */
    static copyModel(sequelizeInstance, model) {
        class TempModel extends Sequelize.Model {
        }

        let newModel = TempModel.init(
            _.merge({ }, model.attributes),
            _.merge({ }, model.options, { 'sequelize': sequelizeInstance, 'modelName': model.name })
        );
        return newModel;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} mockedSequelize
     * @returns {Sequelize} Mocked Sequelize object
     */
    static copyCurrentModels(originalSequelize, mockedSequelize) {
        originalSequelize.modelManager.all.forEach(function (model) {
            SequelizeMocking.copyModel(mockedSequelize, model);
        });

        return mockedSequelize;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise.<Sequelize>}
     */
    static create(originalSequelize, options) {
        let logging = !options || options.logging;
        let mockedSequelize = new Sequelize(FAKE_DATABASE_NAME, null, null, SequelizeMocking.adaptSequelizeOptions(originalSequelize, options));

        mockedSequelize.__originalSequelize = originalSequelize;

        SequelizeMocking.copyCurrentModels(originalSequelize, mockedSequelize);
        SequelizeMocking.modifyConnection(originalSequelize, mockedSequelize);
        SequelizeMocking.modifyModelReferences(originalSequelize, mockedSequelize);
        SequelizeMocking.hookNewModel(originalSequelize, mockedSequelize, options);

        logging && console.log('SequelizeMocking - Mock the context');
        return mockedSequelize
            .sync()
            .then(function () {
                logging && console.log('SequelizeMocking - Database construction done');
                return mockedSequelize;
            });
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {string} fixtureFilePath
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise.<Sequelize>}
     */
    static createAndLoadFixtureFile(originalSequelize, fixtureFilePath, options) {
        let logging = !options || options.logging;

        return SequelizeMocking
            .create(originalSequelize, options)
            .then(function (mockedSequelize) {
                return SequelizeMocking
                    .loadFixtureFile(mockedSequelize, fixtureFilePath, options)
                    .then(function () {
                        logging && console.log('SequelizeMocking - Mocked data injected');
                        return mockedSequelize;
                    });
            });
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} mockedSequelize
     * @param {SequelizeMockingOptions} [options]
     */
    static hookNewModel(originalSequelize, mockedSequelize, options) {
        let logging = !options || options.logging;

        originalSequelize.addHook(AFTER_DEFINE_EVENT, AFTER_DEFINE_EVENT_NAME, function (newModel) {
            SequelizeMocking
                .modifyModelReference(
                    mockedSequelize,
                    SequelizeMocking.copyModel(mockedSequelize, newModel)
                )
                .sync({ 'hooks': true })
                .then(function () {
                    logging && console.log(`Model ${newModel.name} was declared into the database`);
                })
                .catch(function (err) {
                    logging && console.error(`An error occured when initializing the model ${newModel.name}`);
                    console.error(err && err.stack ? err.stack : err);
                    process.exit(1);
                });
        });
    }

    /**
     * @param {Sequelize} sequelize
     * @param {string | Array.<String>} fixtureFilePath
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise.<Sequelize>}
     */
    static async loadFixtureFile(sequelize, fixtureFilePath, options) {
        let logging = !options || options.logging;
        let transformFixtureDataFn = !options || options.transformFixtureDataFn;
        let loadFixturesOptions = {
            'log': logging ? null : _.noop
        };

        if (_.isFunction(transformFixtureDataFn)) {
            loadFixturesOptions.transformFixtureDataFn = transformFixtureDataFn;
        }

        if(Array.isArray(fixtureFilePath) ){

            for(let i = 0; i < fixtureFilePath.length; i++){

                await sequelizeFixtures
                    .loadFile(fixtureFilePath[i], SequelizeMocking.mapModels(sequelize), loadFixturesOptions);

            }

        } else{

            await sequelizeFixtures
                .loadFile(fixtureFilePath, SequelizeMocking.mapModels(sequelize), loadFixturesOptions);
        }

        return sequelize;

    }

    /**
     * @param sequelize
     * @returns {Object}
     */
    static mapModels(sequelize) {
        let map = { };

        sequelize.modelManager.all.forEach(function (model) {
            map[model.name] = model;
        });

        return map;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} newSequelizeToUse
     * @returns {Sequelize} The new Sequelize object
     */
    static modifyConnection(originalSequelize, newSequelizeToUse) {
        originalSequelize.__connectionManager = originalSequelize.connectionManager;
        originalSequelize.connectionManager = newSequelizeToUse.connectionManager;

        originalSequelize.__dialect = originalSequelize.dialect;
        originalSequelize.dialect = newSequelizeToUse.dialect;

        return newSequelizeToUse;
    }

    /**
     * Goal: the instanciate model shall use another instance of @{Sequelize} than the one used to create the model
     *
     * @param {Sequelize} newSequelizeToUse
     * @param {Sequelize.Model} model
     * @returns {Sequelize.Model}
     */
    static modifyModelReference (newSequelizeToUse, model) {
        model.sequelize = newSequelizeToUse;
        return model;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} newSequelizeToUse
     * @returns {Sequelize} The new Sequelize object
     */
    static modifyModelReferences(originalSequelize, newSequelizeToUse) {
        originalSequelize.modelManager.all.forEach(function (model) {
            SequelizeMocking.modifyModelReference(newSequelizeToUse, model);
        });

        return newSequelizeToUse;
    }

    /**
     * @param {Sequelize} mockedSequelize
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise}
     */
    static restore(mockedSequelize, options) {
        let logging = !options || options.logging;

        SequelizeMocking.unhookNewModel(mockedSequelize);

        if (mockedSequelize.__originalSequelize) {
            SequelizeMocking.modifyModelReferences(mockedSequelize, mockedSequelize.__originalSequelize);
        }

        if (mockedSequelize.__dialect && mockedSequelize.__connectionManager) {
            SequelizeMocking.modifyConnection(mockedSequelize, mockedSequelize.__originalSequelize);
        }

        delete mockedSequelize.__originalSequelize;
        delete mockedSequelize.__dialect;
        delete mockedSequelize.__connectionManager;

        logging && console.log('SequelizeMocking - restore the context');
        return mockedSequelize
            .drop({ 'logging': logging })
            .then(function () {
                logging && console.log('SequelizeMocking - Context is restored');
            });
    }

    /**
     * @param {Sequelize} mockedSequelize
     */
    static unhookNewModel(mockedSequelize) {
        if (mockedSequelize.__originalSequelize) {
            mockedSequelize.__originalSequelize.removeHook(AFTER_DEFINE_EVENT, AFTER_DEFINE_EVENT_NAME);
        }
    }
}

module.exports = SequelizeMocking;
<commit_msg>remove async and use sequelizeFixtures.loadFiles<commit_after>/**
 * Base service for mocking with Sequelize
 *
 * @module lib/sequelize-mocking
 * @exports SequelizeMocking
 * @version 0.1.0
 * @since 0.1.0
 * @author Julien Roche
 */

'use strict';

// Imports
const Sequelize = require('sequelize');
const _ = require('lodash');
const sequelizeFixtures = require('sequelize-fixtures');

// Constants and variables
const FAKE_DATABASE_NAME = 'test-database';
const AFTER_DEFINE_EVENT = 'afterDefine';
const AFTER_DEFINE_EVENT_NAME = 'sequelizeMockAfterDefine';
const SQLITE_SEQUELIZE_OPTIONS = {
    'dialect': 'sqlite',
    'storage': ':memory:'
};

/**
 * @class SequelizeMockingOptions
 * @property {boolean} [logging=true]
 * @property {Function} [transformFixtureDataFn] Allow an external caller to do some transforms to the data. See https://github.com/domasx2/sequelize-fixtures/commit/cffbfb1c67c8e05d5099b4455b99ac3aadd0089d
 */

/**
 * Sequelize mocking service
 */
class SequelizeMocking {
    /**
     * @param {Sequelize} sequelizeInstance
     * @param {SequelizeMockingOptions} [options]
     * @returns {Sequelize} Mocked Sequelize object
     */
    static adaptSequelizeOptions(sequelizeInstance, options) {
        let optionsExtended = _.merge(
            { },
            sequelizeInstance.options,
            SQLITE_SEQUELIZE_OPTIONS,
            { 'logging': options && !options.logging ? false : console.log }
        );

        return optionsExtended;
    }

    /**
     * @param {Sequelize} sequelizeInstance
     * @param {Sequelize.Model} model
     * @returns {Sequelize.Model}
     */
    static copyModel(sequelizeInstance, model) {
        class TempModel extends Sequelize.Model {
        }

        let newModel = TempModel.init(
            _.merge({ }, model.attributes),
            _.merge({ }, model.options, { 'sequelize': sequelizeInstance, 'modelName': model.name })
        );
        return newModel;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} mockedSequelize
     * @returns {Sequelize} Mocked Sequelize object
     */
    static copyCurrentModels(originalSequelize, mockedSequelize) {
        originalSequelize.modelManager.all.forEach(function (model) {
            SequelizeMocking.copyModel(mockedSequelize, model);
        });

        return mockedSequelize;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise.<Sequelize>}
     */
    static create(originalSequelize, options) {
        let logging = !options || options.logging;
        let mockedSequelize = new Sequelize(FAKE_DATABASE_NAME, null, null, SequelizeMocking.adaptSequelizeOptions(originalSequelize, options));

        mockedSequelize.__originalSequelize = originalSequelize;

        SequelizeMocking.copyCurrentModels(originalSequelize, mockedSequelize);
        SequelizeMocking.modifyConnection(originalSequelize, mockedSequelize);
        SequelizeMocking.modifyModelReferences(originalSequelize, mockedSequelize);
        SequelizeMocking.hookNewModel(originalSequelize, mockedSequelize, options);

        logging && console.log('SequelizeMocking - Mock the context');
        return mockedSequelize
            .sync()
            .then(function () {
                logging && console.log('SequelizeMocking - Database construction done');
                return mockedSequelize;
            });
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {string} fixtureFilePath
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise.<Sequelize>}
     */
    static createAndLoadFixtureFile(originalSequelize, fixtureFilePath, options) {
        let logging = !options || options.logging;

        return SequelizeMocking
            .create(originalSequelize, options)
            .then(function (mockedSequelize) {
                return SequelizeMocking
                    .loadFixtureFile(mockedSequelize, fixtureFilePath, options)
                    .then(function () {
                        logging && console.log('SequelizeMocking - Mocked data injected');
                        return mockedSequelize;
                    });
            });
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} mockedSequelize
     * @param {SequelizeMockingOptions} [options]
     */
    static hookNewModel(originalSequelize, mockedSequelize, options) {
        let logging = !options || options.logging;

        originalSequelize.addHook(AFTER_DEFINE_EVENT, AFTER_DEFINE_EVENT_NAME, function (newModel) {
            SequelizeMocking
                .modifyModelReference(
                    mockedSequelize,
                    SequelizeMocking.copyModel(mockedSequelize, newModel)
                )
                .sync({ 'hooks': true })
                .then(function () {
                    logging && console.log(`Model ${newModel.name} was declared into the database`);
                })
                .catch(function (err) {
                    logging && console.error(`An error occured when initializing the model ${newModel.name}`);
                    console.error(err && err.stack ? err.stack : err);
                    process.exit(1);
                });
        });
    }

    /**
     * @param {Sequelize} sequelize
     * @param {string | Array.<String>} fixtureFilePath
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise.<Sequelize>}
     */
    static loadFixtureFile(sequelize, fixtureFilePath, options) {
        let logging = !options || options.logging;
        let transformFixtureDataFn = !options || options.transformFixtureDataFn;
        let loadFixturesOptions = {
            'log': logging ? null : _.noop
        };

        if (_.isFunction(transformFixtureDataFn)) {
            loadFixturesOptions.transformFixtureDataFn = transformFixtureDataFn;
        }

        return sequelizeFixtures[Array.isArray(fixtureFilePath) ? 'loadFiles' : 'loadFile'](fixtureFilePath, SequelizeMocking.mapModels(sequelize), loadFixturesOptions)
            .then(function(){
                return sequelize;
            });

    }

    /**
     * @param sequelize
     * @returns {Object}
     */
    static mapModels(sequelize) {
        let map = { };

        sequelize.modelManager.all.forEach(function (model) {
            map[model.name] = model;
        });

        return map;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} newSequelizeToUse
     * @returns {Sequelize} The new Sequelize object
     */
    static modifyConnection(originalSequelize, newSequelizeToUse) {
        originalSequelize.__connectionManager = originalSequelize.connectionManager;
        originalSequelize.connectionManager = newSequelizeToUse.connectionManager;

        originalSequelize.__dialect = originalSequelize.dialect;
        originalSequelize.dialect = newSequelizeToUse.dialect;

        return newSequelizeToUse;
    }

    /**
     * Goal: the instanciate model shall use another instance of @{Sequelize} than the one used to create the model
     *
     * @param {Sequelize} newSequelizeToUse
     * @param {Sequelize.Model} model
     * @returns {Sequelize.Model}
     */
    static modifyModelReference (newSequelizeToUse, model) {
        model.sequelize = newSequelizeToUse;
        return model;
    }

    /**
     * @param {Sequelize} originalSequelize
     * @param {Sequelize} newSequelizeToUse
     * @returns {Sequelize} The new Sequelize object
     */
    static modifyModelReferences(originalSequelize, newSequelizeToUse) {
        originalSequelize.modelManager.all.forEach(function (model) {
            SequelizeMocking.modifyModelReference(newSequelizeToUse, model);
        });

        return newSequelizeToUse;
    }

    /**
     * @param {Sequelize} mockedSequelize
     * @param {SequelizeMockingOptions} [options]
     * @returns {Promise}
     */
    static restore(mockedSequelize, options) {
        let logging = !options || options.logging;

        SequelizeMocking.unhookNewModel(mockedSequelize);

        if (mockedSequelize.__originalSequelize) {
            SequelizeMocking.modifyModelReferences(mockedSequelize, mockedSequelize.__originalSequelize);
        }

        if (mockedSequelize.__dialect && mockedSequelize.__connectionManager) {
            SequelizeMocking.modifyConnection(mockedSequelize, mockedSequelize.__originalSequelize);
        }

        delete mockedSequelize.__originalSequelize;
        delete mockedSequelize.__dialect;
        delete mockedSequelize.__connectionManager;

        logging && console.log('SequelizeMocking - restore the context');
        return mockedSequelize
            .drop({ 'logging': logging })
            .then(function () {
                logging && console.log('SequelizeMocking - Context is restored');
            });
    }

    /**
     * @param {Sequelize} mockedSequelize
     */
    static unhookNewModel(mockedSequelize) {
        if (mockedSequelize.__originalSequelize) {
            mockedSequelize.__originalSequelize.removeHook(AFTER_DEFINE_EVENT, AFTER_DEFINE_EVENT_NAME);
        }
    }
}

module.exports = SequelizeMocking;
<|endoftext|>


<commit_before>// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

foam.ENUM({
  package: 'org.chromium.apis.web',
  name: 'ServerMode',

  values: ['DEV', 'PROD'],
});

foam.CLASS({
  package: 'org.chromium.apis.web',
  name: 'Server',
  extends: 'foam.net.node.Server',
  implements: ['foam.mlang.Expressions'],

  requires: [
    'foam.dao.LRUDAOManager',
    'foam.net.node.CacheHandler',
    'foam.net.node.DirTreeHandler',
    'foam.net.node.FileHandler',
    'foam.net.node.PathnameRouter',
    'foam.net.node.RestDAOHandler',
    'org.chromium.apis.web.BrowserMetricData',
    'org.chromium.apis.web.BrowserMetricDataType',
    'org.chromium.apis.web.DAOContainer',
    'org.chromium.apis.web.ServerMode',
  ],
  imports: [
    'apiVelocityDAO',
    'browserMetricsDAO',
    'releaseDAO',
    'releaseWebInterfaceJunctionDAO',
    'webInterfaceDAO',
  ],

  classes: [
    {
      name: 'CacheDAO',
      extends: 'foam.dao.ProxyDAO',

      requires: [
        'foam.net.node.CachedResponse',
        'foam.dao.MDAO',
      ],
      imports: ['info'],

      properties: [
        {
          name: 'of',
          factory: function() { return this.CachedResponse; },
        },
        {
          name: 'delegate',
          factory: function() {
            return this.MDAO.create({of: this.CachedResponse});
          },
        },
      ],

      methods: [
        function put_(ctx, cachedResponse) {
          // Cache successes.
          if (cachedResponse.statusCode >= 200 &&
              cachedResponse.statusCode < 300) {
            return this.delegate.put_(ctx, cachedResponse);
          }

          // Non-cached does not throw, but does not get stored either.
          this.info(`CacheDAO: Bypass cache storage: ${cachedResponse.id}`);
          return Promise.resolve(cachedResponse);
        },
      ],
    },
  ],

  properties: [
    {
      class: 'Enum',
      of: 'org.chromium.apis.web.ServerMode',
      name: 'mode',
      required: true,
      final: true
    },
    {
      class: 'FObjectProperty',
      of: 'foam.net.node.PathnameRouter',
      name: 'router',
      documentation: `Router for registering routes.`,
      factory: function() {
        return this.PathnameRouter.create();
      }
    },
    {
      class: 'FObjectProperty',
      of: 'foam.net.node.Handler',
      name: 'handler',
      documentation: `Server handler: Cache everything registered to router.`,
      factory: function() {
        // Non-prod server => no caching.
        return this.mode === this.ServerMode.PROD ? this.CacheHandler.create({
          delegate: this.router
        }) : this.router;
      },
    },
    {
      name: 'path',
      factory: function() { return require('path'); },
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'cache_',
    },
    {
      class: 'FObjectProperty',
      of: 'foam.dao.LRUDAOManager',
      name: 'cacheManager_',
    },
  ],

  methods: [
    function start() {

      // Initialize predictably on start().
      this.cache_ = this.CacheDAO.create();
      this.cacheManager_ = this.LRUDAOManager.create({
        dao: this.cache_,
        maxSize: 50,
      });

      this.addDir_('/images',
                   this.path.resolve(__dirname, '../../static/images'));
      this.addDir_('/bundle',
                   this.path.resolve(__dirname, '../../static/bundle'));
      this.addFile_('/blank.html',
                    this.path.resolve(__dirname, '../../static/blank.html'));
      this.addFile_('/index.html',
                    this.path.resolve(__dirname, '../../static/index.html'));
      this.addFile_('/',
                    this.path.resolve(__dirname, '../../static/index.html'));

      const EQ = this.EQ.bind(this);
      const Type = this.BrowserMetricDataType;
      const TYPE = this.BrowserMetricData.TYPE;

      this.addDAORoute_(this.DAOContainer.RELEASE_NAME, this.releaseDAO);
      this.addDAORoute_(this.DAOContainer.WEB_INTERFACE_NAME,
                       this.webInterfaceDAO);
      this.addDAORoute_(this.DAOContainer.RELEASE_WEB_INTERFACE_JUNCTION_NAME,
                       this.releaseWebInterfaceJunctionDAO);

      this.addDAORoute_(
          this.DAOContainer.FAILURE_TO_SHIP_NAME,
          this.browserMetricsDAO.where(EQ(TYPE, Type.FAILURE_TO_SHIP)));
      this.addDAORoute_(
          this.DAOContainer.BROWSER_SPECIFIC_NAME,
          this.browserMetricsDAO.where(EQ(TYPE, Type.BROWSER_SPECIFIC)));
      this.addDAORoute_(
          this.DAOContainer.AGGRESSIVE_REMOVAL_NAME,
          this.browserMetricsDAO.where(EQ(TYPE, Type.AGGRESSIVE_REMOVAL)));
      this.addDAORoute_(
          this.DAOContainer.API_VELOCITY_NAME, this.apiVelocityDAO);

      this.validate();

      this.SUPER();
    },
    function addFile_(pathname, filePath) {
      this.router.addPathname(pathname, this.FileHandler.create({
        filePath,
      }));
    },
    function addDir_(pathnamePrefix, dir) {
      this.router.addPathnamePrefix(pathnamePrefix, this.DirTreeHandler.create({
        dir,
      }));
    },
    function addDAORoute_(daoName, dao) {
      this.router.addPathnamePrefix(`/${daoName}`, this.RestDAOHandler.create({
        dao,
      }));
    },
  ],
});
<commit_msg>Add missing Server export: requestCacheDAO<commit_after>// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

foam.ENUM({
  package: 'org.chromium.apis.web',
  name: 'ServerMode',

  values: ['DEV', 'PROD'],
});

foam.CLASS({
  package: 'org.chromium.apis.web',
  name: 'Server',
  extends: 'foam.net.node.Server',
  implements: ['foam.mlang.Expressions'],

  requires: [
    'foam.dao.LRUDAOManager',
    'foam.net.node.CacheHandler',
    'foam.net.node.DirTreeHandler',
    'foam.net.node.FileHandler',
    'foam.net.node.PathnameRouter',
    'foam.net.node.RestDAOHandler',
    'org.chromium.apis.web.BrowserMetricData',
    'org.chromium.apis.web.BrowserMetricDataType',
    'org.chromium.apis.web.DAOContainer',
    'org.chromium.apis.web.ServerMode',
  ],
  imports: [
    'apiVelocityDAO',
    'browserMetricsDAO',
    'releaseDAO',
    'releaseWebInterfaceJunctionDAO',
    'webInterfaceDAO',
  ],
  exports: ['cache_ as requestCacheDAO'],

  classes: [
    {
      name: 'CacheDAO',
      extends: 'foam.dao.ProxyDAO',

      requires: [
        'foam.net.node.CachedResponse',
        'foam.dao.MDAO',
      ],
      imports: ['info'],

      properties: [
        {
          name: 'of',
          factory: function() { return this.CachedResponse; },
        },
        {
          name: 'delegate',
          factory: function() {
            return this.MDAO.create({of: this.CachedResponse});
          },
        },
      ],

      methods: [
        function put_(ctx, cachedResponse) {
          // Cache successes.
          if (cachedResponse.statusCode >= 200 &&
              cachedResponse.statusCode < 300) {
            return this.delegate.put_(ctx, cachedResponse);
          }

          // Non-cached does not throw, but does not get stored either.
          this.info(`CacheDAO: Bypass cache storage: ${cachedResponse.id}`);
          return Promise.resolve(cachedResponse);
        },
      ],
    },
  ],

  properties: [
    {
      class: 'Enum',
      of: 'org.chromium.apis.web.ServerMode',
      name: 'mode',
      required: true,
      final: true
    },
    {
      class: 'FObjectProperty',
      of: 'foam.net.node.PathnameRouter',
      name: 'router',
      documentation: `Router for registering routes.`,
      factory: function() {
        return this.PathnameRouter.create();
      }
    },
    {
      class: 'FObjectProperty',
      of: 'foam.net.node.Handler',
      name: 'handler',
      documentation: `Server handler: Cache everything registered to router.`,
      factory: function() {
        // Non-prod server => no caching.
        return this.mode === this.ServerMode.PROD ? this.CacheHandler.create({
          delegate: this.router
        }) : this.router;
      },
    },
    {
      name: 'path',
      factory: function() { return require('path'); },
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'cache_',
    },
    {
      class: 'FObjectProperty',
      of: 'foam.dao.LRUDAOManager',
      name: 'cacheManager_',
    },
  ],

  methods: [
    function start() {

      // Initialize predictably on start().
      this.cache_ = this.CacheDAO.create();
      this.cacheManager_ = this.LRUDAOManager.create({
        dao: this.cache_,
        maxSize: 50,
      });

      this.addDir_('/images',
                   this.path.resolve(__dirname, '../../static/images'));
      this.addDir_('/bundle',
                   this.path.resolve(__dirname, '../../static/bundle'));
      this.addFile_('/blank.html',
                    this.path.resolve(__dirname, '../../static/blank.html'));
      this.addFile_('/index.html',
                    this.path.resolve(__dirname, '../../static/index.html'));
      this.addFile_('/',
                    this.path.resolve(__dirname, '../../static/index.html'));

      const EQ = this.EQ.bind(this);
      const Type = this.BrowserMetricDataType;
      const TYPE = this.BrowserMetricData.TYPE;

      this.addDAORoute_(this.DAOContainer.RELEASE_NAME, this.releaseDAO);
      this.addDAORoute_(this.DAOContainer.WEB_INTERFACE_NAME,
                       this.webInterfaceDAO);
      this.addDAORoute_(this.DAOContainer.RELEASE_WEB_INTERFACE_JUNCTION_NAME,
                       this.releaseWebInterfaceJunctionDAO);

      this.addDAORoute_(
          this.DAOContainer.FAILURE_TO_SHIP_NAME,
          this.browserMetricsDAO.where(EQ(TYPE, Type.FAILURE_TO_SHIP)));
      this.addDAORoute_(
          this.DAOContainer.BROWSER_SPECIFIC_NAME,
          this.browserMetricsDAO.where(EQ(TYPE, Type.BROWSER_SPECIFIC)));
      this.addDAORoute_(
          this.DAOContainer.AGGRESSIVE_REMOVAL_NAME,
          this.browserMetricsDAO.where(EQ(TYPE, Type.AGGRESSIVE_REMOVAL)));
      this.addDAORoute_(
          this.DAOContainer.API_VELOCITY_NAME, this.apiVelocityDAO);

      this.validate();

      this.SUPER();
    },
    function addFile_(pathname, filePath) {
      this.router.addPathname(pathname, this.FileHandler.create({
        filePath,
      }));
    },
    function addDir_(pathnamePrefix, dir) {
      this.router.addPathnamePrefix(pathnamePrefix, this.DirTreeHandler.create({
        dir,
      }));
    },
    function addDAORoute_(daoName, dao) {
      this.router.addPathnamePrefix(`/${daoName}`, this.RestDAOHandler.create({
        dao,
      }));
    },
  ],
});
<|endoftext|>


<commit_before>import React, { Component, View, Text, TouchableHighlight, PropTypes, StyleSheet } from 'react-native'
import Icon from 'react-native-vector-icons/FontAwesome'

class ItemCell extends Component {
  _renderDisclosureIndicator (render) {
    if (render) {
      return <Icon style={styles.chevron} name='angle-right' size={22} />
    }
  }

  render () {
    let touchableProps = {
      accessible: this.props.accessible,
      delayLongPress: this.props.delayLongPress,
      delayPressIn: this.props.delayPressIn,
      delayPressOut: this.props.delayPressOut,
      onLongPress: this.props.onLongPress,
      onPress: this.props.onPress,
      onPressIn: this.props.onPressIn,
      onPressOut: this.props.onPressOut,
    }
    return (
      <TouchableHighlight {...touchableProps}
        underlayColor='#D9D9D9'
        style={styles.container}>
        <View style={styles.viewContainer}>
          <View style={styles.paddingView} />
          <View style={[styles.bottomBorder, styles.textContainer]}>
            <Text style={styles.text}>
              {this.props.children}
            </Text>
            {this._renderDisclosureIndicator(this.props.showDisclosureIndicator)}
          </View>
        </View>
      </TouchableHighlight>
    )
  }
}

ItemCell.propTypes = {
  ...TouchableHighlight.propTypes,
  children: PropTypes.string.isRequired,
  showDisclosureIndicator: PropTypes.bool,
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 11.75,
    backgroundColor: 'white',
  },
  viewContainer: {
    flex: 1,
    flexDirection: 'row',
    backgroundColor: 'transparent',
  },
  paddingView: {
    width: 15,
    backgroundColor: 'transparent',
  },
  textContainer: {
    flex: 1,
    flexDirection: 'row',
    paddingBottom: 11.75,
  },
  text: {
    flex: 1,
    fontSize: 16,
    alignSelf: 'center',
    backgroundColor: 'transparent',
  },
  chevron: {
    width: 25,
    paddingRight: 15,
    color: '#C8C7CC',
    alignSelf: 'center',
  },
  bottomBorder: {
    borderBottomWidth: 1/2,
    borderBottomColor: '#C8C7CC',
    borderStyle: 'solid',
  }
})

export default ItemCell
<commit_msg>Solved bottom line issue with RN 0.11.0<commit_after>import React, { View, Text, TouchableHighlight, PropTypes, StyleSheet } from 'react-native'
import Icon from 'react-native-vector-icons/FontAwesome'

class ItemCell extends React.Component {
  _renderDisclosureIndicator (render) {
    if (render) {
      return <Icon style={styles.chevron} name='angle-right' size={22} />
    }
  }

  render () {
    const touchableProps = {
      accessible: this.props.accessible,
      delayLongPress: this.props.delayLongPress,
      delayPressIn: this.props.delayPressIn,
      delayPressOut: this.props.delayPressOut,
      onLongPress: this.props.onLongPress,
      onPress: this.props.onPress,
      onPressIn: this.props.onPressIn,
      onPressOut: this.props.onPressOut,
    }
    return (
      <TouchableHighlight {...touchableProps}
        underlayColor='#D9D9D9'
        style={styles.container}>
        <View style={styles.viewContainer}>
          <View style={styles.paddingView} />
          <View style={[styles.bottomBorder, styles.textContainer]}>
            <Text style={styles.text}>
              {this.props.children}
            </Text>
            {this._renderDisclosureIndicator(this.props.showDisclosureIndicator)}
          </View>
        </View>
      </TouchableHighlight>
    )
  }
}

ItemCell.propTypes = {
  ...TouchableHighlight.propTypes,
  children: PropTypes.string.isRequired,
  showDisclosureIndicator: PropTypes.bool,
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 11.75,
    backgroundColor: 'white',
  },
  viewContainer: {
    flex: 1,
    flexDirection: 'row',
    backgroundColor: 'transparent',
  },
  paddingView: {
    width: 15,
    backgroundColor: 'transparent',
  },
  textContainer: {
    flex: 1,
    flexDirection: 'row',
    paddingBottom: 11.75,
  },
  text: {
    flex: 1,
    fontSize: 16,
    alignSelf: 'center',
    backgroundColor: 'transparent',
  },
  chevron: {
    width: 25,
    paddingRight: 15,
    color: '#C8C7CC',
    alignSelf: 'center',
  },
  bottomBorder: {
    borderBottomWidth: 1,
    borderBottomColor: '#C8C7CC',
    borderStyle: 'solid',
  }
})

export default ItemCell
<|endoftext|>


<commit_before>/**
 * @module  web
 * methods for browsers and business requirement

 * - domready
 * - data storage
 * - getLocation
 * - UA
 */

;(function(K, undef){

// --- method for KM.data ----------------------- *\
function setData(data){
	data && K.mix(stored_data, data);
};

function cloneData(){
	return K.merge({}, stored_data);
};

function getData(name){
	return stored_data[name];
};
// --- /method for KM.data ----------------------- *\

/**
 * get the readable location object of current page
 */
function getCurrentLocation(){
	var L, H;

	// IE may throw an exception when accessing
	// a field from document.location if document.domain has been set
	// ref:
	// stackoverflow.com/questions/1498788/reading-window-location-after-setting-document-domain-in-ie6
	try {
		L = DOC.location;
		H = L.href;
	} catch(e) {

		// Use the href attribute of an A element
		// since IE will modify it given document.location
		H = DOC.createElement('a');
		H.href = EMPTY;
		H = H.href;

		L = parseLocation(H);
	}

	return L;
};

/**
 * parse a link to location object
 * @param {string} H
 * @return {Object} custom location object
 */
function parseLocation(H){
	var E = nullOrEmpty, port;

	H = H.match(REGEX_URL);

	port = H[3];

	return {
		href: 		H[0],
		protocol:	H[1],
		host:		H[2] + (port ? ':' + port : EMPTY),
		hostname:	H[2],
		port:		E( port ),
		pathname:	E( H[4] ),
		search:		E( H[5] ),
		hash:		E( H[6] )
	};
};

function nullOrEmpty(str){
	return str || EMPTY;
};

	// @type {Object}
var stored_data = {},

    is_domready = false,
	is_domready_binded = false,
    is_loaded = false,

    readyList = [],

	// @const
	WIN = K.__HOST,
	DOC = WIN.document,
	EMPTY = '',
	_Browser = Browser,
	ua = navigator.userAgent,
	UA = K.UA,
	REGEX_WEBKIT = /webkit[ \/]([^\s]+)/i,

    /*
    REGEX_URL = /^
    	([\w\+\.\-]+:)		// protocol
    	\/\/
    	([^\/?#:]+)			// domain
    (?::
    	(\d+)				// port
    )?
    	(/[^?#]*)?			// pathname
    	(\?[^?#]*)?			// search
    	(#.*)?				// hash
    $/
    */

    REGEX_URL = /^([\w\+\.\-]+:)\/\/([^\/?#:]+)(?::(\d+))?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;

K.mix(K, {

	/**
	 * module  data

	 * setter
	 * getter

	 * usage:
	 * 1. KM.data()                     returns the shadow copy of the current data stack
	 * 2. KM.data('abc')                returns the data named 'abc'
	 * 3. KM.data('abc', 123)           set the value of 'abc' as 123
	 * 4. KM.data({abc: 123, edf:456})  batch setter
	 *
	 * @param {all=} value
	 */
	data: function(name, value){
		var type = K._type(name),
			empty_obj = {},
			is_getter, ret;

		if(name === undef){
			ret = cloneData(); // get: return shadow copy
			is_getter = true;

		}else if(type === 'string'){
			if(value === undef){
				ret = getData(name); // get: return the value of name
				is_getter = true;
			}else{
				empty_obj[name] = value;
				setData(empty_obj) // set
			}

		}else if(type === 'object'){
			setData(name); // set
		}

		return is_getter ? ret : K;
	},

	isDomReady: function(){
		return is_domready;
	},

	isLoaded: function(){
		return is_loaded;
	},

	/**
	 * the entire entry for domready event
	 * window.addEvent('domready', fn) has been carried here, and has no more support
	 * @param {function()} fn the function to be executed when dom is ready
	 */
	ready: function(fn){
		// delay the initialization of binding domready, making page render faster
		is_domready_binded || bind_domready();

		if(is_domready){
			fn.call(WIN, this);
		}else{
			readyList.push(fn);
		}
	},

	/**
	 * @param {string} href
	 * @return {Object}
	 *	- if href undefined, returns the current location
	 *	- if href is a string, returns the parsed loaction
	 */
	getLocation: function(href){
		return href ?
			parseLocation(href)
		:	getCurrentLocation();
	}

});

/**
 * user agent

 type {number} KM.UA.<browser> major version of current browser, default to undefined
 - KM.UA.ie
 - KM.UA.firefox
 - KM.UA.opera
 - KM.UA.chrome
 - KM.UA.webkit

 usage:

 	if(KM.UA.ie < 8){...}

 	// always instead of using this:
 	if(Browser.ie && Browser.version < 8){...}

 */
['ie', 'firefox', 'opera', 'chrome'].each(function(name){
	var B = _Browser;

	B[name] && (UA[name] = B.version);
});

K.mix(UA, _Browser.Platform, true, ['mac', 'win', 'linux', 'ios', 'android', 'webos', 'other']);
UA.platform = _Browser.Platform.name;

// UA.fullversion = String(_Browser.version);

(function(UA){
	var m = ua.match(REGEX_WEBKIT);

	if(m){
		UA.webkit = parseInt( m[1] );
	}
})(K.UA);

/**
 * load event
 */
WIN.addEvent('load', function(){
	is_loaded = true;
});

/**
 * fix bugs of mootools
 * ----------------------------------------------------------------------------------------------------------- */

// in mootools 1.3.2, on ie < 8, document.body and html must be 'dollar'ed before the using of many methods, esp in Element.dimension
$(document.body);
$(document.documentElement);

/**
 * Custom domready event
 * @private
 * ----------------------------------------------------------------------------------------------------------- */

function domready(){

	// fire domready only once
	if(!is_domready){
		is_domready = true;
		fire_domready();
	}
}

function fire_domready(){
	var self = this,
		r = readyList, fn;

	if(r){
		for(var i = 0, len = r.length; i < len; i ++){
			fn = r[i]
			fn && fn.call(WIN, K);
		}

		r.length = 0;
		readyList = null;
	}
};

function bind_domready(){
	var COMPLETE = 'complete', doc = DOC,
		doScroll = doc.documentElement.doScroll,
		eventType = doScroll ? 'readystatechange' : 'DOMContentLoaded';

	is_domready_binded = true;

	// Catch cases where ready() is called after the
	// browser event has already occurred.
	if(doc.readyState === COMPLETE) return domready();

	function ready(){
		doc.removeListener(eventType, ready).removeListener('load', ready)
		domready();
	}

	doc.addListener(eventType, ready);

	// A fallback to load
	// and make sure that domready event fires before load event registered by user
	doc.addListener('load', ready);

	if(doScroll){
		var not_framed = false;

		try {
			not_framed = win.frameElement == null;
		} catch(e) {}

		if(not_framed){

			// use conditional function declaration, poll_scroll will not be declared in firefox, that it will save memory
			function poll_scroll(){
				try {
					// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
					doScroll('left');
					ready();
				} catch(ex) {
					setTimeout(poll_scroll, 10);
				}
			}
			poll_scroll();
		}
	}
}

})(KM);

/**
 TODO:
 - add more user-agent datas.

 change log:
 2011-07-05  Kael:
 - remove UA.fullversion
 - add adapter for Browser.Platform

 2011-06-12  Kael:
 - fix a bug about the regular expression of location pattern that more than one question mark should be allowed in search query
 - add UA.chrome.

 2011-04-26  Kael:
 - adapt mootools.Browser
 - remove ua.chrome, ua.safari, add ua.webkit

 2011-04-12  Kael Zhang:
 - fix a bug that domready could not be properly fired
 - add KM.getLocation method,
 	1. to fix the bug of ie6, which will cause an exception when fetching the value of window.location if document.domain is already specified
 	2. which can split an specified uri into different parts

 2010-12-31  Kael Zhang:
 - migrate domready event out from mootools to here, and change some way
 - migrate .data and .delay methods from core/lang to here

 */<commit_msg>fix the bug of mootools on ie < 8 that document.body and html don't support most of the method, because they haven't dollared yet<commit_after>/**
 * @module  web
 * methods for browsers and business requirement

 * - domready
 * - data storage
 * - getLocation
 * - UA
 */

;(function(K, undef){

// --- method for KM.data ----------------------- *\
function setData(data){
	data && K.mix(stored_data, data);
};

function cloneData(){
	return K.merge({}, stored_data);
};

function getData(name){
	return stored_data[name];
};
// --- /method for KM.data ----------------------- *\

/**
 * get the readable location object of current page
 */
function getCurrentLocation(){
	var L, H;

	// IE may throw an exception when accessing
	// a field from document.location if document.domain has been set
	// ref:
	// stackoverflow.com/questions/1498788/reading-window-location-after-setting-document-domain-in-ie6
	try {
		L = DOC.location;
		H = L.href;
	} catch(e) {

		// Use the href attribute of an A element
		// since IE will modify it given document.location
		H = DOC.createElement('a');
		H.href = EMPTY;
		H = H.href;

		L = parseLocation(H);
	}

	return L;
};

/**
 * parse a link to location object
 * @param {string} H
 * @return {Object} custom location object
 */
function parseLocation(H){
	var E = nullOrEmpty, port;

	H = H.match(REGEX_URL);

	port = H[3];

	return {
		href: 		H[0],
		protocol:	H[1],
		host:		H[2] + (port ? ':' + port : EMPTY),
		hostname:	H[2],
		port:		E( port ),
		pathname:	E( H[4] ),
		search:		E( H[5] ),
		hash:		E( H[6] )
	};
};

function nullOrEmpty(str){
	return str || EMPTY;
};

	// @type {Object}
var stored_data = {},

    is_domready = false,
	is_domready_binded = false,
    is_loaded = false,

    readyList = [],

	// @const
	WIN = K.__HOST,
	DOC = WIN.document,
	EMPTY = '',
	_Browser = Browser,
	ua = navigator.userAgent,
	UA = K.UA,
	REGEX_WEBKIT = /webkit[ \/]([^\s]+)/i,

    /*
    REGEX_URL = /^
    	([\w\+\.\-]+:)		// protocol
    	\/\/
    	([^\/?#:]+)			// domain
    (?::
    	(\d+)				// port
    )?
    	(/[^?#]*)?			// pathname
    	(\?[^?#]*)?			// search
    	(#.*)?				// hash
    $/
    */

    REGEX_URL = /^([\w\+\.\-]+:)\/\/([^\/?#:]+)(?::(\d+))?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;

K.mix(K, {

	/**
	 * module  data

	 * setter
	 * getter

	 * usage:
	 * 1. KM.data()                     returns the shadow copy of the current data stack
	 * 2. KM.data('abc')                returns the data named 'abc'
	 * 3. KM.data('abc', 123)           set the value of 'abc' as 123
	 * 4. KM.data({abc: 123, edf:456})  batch setter
	 *
	 * @param {all=} value
	 */
	data: function(name, value){
		var type = K._type(name),
			empty_obj = {},
			is_getter, ret;

		if(name === undef){
			ret = cloneData(); // get: return shadow copy
			is_getter = true;

		}else if(type === 'string'){
			if(value === undef){
				ret = getData(name); // get: return the value of name
				is_getter = true;
			}else{
				empty_obj[name] = value;
				setData(empty_obj) // set
			}

		}else if(type === 'object'){
			setData(name); // set
		}

		return is_getter ? ret : K;
	},

	isDomReady: function(){
		return is_domready;
	},

	isLoaded: function(){
		return is_loaded;
	},

	/**
	 * the entire entry for domready event
	 * window.addEvent('domready', fn) has been carried here, and has no more support
	 * @param {function()} fn the function to be executed when dom is ready
	 */
	ready: function(fn){
		// delay the initialization of binding domready, making page render faster
		is_domready_binded || bind_domready();

		if(is_domready){
			fn.call(WIN, this);
		}else{
			readyList.push(fn);
		}
	},

	/**
	 * @param {string} href
	 * @return {Object}
	 *	- if href undefined, returns the current location
	 *	- if href is a string, returns the parsed loaction
	 */
	getLocation: function(href){
		return href ?
			parseLocation(href)
		:	getCurrentLocation();
	}

});

/**
 * user agent

 type {number} KM.UA.<browser> major version of current browser, default to undefined
 - KM.UA.ie
 - KM.UA.firefox
 - KM.UA.opera
 - KM.UA.chrome
 - KM.UA.webkit

 usage:

 	if(KM.UA.ie < 8){...}

 	// always instead of using this:
 	if(Browser.ie && Browser.version < 8){...}

 */
['ie', 'firefox', 'opera', 'chrome'].each(function(name){
	var B = _Browser;

	B[name] && (UA[name] = B.version);
});

K.mix(UA, _Browser.Platform, true, ['mac', 'win', 'linux', 'ios', 'android', 'webos', 'other']);
UA.platform = _Browser.Platform.name;

// UA.fullversion = String(_Browser.version);

(function(UA){
	var m = ua.match(REGEX_WEBKIT);

	if(m){
		UA.webkit = parseInt( m[1] );
	}
})(K.UA);

/**
 * load event
 */
WIN.addEvent('load', function(){
	is_loaded = true;
});

/**
 * fix bugs of mootools
 * ----------------------------------------------------------------------------------------------------------- */

// in mootools 1.3.2, on ie < 8, document.body and html must be 'dollar'ed before the using of most of the methods, esp in Element.dimension
$(document.body);
$(document.documentElement);

/**
 * Custom domready event
 * @private
 * ----------------------------------------------------------------------------------------------------------- */

function domready(){

	// fire domready only once
	if(!is_domready){
		is_domready = true;
		fire_domready();
	}
}

function fire_domready(){
	var self = this,
		r = readyList, fn;

	if(r){
		for(var i = 0, len = r.length; i < len; i ++){
			fn = r[i]
			fn && fn.call(WIN, K);
		}

		r.length = 0;
		readyList = null;
	}
};

function bind_domready(){
	var COMPLETE = 'complete', doc = DOC,
		doScroll = doc.documentElement.doScroll,
		eventType = doScroll ? 'readystatechange' : 'DOMContentLoaded';

	is_domready_binded = true;

	// Catch cases where ready() is called after the
	// browser event has already occurred.
	if(doc.readyState === COMPLETE) return domready();

	function ready(){
		doc.removeListener(eventType, ready).removeListener('load', ready)
		domready();
	}

	doc.addListener(eventType, ready);

	// A fallback to load
	// and make sure that domready event fires before load event registered by user
	doc.addListener('load', ready);

	if(doScroll){
		var not_framed = false;

		try {
			not_framed = win.frameElement == null;
		} catch(e) {}

		if(not_framed){

			// use conditional function declaration, poll_scroll will not be declared in firefox, that it will save memory
			function poll_scroll(){
				try {
					// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
					doScroll('left');
					ready();
				} catch(ex) {
					setTimeout(poll_scroll, 10);
				}
			}
			poll_scroll();
		}
	}
}

})(KM);

/**
 TODO:
 - add more user-agent datas.

 change log:
 2011-07-05  Kael:
 - remove UA.fullversion
 - add adapter for Browser.Platform

 2011-06-12  Kael:
 - fix a bug about the regular expression of location pattern that more than one question mark should be allowed in search query
 - add UA.chrome.

 2011-04-26  Kael:
 - adapt mootools.Browser
 - remove ua.chrome, ua.safari, add ua.webkit

 2011-04-12  Kael Zhang:
 - fix a bug that domready could not be properly fired
 - add KM.getLocation method,
 	1. to fix the bug of ie6, which will cause an exception when fetching the value of window.location if document.domain is already specified
 	2. which can split an specified uri into different parts

 2010-12-31  Kael Zhang:
 - migrate domready event out from mootools to here, and change some way
 - migrate .data and .delay methods from core/lang to here

 */<|endoftext|>


<commit_before>var couple = require('../couple');
var messenger = require('messenger-memory');
var signaller = require('rtc-signaller');
var test = require('tape');
var rtc = require('..');
var conns = [];
var signallers = [];
var monitors = [];

var dcConstraints = {
  mandatory: {
    DtlsSrtpKeyAgreement: false,
    internalSctpDataChannels: false
  },
  optional: [
    { RtpDataChannels: true }
  ]
};

test('create peer connections', function(t) {
  t.plan(2);

  t.ok(conns[0] = rtc.createConnection({}, dcConstraints), 'created a');
  t.ok(conns[1] = rtc.createConnection({}, dcConstraints), 'created b');
});

test('create signallers', function(t) {
  t.plan(2);

  t.ok(signallers[0] = signaller(messenger()), 'created signaller a');
  t.ok(signallers[1] = signaller(messenger()), 'created signaller b');
});

test('announce signallers', function(t) {
  t.plan(1);
  signallers[0].announce();
  signallers[1].announce();

  setTimeout(t.pass.bind(t, 'done'), 50);
});

test('couple a --> b', function(t) {
  t.plan(1);

  t.ok(
    monitors[0] = couple(conns[0], signallers[1].id, signallers[0]),
    'ok'
  );
});

test('couple b --> a', function(t) {
  t.plan(1);
  t.ok(
    monitors[1] = couple(conns[1], signallers[0].id, signallers[1]),
    'ok'
  );
});

test('activate connection', function(t) {
  t.plan(monitors.length);

  monitors.forEach(function(mon, index) {
    mon.once('active', t.pass.bind(t, 'connection ' + index + ' active'));
  });

  monitors[0].createOffer();
});

// test('create a data channel on a', function(t) {
//   t.plan(2);

//   conns[1].addEventListener('datachannel', function(evt) {
//     t.pass('got data channel');
//   });

//   t.ok(
//     conns[0].createDataChannel('RTCDataChannel', { reliable: false }),
//     'a created'
//   );
// });

// test('close connections', function(t) {
//   t.plan(2);

//   a.once('close', t.pass.bind(t, 'a closed'));
//   b.once('close', t.pass.bind(t, 'b closed'));

//   a.close();
//   b.close();
// });<commit_msg>Added some messenging delay into coupling logic<commit_after>var couple = require('../couple');
var messenger = require('messenger-memory');
var signaller = require('rtc-signaller');
var test = require('tape');
var rtc = require('..');
var conns = [];
var signallers = [];
var monitors = [];
var scope = [];
var messengers = [];

var dcConstraints = {
  mandatory: {
    DtlsSrtpKeyAgreement: false,
    internalSctpDataChannels: false
  },
  optional: [
    { RtpDataChannels: true }
  ]
};

test('create peer connections', function(t) {
  t.plan(2);

  t.ok(conns[0] = rtc.createConnection({}, dcConstraints), 'created a');
  t.ok(conns[1] = rtc.createConnection({}, dcConstraints), 'created b');
});

test('create test messengers', function(t) {
  t.plan(1);
  messengers = [
    messenger({ delay: Math.random() * 200, scope: scope }),
    messenger({ delay: Math.random() * 200, scope: scope })
  ];
  t.ok(messengers.length == 2, 'created');
});

test('create signallers', function(t) {
  t.plan(2);

  t.ok(signallers[0] = signaller(messengers[0]), 'created signaller a');
  t.ok(signallers[1] = signaller(messengers[1]), 'created signaller b');
});

test('announce signallers', function(t) {
  t.plan(1);
  signallers[0].announce();
  signallers[1].announce();

  // TODO: do this better....
  setTimeout(t.pass.bind(t, 'done'), 600);
});

test('couple a --> b', function(t) {
  t.plan(1);

  t.ok(
    monitors[0] = couple(conns[0], signallers[1].id, signallers[0]),
    'ok'
  );
});

test('couple b --> a', function(t) {
  t.plan(1);
  t.ok(
    monitors[1] = couple(conns[1], signallers[0].id, signallers[1]),
    'ok'
  );
});

test('activate connection', function(t) {
  t.plan(monitors.length);

  monitors.forEach(function(mon, index) {
    mon.once('active', t.pass.bind(t, 'connection ' + index + ' active'));
  });

  monitors[0].createOffer();
});

// test('create a data channel on a', function(t) {
//   t.plan(2);

//   conns[1].addEventListener('datachannel', function(evt) {
//     t.pass('got data channel');
//   });

//   t.ok(
//     conns[0].createDataChannel('RTCDataChannel', { reliable: false }),
//     'a created'
//   );
// });

// test('close connections', function(t) {
//   t.plan(2);

//   a.once('close', t.pass.bind(t, 'a closed'));
//   b.once('close', t.pass.bind(t, 'b closed'));

//   a.close();
//   b.close();
// });<|endoftext|>


<commit_before>(function (deku, compiler) {
  var core = {
    isObject: function (obj) {
      return obj === Object(obj);
    },

    isArray: Array.isArray || function (obj) {
      return Object.prototype.toString.call(obj) === '[object Array]';
    },

    extend: function (target) {
      var len = arguments.length;
      var i;
      var source;
      var key;
      if (target == null) {
        return target;
      }
      for (i = 1; i < len; i++) {
        source = arguments[i];
        if (source != null) {
          for (key in source) {
            if (target[key] === void 0) {
              target[key] = source[key];
            }
          }
        }
      }
      return target;
    },

    escape: (function () {
      var map = {
        '&': '&',
        '"': '"',
        "'": ''',
        '<': '<',
        '>': '>'
      };
      var checkRegex = /[&"'<>]/;
      var replaceRegex = /[&"'<>]/g;
      return function (value) {
        if (value == null) {
          return '';
        }
        value = String(value);
        if (!checkRegex.test(value)) {
          return value;
        }
        return value.replace(replaceRegex, function (s) {
          return map[s];
        });
      }
    }()),

    compile: typeof compiler !== 'undefined' ? compiler.compile : function () {
      throw new Error("The deku.runtime.js doesn't support to compile partial templates. " +
        "Consider to compile all partial templates with deku compiler in advance.");
    },

    handleBlock: function (context, contextStack, value, fn) {
      var result = '';
      var i;
      var len;
      var element;
      if (core.isArray(value)) {
        len = value.length;
        for (i = 0; i < len; i++) {
          element = value[i];
          contextStack.push(element);
          result += fn(element, contextStack, i, i + 1 < len);
          contextStack.pop();
        }
      } else if (core.isObject(value)) {
        contextStack.push(value);
        result = fn(value, contextStack);
        contextStack.pop();
      } else if (value) {
        result = fn(context, contextStack);
      }
      return result;
    },

    handleInverse: function (context, contextStack, value, fn) {
      var result = '';
      if (!value) {
        result = fn(context, contextStack);
      } else if (core.isArray(value) && value.length === 0) {
        result = fn(context, contextStack);
      }
      return result;
    },

    prepare: function (source, options) {
      var template = typeof source === 'string' ? core.compile(source) : source;
      var templateContext = {
        escape: core.escape,
        compile: core.compile,
        handleBlock: core.handleBlock,
        handleInverse: core.handleInverse,
        noSuchValue: options.noSuchValue,
        noSuchPartial: options.noSuchPartial,
        noSuchProcessor: options.noSuchProcessor,
        prePipeline: options.prePipeline,
        postPipeline: options.postPipeline,
        templates: options.templates,
        processors: options.processors
      };
      return {
        render: function (data) {
          return template.call(templateContext, data);
        }
      };
    }
  };

  if (typeof module !== 'undefined') {
    module.exports = core;
  } else {
    deku.internal.core = core;
  }

}(typeof module !== 'undefined' ? {} : deku,
  typeof module !== 'undefined' ? require('./compiler') : deku.internal.compiler));<commit_msg>use empty object when passed data is null or undefined.<commit_after>(function (deku, compiler) {
  var core = {
    isObject: function (obj) {
      return obj === Object(obj);
    },

    isArray: Array.isArray || function (obj) {
      return Object.prototype.toString.call(obj) === '[object Array]';
    },

    extend: function (target) {
      var len = arguments.length;
      var i;
      var source;
      var key;
      if (target == null) {
        return target;
      }
      for (i = 1; i < len; i++) {
        source = arguments[i];
        if (source != null) {
          for (key in source) {
            if (target[key] === void 0) {
              target[key] = source[key];
            }
          }
        }
      }
      return target;
    },

    escape: (function () {
      var map = {
        '&': '&',
        '"': '"',
        "'": ''',
        '<': '<',
        '>': '>'
      };
      var checkRegex = /[&"'<>]/;
      var replaceRegex = /[&"'<>]/g;
      return function (value) {
        if (value == null) {
          return '';
        }
        value = String(value);
        if (!checkRegex.test(value)) {
          return value;
        }
        return value.replace(replaceRegex, function (s) {
          return map[s];
        });
      }
    }()),

    compile: typeof compiler !== 'undefined' ? compiler.compile : function () {
      throw new Error("The deku.runtime.js doesn't support to compile partial templates. " +
        "Consider to compile all partial templates with deku compiler in advance.");
    },

    handleBlock: function (context, contextStack, value, fn) {
      var result = '';
      var i;
      var len;
      var element;
      if (core.isArray(value)) {
        len = value.length;
        for (i = 0; i < len; i++) {
          element = value[i];
          contextStack.push(element);
          result += fn(element, contextStack, i, i + 1 < len);
          contextStack.pop();
        }
      } else if (core.isObject(value)) {
        contextStack.push(value);
        result = fn(value, contextStack);
        contextStack.pop();
      } else if (value) {
        result = fn(context, contextStack);
      }
      return result;
    },

    handleInverse: function (context, contextStack, value, fn) {
      var result = '';
      if (!value) {
        result = fn(context, contextStack);
      } else if (core.isArray(value) && value.length === 0) {
        result = fn(context, contextStack);
      }
      return result;
    },

    prepare: function (source, options) {
      var template = typeof source === 'string' ? core.compile(source) : source;
      var templateContext = {
        escape: core.escape,
        compile: core.compile,
        handleBlock: core.handleBlock,
        handleInverse: core.handleInverse,
        noSuchValue: options.noSuchValue,
        noSuchPartial: options.noSuchPartial,
        noSuchProcessor: options.noSuchProcessor,
        prePipeline: options.prePipeline,
        postPipeline: options.postPipeline,
        templates: options.templates,
        processors: options.processors
      };
      return {
        render: function (data) {
          return template.call(templateContext, data  != null ? data : {});
        }
      };
    }
  };

  if (typeof module !== 'undefined') {
    module.exports = core;
  } else {
    deku.internal.core = core;
  }

}(typeof module !== 'undefined' ? {} : deku,
  typeof module !== 'undefined' ? require('./compiler') : deku.internal.compiler));<|endoftext|>


<commit_before>Jiquid.System = function(layer, world) {

	this.wall = new Jello.AABB(
		new Jello.Vector2(-50, 2),
		new Jello.Vector2(50, 50)
	);
	this.boundaries = new Jello.AABB();
	this.materials = [];
	this.gravity = new Jello.Vector2(0,-0.05);// 0.004, 0.02
	this.particles = [];
	this.grid = [];
	this.gsizeY = 0;

	this.datGui = new dat.GUI();
	this.datGui.add(this.gravity, "x").min(-0.2).max(0.2).step(-0.01);
	this.datGui.add(this.gravity, "y").min(-0.2).max(0.2).step(-0.01);

	this.materials.push(new Jiquid.Material()
		.addDebugGui(this.datGui.addFolder("Mat1"))
		.setParticleMass(1)
		.setRestDensity(2)
		.setStiffness(1)
		.setBulkViscosity (1)
		.setElasticity(0)
		.setShearViscosity(0.5)
		.setMeltRate(0.0) // unused
		.setSmoothing(0.5)
		.setYieldPoint(0.1) // unused
		.setYieldRate(0.5));

	this.materials.push(new Jiquid.Material()
		.addDebugGui(this.datGui.addFolder("Mat2"))
		.setParticleMass(1)
		.setRestDensity(8)
		.setStiffness(0.0625)
		.setBulkViscosity (0.5)
		.setElasticity(0.5)
		.setShearViscosity(0.1)
		.setMeltRate(0.0) // unused
		.setSmoothing(1.0)
		.setYieldPoint(0.1) // unused
		.setYieldRate(0.5));

    for (var i = -50; i < -10; i++) {
        for (var j = 10; j < 50; j++) {
        	this.particles.push(new Jiquid.Particle(i, j, 0.1, 0, this.materials[0]));
        }
    }

    for (var i = 0; i < 50; i++) {
        for (var j = 10; j < 50; j++) {
        	this.particles.push(new Jiquid.Particle(i, j, -0.1, 0, this.materials[1]));
        }
    }

    // expand boundaries to include all particles
    this.boundaries.clear();
    _.each(this.particles, function(p) {
    	this.boundaries.expandToInclude(p.position);
    }, this);

	// expand boundaries a bit further
	this.boundaries.Min.x = Math.floor(this.boundaries.Min.x-1);
	this.boundaries.Min.y = Math.floor(this.boundaries.Min.y-1);
	this.boundaries.Max.x = Math.floor(this.boundaries.Max.x+3);
	this.boundaries.Max.y = Math.floor(this.boundaries.Max.y+3);

    this.simulator = new Jiquid.Simulator();
};

Jiquid.System.prototype.getNumberOfParticles = function() {
	return this.particles.length;
};

Jiquid.System.prototype.update = function() {
	this.simulator.update(this);

	// clear grid
	this.grid.length = 0;
	this.gsizeY = Math.floor(this.boundaries.Max.y-this.boundaries.Min.y);

	var activeCount = 0;

	// calculate particle kernels, and add density and density gradients to the grid
	_.each(this.particles, function(p) {
		p.cx = Math.floor(p.position.x - this.boundaries.Min.x - 0.5); // get cell x
		p.cy = Math.floor(p.position.y - this.boundaries.Min.y - 0.5); // get cell y
		p.gridIndex = p.cx * this.gsizeY + p.cy;

		var x = p.cx - (p.position.x - this.boundaries.Min.x);
		p.px[0] = (0.5 * x * x + 1.5 * x + 1.125);
		p.gx[0] = (x++ + 1.5);
		p.px[1] = (-x * x + 0.75);
		p.gx[1] = (-2.0 * (x++));
		p.px[2] = (0.5 * x * x - 1.5 * x + 1.125);
		p.gx[2] = (x - 1.5);

		var y = p.cy - (p.position.y - this.boundaries.Min.y);
		p.py[0] = (0.5 * y * y + 1.5 * y + 1.125);
		p.gy[0] = (y++ + 1.5);
		p.py[1] = (-y * y + 0.75);
		p.gy[1] = (-2.0 * (y++));
		p.py[2] = (0.5 * y * y - 1.5 * y + 1.125);
		p.gy[2] = (y - 1.5);

		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
            var pgxi = p.gx[i];
			for (var j = 0; j < 3; j++) {
				var gaj = ga+j;
				var n = this.grid[gaj];
				if (n === undefined) {
                    this.grid[gaj] = n = new Jiquid.Node();
                    activeCount++;
				}
				phi = pxi * p.py[j];
				n.m += phi;
				n.gx += pgxi * p.py[j];
				n.gy += pxi * p.gy[j];
			}
		}
	}, this);

	var loadedMaterial;
	var stiffness,
		restDensity,
		elasticity,
		viscosity,
		elasticityS,
		viscosityS,
		bulkViscosityS;

	// Sum particle density from grid, and add pressure and elastic forces to grid
	_.each(this.particles, function(p) {
        if (p.material != loadedMaterial) {
            loadedMaterial = p.material;

            stiffness = loadedMaterial.stiffness;
            restDensity = loadedMaterial.restDensity;
            elasticity = loadedMaterial.elasticity;
            viscosity = loadedMaterial.viscosity;

            elasticityS = loadedMaterial.elasticity;
            viscosityS = loadedMaterial.viscosity;
            bulkViscosityS = loadedMaterial.bulkViscosity;
        }

        var density = 0;
		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				var phi = pxi * p.py[j];
				density += phi*n.m;
			}
		}
		var pressure = (density-restDensity)/restDensity;
		if (pressure > 4.0)
			pressure = 4.0;

		var fx = 0, fy = 0;
		if (p.position.x < this.wall.Min.x) {
			fx += this.wall.Min.x - p.position.x;
            p.velocity.x *= 0.1;
		}
		if (p.position.x > this.wall.Max.x) {
			fx += this.wall.Max.x - p.position.x;
            p.velocity.x *= 0.1;
		}
		if (p.position.y < this.wall.Min.y) {
			fy += this.wall.Min.y - p.position.y;
            p.velocity.y *= 0.1;
		}
		if (p.position.y > this.wall.Max.y) {
			fy += this.wall.Max.y - p.position.y;
            p.velocity.y *= 0.1;
		}

		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
            var gxi = p.gx[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				n.acceleration.x += -((gxi * p.py[j]) * pressure) + fx * phi;
				n.acceleration.y += -((pxi * p.gy[j]) * pressure) + fy * phi;
			}
		}
	}, this);

    // divide grid acceleration by mass
	_.each(this.grid, function(n) {
		if (n.m > 0.0) {
			n.acceleration.divFloatSelf(n.m);
			n.acceleration.addSelf(this.gravity);
		}
	}, this);

	// accelerate particles and interpolate velocity back to grid
	_.each(this.particles, function(p) {
		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				p.velocity.addSelf(n.acceleration.mulFloat(phi));
			}
		}
		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				n.velocity.addSelf(p.velocity.mulFloat(phi));
			}
		}
	}, this);

	// divide grid velocity by mass
	_.each(this.grid, function(n) {
		if (n.m > 0.0) {
			n.velocity.divFloatSelf(n.m);
		}
	}, this);

	this.boundaries.clear();

	loadedMaterial = undefined;
	var smoothing,
		yieldRate,
		elastic;

	// advance particles
	_.each(this.particles, function(p) {
        if (p.material != loadedMaterial) {
            loadedMaterial = p.material;

            smoothing = loadedMaterial.smoothing;
            yieldRate = loadedMaterial.yieldRate;
            elastic = loadedMaterial.elasticity > 0;
        }

        var gridVelocity = Jello.Vector2.Zero.copy();
		for (i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				gridVelocity.addSelf(n.velocity.mulFloat(phi));
			}
		}
		p.position.addSelf(gridVelocity);
		p.velocity.addSelf(gridVelocity.sub(p.velocity).mulFloat(smoothing));
		p.gridVelocity.set(gridVelocity);

	    // expand boundaries to include all particles
		this.boundaries.expandToInclude(p.position);
	}, this);

	// expand boundaries a bit further
	this.boundaries.Min.x = Math.floor(this.boundaries.Min.x-1);
	this.boundaries.Min.y = Math.floor(this.boundaries.Min.y-1);
	this.boundaries.Max.x = Math.floor(this.boundaries.Max.x+3);
	this.boundaries.Max.y = Math.floor(this.boundaries.Max.y+3);
};

Jiquid.System.prototype.draw = function(renderer) {
	this.boundaries.debugDraw(renderer);

	// draw all particles in the system
	_.each(this.particles, function(p) {
		p.draw(renderer);
	}, this);
};
<commit_msg>added prototypical interaction techniques for the interaction with fluids<commit_after>mini.Module(
		"jiquid/system"
)
.requires(
		"basic/mouse"
)
.defines(function(Mouse) {
	Jiquid.Mouse = Mouse;
});

Jiquid.System = function() {

	this.wall = new Jello.AABB(
		new Jello.Vector2(-50, 2),
		new Jello.Vector2(50, 50)
	);
	this.boundaries = new Jello.AABB();
	this.materials = [];
	this.gravity = new Jello.Vector2(0,-0.05);// 0.004, 0.02
	this.particles = [];
	this.grid = [];
	this.gsizeY = 0;
	this.interaction = "drag";

	this.datGui = new dat.GUI();
	this.datGui.add(this.gravity, "x").min(-0.2).max(0.2).step(-0.01);
	this.datGui.add(this.gravity, "y").min(-0.2).max(0.2).step(-0.01);
	this.datGui.add(this, "interaction", ["drag", "attract", "repel"]);

	this.materials.push(new Jiquid.Material()
		.addDebugGui(this.datGui.addFolder("Mat1"))
		.setParticleMass(1)
		.setRestDensity(2)
		.setStiffness(1)
		.setBulkViscosity (1)
		.setElasticity(0)
		.setShearViscosity(0.5)
		.setMeltRate(0.0) // unused
		.setSmoothing(0.5)
		.setYieldPoint(0.1) // unused
		.setYieldRate(0.5));

	this.materials.push(new Jiquid.Material()
		.addDebugGui(this.datGui.addFolder("Mat2"))
		.setParticleMass(1)
		.setRestDensity(8)
		.setStiffness(0.0625)
		.setBulkViscosity (0.5)
		.setElasticity(0.5)
		.setShearViscosity(0.1)
		.setMeltRate(0.0) // unused
		.setSmoothing(1.0)
		.setYieldPoint(0.1) // unused
		.setYieldRate(0.5));

    for (var i = -50; i < -10; i++) {
        for (var j = 10; j < 50; j++) {
        	this.particles.push(new Jiquid.Particle(i, j, 0.1, 0, this.materials[0]));
        }
    }

    for (var i = 0; i < 50; i++) {
        for (var j = 10; j < 50; j++) {
        	this.particles.push(new Jiquid.Particle(i, j, -0.1, 0, this.materials[1]));
        }
    }

    // expand boundaries to include all particles
    this.boundaries.clear();
    _.each(this.particles, function(p) {
    	this.boundaries.expandToInclude(p.position);
    }, this);

	// expand boundaries a bit further
	this.boundaries.Min.x = Math.floor(this.boundaries.Min.x-1);
	this.boundaries.Min.y = Math.floor(this.boundaries.Min.y-1);
	this.boundaries.Max.x = Math.floor(this.boundaries.Max.x+3);
	this.boundaries.Max.y = Math.floor(this.boundaries.Max.y+3);

    this.simulator = new Jiquid.Simulator();
};

Jiquid.System.prototype.getNumberOfParticles = function() {
	return this.particles.length;
};

Jiquid.System.prototype.update = function() {
	this.simulator.update(this);

	this.handleInteraction();

	// clear grid
	this.grid.length = 0;
	this.gsizeY = Math.floor(this.boundaries.Max.y-this.boundaries.Min.y);

	var activeCount = 0;

	// calculate particle kernels, and add density and density gradients to the grid
	_.each(this.particles, function(p) {
		p.cx = Math.floor(p.position.x - this.boundaries.Min.x - 0.5); // get cell x
		p.cy = Math.floor(p.position.y - this.boundaries.Min.y - 0.5); // get cell y
		p.gridIndex = p.cx * this.gsizeY + p.cy;

		var x = p.cx - (p.position.x - this.boundaries.Min.x);
		p.px[0] = (0.5 * x * x + 1.5 * x + 1.125);
		p.gx[0] = (x++ + 1.5);
		p.px[1] = (-x * x + 0.75);
		p.gx[1] = (-2.0 * (x++));
		p.px[2] = (0.5 * x * x - 1.5 * x + 1.125);
		p.gx[2] = (x - 1.5);

		var y = p.cy - (p.position.y - this.boundaries.Min.y);
		p.py[0] = (0.5 * y * y + 1.5 * y + 1.125);
		p.gy[0] = (y++ + 1.5);
		p.py[1] = (-y * y + 0.75);
		p.gy[1] = (-2.0 * (y++));
		p.py[2] = (0.5 * y * y - 1.5 * y + 1.125);
		p.gy[2] = (y - 1.5);

		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
            var pgxi = p.gx[i];
			for (var j = 0; j < 3; j++) {
				var gaj = ga+j;
				var n = this.grid[gaj];
				if (n === undefined) {
                    this.grid[gaj] = n = new Jiquid.Node();
                    activeCount++;
				}
				phi = pxi * p.py[j];
				n.m += phi;
				n.gx += pgxi * p.py[j];
				n.gy += pxi * p.gy[j];
			}
		}
	}, this);

	var loadedMaterial;
	var stiffness,
		restDensity,
		elasticity,
		viscosity,
		elasticityS,
		viscosityS,
		bulkViscosityS;

	// Sum particle density from grid, and add pressure and elastic forces to grid
	_.each(this.particles, function(p) {
        if (p.material != loadedMaterial) {
            loadedMaterial = p.material;

            stiffness = loadedMaterial.stiffness;
            restDensity = loadedMaterial.restDensity;
            elasticity = loadedMaterial.elasticity;
            viscosity = loadedMaterial.viscosity;

            elasticityS = loadedMaterial.elasticity;
            viscosityS = loadedMaterial.viscosity;
            bulkViscosityS = loadedMaterial.bulkViscosity;
        }

        var density = 0;
		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				var phi = pxi * p.py[j];
				density += phi*n.m;
			}
		}
		var pressure = (density-restDensity)/restDensity;
		if (pressure > 4.0)
			pressure = 4.0;

		var fx = 0, fy = 0;
		if (p.position.x < this.wall.Min.x) {
			fx += this.wall.Min.x - p.position.x;
            p.velocity.x *= 0.1;
		}
		if (p.position.x > this.wall.Max.x) {
			fx += this.wall.Max.x - p.position.x;
            p.velocity.x *= 0.1;
		}
		if (p.position.y < this.wall.Min.y) {
			fy += this.wall.Min.y - p.position.y;
            p.velocity.y *= 0.1;
		}
		if (p.position.y > this.wall.Max.y) {
			fy += this.wall.Max.y - p.position.y;
            p.velocity.y *= 0.1;
		}

		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
            var gxi = p.gx[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				n.acceleration.x += -((gxi * p.py[j]) * pressure) + fx * phi;
				n.acceleration.y += -((pxi * p.gy[j]) * pressure) + fy * phi;
			}
		}
	}, this);

    // divide grid acceleration by mass
	_.each(this.grid, function(n) {
		if (n.m > 0.0) {
			n.acceleration.divFloatSelf(n.m);
			n.acceleration.addSelf(this.gravity);
		}
	}, this);

	// accelerate particles and interpolate velocity back to grid
	_.each(this.particles, function(p) {
		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				p.velocity.addSelf(n.acceleration.mulFloat(phi));
			}
		}
		for (var i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (var j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				n.velocity.addSelf(p.velocity.mulFloat(phi));
			}
		}
	}, this);

	// divide grid velocity by mass
	_.each(this.grid, function(n) {
		if (n.m > 0.0) {
			n.velocity.divFloatSelf(n.m);
		}
	}, this);

	this.boundaries.clear();

	loadedMaterial = undefined;
	var smoothing,
		yieldRate,
		elastic;

	// advance particles
	_.each(this.particles, function(p) {
        if (p.material != loadedMaterial) {
            loadedMaterial = p.material;

            smoothing = loadedMaterial.smoothing;
            yieldRate = loadedMaterial.yieldRate;
            elastic = loadedMaterial.elasticity > 0;
        }

        var gridVelocity = Jello.Vector2.Zero.copy();
		for (i = 0; i < 3; i++) {
			var ga = p.gridIndex+i*this.gsizeY;
            var pxi = p.px[i];
			for (j = 0; j < 3; j++) {
				var n = this.grid[ga+j];
				phi = pxi * p.py[j];
				gridVelocity.addSelf(n.velocity.mulFloat(phi));
			}
		}
		p.position.addSelf(gridVelocity);
		p.velocity.addSelf(gridVelocity.sub(p.velocity).mulFloat(smoothing));
		p.gridVelocity.set(gridVelocity);

	    // expand boundaries to include all particles
		this.boundaries.expandToInclude(p.position);
	}, this);

	// expand boundaries a bit further
	this.boundaries.Min.x = Math.floor(this.boundaries.Min.x-1);
	this.boundaries.Min.y = Math.floor(this.boundaries.Min.y-1);
	this.boundaries.Max.x = Math.floor(this.boundaries.Max.x+3);
	this.boundaries.Max.y = Math.floor(this.boundaries.Max.y+3);
};

// simple interaction techniques
Jiquid.System.prototype.handleInteraction = function() {
	this.interactionLastPoint = this.interactionCurrentPoint || env.camera.screenToWorldCoordinates(env.input.mouse);
	this.interactionCurrentPoint = env.camera.screenToWorldCoordinates(env.input.mouse);

	if(env.input.state(Jiquid.Mouse.LeftButton) || env.input.state(Jiquid.Mouse.RightButton)) {
		// drag
		if(env.input.state(Jiquid.Mouse.LeftButton)) {
			_.each(this.particles, function(p) {
				if(p.position.sub(this.interactionCurrentPoint).lengthSquared() < 50)
					p.velocity.addSelf(this.interactionCurrentPoint.sub(this.interactionLastPoint)
							//.mulFloat(0.1)
							.mulFloat(0.1 * Math.log((1+p.position.sub(this.interactionCurrentPoint).lengthSquared())))
							);
			}, this);
		} else
			// attract
			if(env.input.state(Jiquid.Mouse.RightButton)) {
			_.each(this.particles, function(p) {
				var vectorToMouse = this.interactionCurrentPoint.sub(p.position);
				var distanceToMouse = vectorToMouse.lengthSquared();
				if(distanceToMouse < 50)
					p.velocity.addSelf(vectorToMouse
							.mulFloat(1/distanceToMouse)
							.mulFloat((Math.log(1+distanceToMouse)))
							);
			}, this);
			// repel
//			if(env.input.state(Jiquid.Mouse.RightButton)) {
//			_.each(this.particles, function(p) {
//				var vectorToMouse = this.interactionCurrentPoint.sub(p.position);
//				var distanceToMouse = vectorToMouse.lengthSquared();
//				if(distanceToMouse < 50)
//					p.velocity.addSelf(vectorToMouse
//							//.mulFloat(0.1)
//							.mulFloat(0.1 * Math.log(1/(1+distanceToMouse)))
//							);
//			}, this);
		}
	} else {
		this.interactionLastPoint = undefined;
		this.interactionCurrentPoint = undefined;
	};
};

Jiquid.System.prototype.draw = function(renderer) {
	this.boundaries.debugDraw(renderer);

	env.renderer.setOptions({color: "pink", opacity: 0.5});
	env.renderer.drawDot(env.camera.screenToWorldCoordinates(env.input.mouse), 10);

	// draw all particles in the system
	_.each(this.particles, function(p) {
		p.draw(renderer);
	}, this);
};
<|endoftext|>