<commit_before>/* *****************************************************************************
 * Caleydo - Visualization for Molecular Biology - http://caleydo.org
 * Copyright (c) The Caleydo Team. All rights reserved.
 * Licensed under the new BSD license, available at http://caleydo.org/license
 **************************************************************************** */

const {libraryAliases, libraryExternals, modules, entries, ignores, type, registry, vendor} = require('./.yo-rc.json')['generator-phovea'];
const resolve = require('path').resolve;
const pkg = require('./package.json');
const webpack = require('webpack');
const fs = require('fs');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const buildInfo = require('./buildInfo.js');

const now = new Date();
const prefix = (n) => n < 10 ? ('0' + n) : n.toString();
const buildId = `${now.getUTCFullYear()}${prefix(now.getUTCMonth() + 1)}${prefix(now.getUTCDate())}-${prefix(now.getUTCHours())}${prefix(now.getUTCMinutes())}${prefix(now.getUTCSeconds())}`;
pkg.version = pkg.version.replace('SNAPSHOT', buildId);

const year = (new Date()).getFullYear();
const banner = '/*! ' + (pkg.title || pkg.name) + ' - v' + pkg.version + ' - ' + year + '\n' +
  (pkg.homepage ? '* ' + pkg.homepage + '\n' : '') +
  '* Copyright (c) ' + year + ' ' + pkg.author.name + ';' +
  ' Licensed ' + pkg.license + '*/\n';

const preCompilerFlags = {flags: (registry || {}).flags || {}};
const includeFeature = registry ? (extension, id) => {
  const exclude = registry.exclude || [];
  const include = registry.include || [];
  if (!exclude && !include) {
    return true;
  }
  const test = (f) => Array.isArray(f) ? extension.match(f[0]) && (id || '').match(f[1]) : extension.match(f);
  return include.every(test) && !exclude.some(test);
} : () => true;

const tsLoader = [
  {
    loader: 'awesome-typescript-loader'
  }
];

const tsLoaderDev = [
  {loader: 'cache-loader'},
  {
    loader: 'thread-loader',
    options: {
      // there should be 1 cpu for the fork-ts-checker-webpack-plugin
      workers: require('os').cpus().length - 1
    }
  },
  {
    loader: 'ts-loader',
    options: {
      happyPackMode: true, // IMPORTANT! use happyPackMode mode to speed-up compilation and reduce errors reported to webpack,
      compilerOptions: {
        target: 'es6',
        "jsx": "react",
        "jsxFactory": "h",
      }
    }
  }
];

// list of loaders and their mappings
const webpackloaders = [
  {test: /\.scss$/, use: 'style-loader!css-loader!sass-loader'},
  {test: /\.css$/, use: 'style-loader!css-loader'},
  {test: /\.tsx?$/, use: tsLoader},
  {
    test: /phovea(_registry)?\.js$/, use: [{
      loader: 'ifdef-loader',
      options: Object.assign({include: includeFeature}, preCompilerFlags)
    }]
  },
  {test: /\.json$/, use: 'json-loader'},
  {
    test: /\.(png|jpg|gif|webp)$/,
    loader: 'url-loader',
    options: {
      limit: 10000 // inline <= 10kb
    }
  },
  {
    test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
    loader: 'url-loader',
    options: {
      limit: 10000, // inline <= 10kb
      mimetype: 'application/font-woff'
    }
  },
  {
    test: /\.svg(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
    loader: 'url-loader',
    options: {
      limit: 10000, // inline <= 10kb
      mimetype: 'image/svg+xml'
    }
  },
  {test: /\.(ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file-loader'}
];

/**
 * tests whether the given phovea module name is matching the requested file and if so convert it to an external lookup
 * depending on the loading type
 */
function testPhoveaModule(moduleName, request) {
  if (!(new RegExp('^' + moduleName + '/src.*')).test(request)) {
    return false;
  }
  const subModule = request.match(/.*\/src\/?(.*)/)[1];
  // skip empty modules = root
  const path = subModule === '' ? [moduleName] : [moduleName, subModule];
  // phovea_<name> ... phovea.name
  const rootPath = /phovea_.*/.test(moduleName) ? ['phovea', moduleName.slice(7)].concat(path.slice(1)) : path;
  return {
    root: rootPath,
    commonjs2: path,
    commonjs: path,
    amd: request + (subModule === '' ? '/main' : '')
  };
}

function testPhoveaModules(modules) {
  return (context, request, callback) => {
    for (let i = 0; i < modules.length; ++i) {
      const r = testPhoveaModule(modules[i], request);
      if (r) {
        return callback(null, r);
      }
    }
    callback();
  };
}

// use workspace registry file if available
const isWorkspaceContext = fs.existsSync(resolve(__dirname, '..', 'phovea_registry.js'));
const registryFile = isWorkspaceContext ? '../phovea_registry.js' : './phovea_registry.js';
const actMetaData = `file-loader?name=phoveaMetaData.json!${buildInfo.metaDataTmpFile(pkg)}`;
const actBuildInfoFile = `file-loader?name=buildInfo.json!${buildInfo.tmpFile()}`;

/**
 * inject the registry to be included
 * @param entry
 * @returns {*}
 */
function injectRegistry(entry) {
  const extraFiles = [registryFile, actBuildInfoFile, actMetaData];
  // build also the registry
  if (typeof entry === 'string') {
    return extraFiles.concat(entry);
  }
  const transformed = {};
  Object.keys(entry).forEach((eentry) => {
    transformed[eentry] = extraFiles.concat(entry[eentry]);
  });
  return transformed;
}

/**
 * generate a webpack configuration
 */
function generateWebpack(options) {
  let base = {
    entry: injectRegistry(options.entries),
    output: {
      path: resolve(__dirname, 'build'),
      filename: (options.name || (pkg.name + (options.bundle ? '_bundle' : ''))) + (options.min && !options.nosuffix ? '.min' : '') + '.js',
      chunkFilename: '[chunkhash].js',
      publicPath: '' // no public path = relative
    },
    resolve: {
      // add `.ts` and `.tsx` as a resolvable extension.
      extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
      alias: Object.assign({}, options.libs || {}),
      symlinks: false,
      // fallback to the directory above if they are siblings just in the workspace context
      modules: isWorkspaceContext ? [
        resolve(__dirname, '../'),
        'node_modules'
      ] : ['node_modules']
    },
    plugins: [
      // define magic constants that are replaced
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(options.isProduction ? 'production' : 'development'),
        __VERSION__: JSON.stringify(pkg.version),
        __LICENSE__: JSON.stringify(pkg.license),
        __BUILD_ID__: buildId,
        __DEBUG__: options.isDev || options.isTest,
        __TEST__: options.isTest,
        __PRODUCTION__: options.isProduction,
        __APP_CONTEXT__: JSON.stringify('/')
      })
      // rest depends on type
    ],
    externals: [],
    module: {
      loaders: webpackloaders.slice()
    },
    devServer: {
      proxy: {
        '/api/*': {
          target: 'http://localhost:9000',
          secure: false,
          ws: true
        },
        '/login': {
          target: 'http://localhost:9000',
          secure: false
        },
        '/logout': {
          target: 'http://localhost:9000',
          secure: false
        },
        '/loggedinas': {
          target: 'http://localhost:9000',
          secure: false
        }
      },
      contentBase: resolve(__dirname, 'build'),
      watchOptions: {
        aggregateTimeout: 500,
        ignored: /node_modules/
      }
    },
    watchOptions: {
      aggregateTimeout: 500,
      ignored: /node_modules/
    }
  };

  if (options.isProduction) {
    base.plugins.unshift(new webpack.BannerPlugin({
      banner: banner,
      raw: true
    }));
    base.plugins.push(new webpack.optimize.MinChunkSizePlugin({
      minChunkSize: 10000 // at least 10.000 characters
    }));
    // base.plugins.push(new webpack.optimize.ModuleConcatenationPlugin());
  } else if (options.isDev) {
    // switch to def settings
    base.module.loaders.find((d) => d.use === tsLoader).use = tsLoaderDev;
    base.plugins.push(new ForkTsCheckerWebpackPlugin({checkSyntacticErrors: true, tsconfig: './tsconfig_dev.json'}));
  }

  if (options.library) {
    let libName = /phovea_.*/.test(pkg.name) ? ['phovea', pkg.name.slice(7)] : pkg.name;
    // generate a library, i.e. output the last entry element
    // create library name
    if (options.moduleBundle) {
      libName = 'phovea';
    }
    base.output.library = libName;
    base.output.libraryTarget = 'umd';
    base.output.umdNamedDefine = false; // anonymous require module
  }

  if (!options.bundle) {
    // if we don't bundle don't include external libraries and other phovea modules
    base.externals.push(...(options.externals || Object.keys(options.libs || {})));

    // ignore all phovea modules
    if (options.modules) {
      base.externals.push(testPhoveaModules(options.modules));
    }

    // ignore extra modules
    (options.ignore || []).forEach(function (d) {
      base.module.loaders.push({test: new RegExp(d), loader: 'null-loader'}); // use null loader
    });
    // ingore phovea module registry calls
    (options.modules || []).forEach(function (m) {
      base.module.loaders.push({
        test: new RegExp('.*[\\\\/]' + m + '[\\\\/]phovea_registry.js'),
        loader: 'null-loader'
      }); // use null loader
    });
  }
  if (!options.bundle || options.isApp) {
    // extract the included css file to own file
    const p = new ExtractTextPlugin({
      filename: (options.isApp || options.moduleBundle ? 'style' : pkg.name) + (options.min && !options.nosuffix ? '.min' : '') + '.css',
      allChunks: true // there seems to be a bug in dynamically loaded chunk styles are not loaded, workaround: extract all styles from all chunks
    });
    base.plugins.push(p);
    base.module.loaders[0] = {
      test: /\.scss$/,
      loader: p.extract(['css-loader', 'sass-loader'])
    };
    base.module.loaders[1] = {
      test: /\.css$/,
      loader: p.extract(['css-loader'])
    };
  }
  if (options.isApp) {
    // create manifest
    // base.plugins.push(new webpack.optimize.AppCachePlugin());
  }
  if (options.commons) {
    // build a commons plugin
    base.plugins.push(new webpack.optimize.CommonsChunkPlugin({
      // The order of this array matters
      name: 'common',
      filename: 'common.js',
      minChunks: 2
    }));
  }
  if (options.vendor) {
    (Array.isArray(options.vendor) ? options.vendor : [options.vendor]).forEach((reg) => {
      base.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        async: true,
        children: true,
        deepChildren: true,
        minChunks: (module, count) => new RegExp(reg, 'i').test(module.resource) && count >= 2
      }));
    });
  }
  if (options.min) {
    // use a minifier
    base.plugins.push(
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      }),
      new webpack.optimize.UglifyJsPlugin());
  } else {
    // generate source maps
    base.devtool = 'inline-source-map';
  }
  return base;
}

function generateWebpackConfig(env) {
  const isTest = env === 'test';
  const isProduction = env === 'prod';
  const isDev = !isProduction && !isTest;

  const base = {
    entries: entries,
    libs: libraryAliases,
    externals: libraryExternals,
    modules: modules,
    vendor: vendor,
    ignore: ignores,
    isProduction: isProduction,
    isDev: isDev,
    isTest: isTest
  };

  if (isTest) {
    return generateWebpack(Object.assign({}, base, {
      bundle: true
    }));
  }

  if (type.startsWith('app')) {
    base.isApp = true;
    base.bundle = true; // bundle everything together
    base.name = '[name]'; // multiple entries case
    base.commons = true; // extract commons module
  } else if (type === 'bundle') {
    base.library = true; // expose as library
    base.moduleBundle = true; // expose as library 'phovea'
    base.name = pkg.name; // to avoid adding _bundle
    base.bundle = true;
  } else { // type === 'lib'
    base.library = true;
  }

  // single generation
  if (isDev) {
    return generateWebpack(base);
  }
  if (type.startsWith('app')) { // isProduction app
    return generateWebpack(Object.assign({}, base, {
      min: true,
      nosuffix: true
    }));
  }
  // isProduction
  return [
    // plain
    generateWebpack(base),
    // minified
    generateWebpack(Object.assign({}, base, {
      min: true
    }))
  ];
}

module.exports = generateWebpackConfig;
module.exports.generateWebpack = generateWebpack;
<commit_msg>common notation for webpack compiler options<commit_after>/* *****************************************************************************
 * Caleydo - Visualization for Molecular Biology - http://caleydo.org
 * Copyright (c) The Caleydo Team. All rights reserved.
 * Licensed under the new BSD license, available at http://caleydo.org/license
 **************************************************************************** */

const {libraryAliases, libraryExternals, modules, entries, ignores, type, registry, vendor} = require('./.yo-rc.json')['generator-phovea'];
const resolve = require('path').resolve;
const pkg = require('./package.json');
const webpack = require('webpack');
const fs = require('fs');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const buildInfo = require('./buildInfo.js');

const now = new Date();
const prefix = (n) => n < 10 ? ('0' + n) : n.toString();
const buildId = `${now.getUTCFullYear()}${prefix(now.getUTCMonth() + 1)}${prefix(now.getUTCDate())}-${prefix(now.getUTCHours())}${prefix(now.getUTCMinutes())}${prefix(now.getUTCSeconds())}`;
pkg.version = pkg.version.replace('SNAPSHOT', buildId);

const year = (new Date()).getFullYear();
const banner = '/*! ' + (pkg.title || pkg.name) + ' - v' + pkg.version + ' - ' + year + '\n' +
  (pkg.homepage ? '* ' + pkg.homepage + '\n' : '') +
  '* Copyright (c) ' + year + ' ' + pkg.author.name + ';' +
  ' Licensed ' + pkg.license + '*/\n';

const preCompilerFlags = {flags: (registry || {}).flags || {}};
const includeFeature = registry ? (extension, id) => {
  const exclude = registry.exclude || [];
  const include = registry.include || [];
  if (!exclude && !include) {
    return true;
  }
  const test = (f) => Array.isArray(f) ? extension.match(f[0]) && (id || '').match(f[1]) : extension.match(f);
  return include.every(test) && !exclude.some(test);
} : () => true;

const tsLoader = [
  {
    loader: 'awesome-typescript-loader'
  }
];

const tsLoaderDev = [
  {loader: 'cache-loader'},
  {
    loader: 'thread-loader',
    options: {
      // there should be 1 cpu for the fork-ts-checker-webpack-plugin
      workers: require('os').cpus().length - 1
    }
  },
  {
    loader: 'ts-loader',
    options: {
      happyPackMode: true, // IMPORTANT! use happyPackMode mode to speed-up compilation and reduce errors reported to webpack,
      compilerOptions: {
        target: 'es6',
        jsx: 'react',
        jsxFactory: 'h',
      }
    }
  }
];

// list of loaders and their mappings
const webpackloaders = [
  {test: /\.scss$/, use: 'style-loader!css-loader!sass-loader'},
  {test: /\.css$/, use: 'style-loader!css-loader'},
  {test: /\.tsx?$/, use: tsLoader},
  {
    test: /phovea(_registry)?\.js$/, use: [{
      loader: 'ifdef-loader',
      options: Object.assign({include: includeFeature}, preCompilerFlags)
    }]
  },
  {test: /\.json$/, use: 'json-loader'},
  {
    test: /\.(png|jpg|gif|webp)$/,
    loader: 'url-loader',
    options: {
      limit: 10000 // inline <= 10kb
    }
  },
  {
    test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
    loader: 'url-loader',
    options: {
      limit: 10000, // inline <= 10kb
      mimetype: 'application/font-woff'
    }
  },
  {
    test: /\.svg(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
    loader: 'url-loader',
    options: {
      limit: 10000, // inline <= 10kb
      mimetype: 'image/svg+xml'
    }
  },
  {test: /\.(ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file-loader'}
];

/**
 * tests whether the given phovea module name is matching the requested file and if so convert it to an external lookup
 * depending on the loading type
 */
function testPhoveaModule(moduleName, request) {
  if (!(new RegExp('^' + moduleName + '/src.*')).test(request)) {
    return false;
  }
  const subModule = request.match(/.*\/src\/?(.*)/)[1];
  // skip empty modules = root
  const path = subModule === '' ? [moduleName] : [moduleName, subModule];
  // phovea_<name> ... phovea.name
  const rootPath = /phovea_.*/.test(moduleName) ? ['phovea', moduleName.slice(7)].concat(path.slice(1)) : path;
  return {
    root: rootPath,
    commonjs2: path,
    commonjs: path,
    amd: request + (subModule === '' ? '/main' : '')
  };
}

function testPhoveaModules(modules) {
  return (context, request, callback) => {
    for (let i = 0; i < modules.length; ++i) {
      const r = testPhoveaModule(modules[i], request);
      if (r) {
        return callback(null, r);
      }
    }
    callback();
  };
}

// use workspace registry file if available
const isWorkspaceContext = fs.existsSync(resolve(__dirname, '..', 'phovea_registry.js'));
const registryFile = isWorkspaceContext ? '../phovea_registry.js' : './phovea_registry.js';
const actMetaData = `file-loader?name=phoveaMetaData.json!${buildInfo.metaDataTmpFile(pkg)}`;
const actBuildInfoFile = `file-loader?name=buildInfo.json!${buildInfo.tmpFile()}`;

/**
 * inject the registry to be included
 * @param entry
 * @returns {*}
 */
function injectRegistry(entry) {
  const extraFiles = [registryFile, actBuildInfoFile, actMetaData];
  // build also the registry
  if (typeof entry === 'string') {
    return extraFiles.concat(entry);
  }
  const transformed = {};
  Object.keys(entry).forEach((eentry) => {
    transformed[eentry] = extraFiles.concat(entry[eentry]);
  });
  return transformed;
}

/**
 * generate a webpack configuration
 */
function generateWebpack(options) {
  let base = {
    entry: injectRegistry(options.entries),
    output: {
      path: resolve(__dirname, 'build'),
      filename: (options.name || (pkg.name + (options.bundle ? '_bundle' : ''))) + (options.min && !options.nosuffix ? '.min' : '') + '.js',
      chunkFilename: '[chunkhash].js',
      publicPath: '' // no public path = relative
    },
    resolve: {
      // add `.ts` and `.tsx` as a resolvable extension.
      extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
      alias: Object.assign({}, options.libs || {}),
      symlinks: false,
      // fallback to the directory above if they are siblings just in the workspace context
      modules: isWorkspaceContext ? [
        resolve(__dirname, '../'),
        'node_modules'
      ] : ['node_modules']
    },
    plugins: [
      // define magic constants that are replaced
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(options.isProduction ? 'production' : 'development'),
        __VERSION__: JSON.stringify(pkg.version),
        __LICENSE__: JSON.stringify(pkg.license),
        __BUILD_ID__: buildId,
        __DEBUG__: options.isDev || options.isTest,
        __TEST__: options.isTest,
        __PRODUCTION__: options.isProduction,
        __APP_CONTEXT__: JSON.stringify('/')
      })
      // rest depends on type
    ],
    externals: [],
    module: {
      loaders: webpackloaders.slice()
    },
    devServer: {
      proxy: {
        '/api/*': {
          target: 'http://localhost:9000',
          secure: false,
          ws: true
        },
        '/login': {
          target: 'http://localhost:9000',
          secure: false
        },
        '/logout': {
          target: 'http://localhost:9000',
          secure: false
        },
        '/loggedinas': {
          target: 'http://localhost:9000',
          secure: false
        }
      },
      contentBase: resolve(__dirname, 'build'),
      watchOptions: {
        aggregateTimeout: 500,
        ignored: /node_modules/
      }
    },
    watchOptions: {
      aggregateTimeout: 500,
      ignored: /node_modules/
    }
  };

  if (options.isProduction) {
    base.plugins.unshift(new webpack.BannerPlugin({
      banner: banner,
      raw: true
    }));
    base.plugins.push(new webpack.optimize.MinChunkSizePlugin({
      minChunkSize: 10000 // at least 10.000 characters
    }));
    // base.plugins.push(new webpack.optimize.ModuleConcatenationPlugin());
  } else if (options.isDev) {
    // switch to def settings
    base.module.loaders.find((d) => d.use === tsLoader).use = tsLoaderDev;
    base.plugins.push(new ForkTsCheckerWebpackPlugin({checkSyntacticErrors: true, tsconfig: './tsconfig_dev.json'}));
  }

  if (options.library) {
    let libName = /phovea_.*/.test(pkg.name) ? ['phovea', pkg.name.slice(7)] : pkg.name;
    // generate a library, i.e. output the last entry element
    // create library name
    if (options.moduleBundle) {
      libName = 'phovea';
    }
    base.output.library = libName;
    base.output.libraryTarget = 'umd';
    base.output.umdNamedDefine = false; // anonymous require module
  }

  if (!options.bundle) {
    // if we don't bundle don't include external libraries and other phovea modules
    base.externals.push(...(options.externals || Object.keys(options.libs || {})));

    // ignore all phovea modules
    if (options.modules) {
      base.externals.push(testPhoveaModules(options.modules));
    }

    // ignore extra modules
    (options.ignore || []).forEach(function (d) {
      base.module.loaders.push({test: new RegExp(d), loader: 'null-loader'}); // use null loader
    });
    // ingore phovea module registry calls
    (options.modules || []).forEach(function (m) {
      base.module.loaders.push({
        test: new RegExp('.*[\\\\/]' + m + '[\\\\/]phovea_registry.js'),
        loader: 'null-loader'
      }); // use null loader
    });
  }
  if (!options.bundle || options.isApp) {
    // extract the included css file to own file
    const p = new ExtractTextPlugin({
      filename: (options.isApp || options.moduleBundle ? 'style' : pkg.name) + (options.min && !options.nosuffix ? '.min' : '') + '.css',
      allChunks: true // there seems to be a bug in dynamically loaded chunk styles are not loaded, workaround: extract all styles from all chunks
    });
    base.plugins.push(p);
    base.module.loaders[0] = {
      test: /\.scss$/,
      loader: p.extract(['css-loader', 'sass-loader'])
    };
    base.module.loaders[1] = {
      test: /\.css$/,
      loader: p.extract(['css-loader'])
    };
  }
  if (options.isApp) {
    // create manifest
    // base.plugins.push(new webpack.optimize.AppCachePlugin());
  }
  if (options.commons) {
    // build a commons plugin
    base.plugins.push(new webpack.optimize.CommonsChunkPlugin({
      // The order of this array matters
      name: 'common',
      filename: 'common.js',
      minChunks: 2
    }));
  }
  if (options.vendor) {
    (Array.isArray(options.vendor) ? options.vendor : [options.vendor]).forEach((reg) => {
      base.plugins.push(new webpack.optimize.CommonsChunkPlugin({
        async: true,
        children: true,
        deepChildren: true,
        minChunks: (module, count) => new RegExp(reg, 'i').test(module.resource) && count >= 2
      }));
    });
  }
  if (options.min) {
    // use a minifier
    base.plugins.push(
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      }),
      new webpack.optimize.UglifyJsPlugin());
  } else {
    // generate source maps
    base.devtool = 'inline-source-map';
  }
  return base;
}

function generateWebpackConfig(env) {
  const isTest = env === 'test';
  const isProduction = env === 'prod';
  const isDev = !isProduction && !isTest;

  const base = {
    entries: entries,
    libs: libraryAliases,
    externals: libraryExternals,
    modules: modules,
    vendor: vendor,
    ignore: ignores,
    isProduction: isProduction,
    isDev: isDev,
    isTest: isTest
  };

  if (isTest) {
    return generateWebpack(Object.assign({}, base, {
      bundle: true
    }));
  }

  if (type.startsWith('app')) {
    base.isApp = true;
    base.bundle = true; // bundle everything together
    base.name = '[name]'; // multiple entries case
    base.commons = true; // extract commons module
  } else if (type === 'bundle') {
    base.library = true; // expose as library
    base.moduleBundle = true; // expose as library 'phovea'
    base.name = pkg.name; // to avoid adding _bundle
    base.bundle = true;
  } else { // type === 'lib'
    base.library = true;
  }

  // single generation
  if (isDev) {
    return generateWebpack(base);
  }
  if (type.startsWith('app')) { // isProduction app
    return generateWebpack(Object.assign({}, base, {
      min: true,
      nosuffix: true
    }));
  }
  // isProduction
  return [
    // plain
    generateWebpack(base),
    // minified
    generateWebpack(Object.assign({}, base, {
      min: true
    }))
  ];
}

module.exports = generateWebpackConfig;
module.exports.generateWebpack = generateWebpack;
<|endoftext|>


<commit_before>var View = require("../view");
var template = require("../tmpl/share-link-view");
var ConversationModel = require("../models/conversation");

module.exports = View.extend({
    name: "shareLinkView",
    template: template,
    model: ConversationModel,
    events: {
      "click #okButton": function() {
        this.trigger("done");
      },
      "mouseup input": function(event) {
        // :( http://stackoverflow.com/questions/3272089/programmatically-selecting-text-in-an-input-field-on-ios-devices-mobile-safari
        setTimeout(function() {
          if (event.target) {
            if (event.target.setSelectionRange) {
              event.target.setSelectionRange(0,9999999);
            } else {
              $(event.target).select();
            }
          }
        }, 1);
      }
    },
    sendShareLinkToEmail: function(){
      //https://www.dropbox.com/s/f8ed1vtpim28aej/Screenshot%202014-03-02%2023.29.31.png
      //owner was undefined on the client side, so going to server to make requests for that information and fire mailgun
      //if oid is on the client side, can send that instead of zid
      $.ajax({
        type: "POST",
        url: "/v3/sendCreatedLinkToEmail",
        data: this.model.attributes.zid
      }).done(function(){
        console.log('sent!')
      })
    }
  });<commit_msg>mail sending on client when share link button clicked<commit_after>var View = require("../view");
var template = require("../tmpl/share-link-view");
var ConversationModel = require("../models/conversation");

module.exports = View.extend({
    name: "shareLinkView",
    template: template,
    model: ConversationModel,
    events: {
      "click #okButton": function() {
        this.trigger("done");
      },
      "mouseup input": function(event) {
        // :( http://stackoverflow.com/questions/3272089/programmatically-selecting-text-in-an-input-field-on-ios-devices-mobile-safari
        setTimeout(function() {
          if (event.target) {
            if (event.target.setSelectionRange) {
              event.target.setSelectionRange(0,9999999);
            } else {
              $(event.target).select();
            }
          }
        }, 1);
      }
    },
    sendShareLinkToEmail: function(){
      //https://www.dropbox.com/s/f8ed1vtpim28aej/Screenshot%202014-03-02%2023.29.31.png
      //owner was undefined on the client side, so going to server to make requests for that information and fire mailgun
      //if oid is on the client side, can send that instead of zid
      var zid=this.model.attributes.zid;
      $.ajax({
        type: "POST",
        dataType: "json",
        xhrFields: {
          withCredentials: true
        },
        crossDomain: true,
        url: "/v3/sendCreatedLinkToEmail",
        data: {
          zid: zid
        }
      }).done(function(){
        console.log('sent!')
      })
    }
  });<|endoftext|>


<commit_before>"use strict";

function verif_form_sortie() {
  if (parseInt(document.getElementById('najout').value) >= 1) {
    document.getElementById("formulaire").submit();
  }
}

function submanut(x) {
  var number = document.getElementById("number");

  if ((number.value - x) > 0 ) {
    number.value = (number.value - x).toFixed(2);
  }
}

function number_write(x) {
  var text_box = document.getElementById("number");
  text_box.value = text_box.value + x;
}

function number_clear() {
  const input = document.getElementById("number");
  input.value = "";
  input.setCustomValidity("");
}

/*
 * nom: HTML
 * id: HTML <input>
 * masse_max: float
 * masse_bac: float
 */

function masse_write(nom, id, masse_max, masse_bac) {
  const input = document.getElementById("number");
  const number_value = parseFloat(input.value);
  const masse_reele = number_value - masse_bac;

  if (masse_reele > 0.00) {
    if (number_value <= masse_max) {
      const masseTotale = document.getElementById("massetot");
      masseTotale.textContent = (parseFloat(masseTotale.textContent) + masse_reele).toFixed(2);

      id.value = (parseFloat(id.value) + masse_reele).toFixed(2);
      nom.textContent = (parseFloat(nom.textContent) + masse_reele).toFixed(2);
      input.value = "";

      const nAjout = document.getElementById("najout");
      nAjout.value = parseInt(nAjout.value) + 1;

      number_clear();
    } else {
      input.setCustomValidity("Masse supérieure aux limites de pesée de la balance.");
    }
  } else {
      input.setCustomValidity("Masse entrée inférieure au poids du conteneur ou inférieure ou égale à 0.");
  }
}

function aff_dechets_recycle() {
  var index, len, bname;
  const ref2 = document.getElementById("sel_filiere");
  const tabref2 = ref2.value.split('|');
  ref2.disabled = true;
    const id_filiere = document.getElementById("id_filiere");
  id_filiere.value = tabref2[0];

  const type_dechet = document.getElementById("type_dechet");
  type_dechet.value = tabref2[1];

  for (index = 0, len = tabtyps.length; index < len; ++index) {

    console.log(tabref2[1].split('a'));

    bname = tabref2[1].split('a')[index];
    if(parseInt(bname > 0)){
document.getElementById(bname).style.display = "block";
    }
}

}

function masse_write_recycle(nom, id, masse_max, masse_bac) {
    const ref = document.getElementById("sel_filiere");
  const tabref = ref.value.split('|');

  const id_filiere = document.getElementById("id_filiere");
  id_filiere.value = tabref[0];

  const type_dechet = document.getElementById("type_dechet");
  type_dechet.value = tabref[1];

  ref.disabled = true;
  const input = document.getElementById("number");
  const number_value = parseFloat(input.value);
  const masse_reele = number_value - masse_bac;

  if (masse_reele > 0.00) {
    if (number_value <= masse_max) {
      const masseTotale = document.getElementById("massetot");
      masseTotale.textContent = (parseFloat(masseTotale.textContent) + masse_reele).toFixed(2);

      id.value = (parseFloat(id.value) + masse_reele).toFixed(2);
      nom.textContent = (parseFloat(nom.textContent) + masse_reele).toFixed(2);
      input.value = "";

      const nAjout = document.getElementById("najout");
      nAjout.value = parseInt(nAjout.value) + 1;

      number_clear();
    } else {
      input.setCustomValidity("Masse supérieure aux limites de pesée de la balance.");
    }
  } else {
      input.setCustomValidity("Masse entrée inférieure au poids du conteneur ou inférieure ou égale à 0.");
  }
}

function recocom() {
  document.getElementById("commentaire").value =
    document.getElementById("commentaireini").value;
}

function tdechet_add(pesee_max) {
  const ref = document.getElementById("sel_filiere");
  const tabref = ref.value.split('|');

  const id_filiere = document.getElementById("id_filiere");
  id_filiere.value = tabref[0];

  const id_type_dechet = document.getElementById("id_type_dechet");
  id_type_dechet.value = tabref[1];

  const type_dechet = document.getElementById("type_dechet");
  type_dechet.value = tabref[2];

  ref.disabled = true;
  masse_write(document.getElementById(tabref[1]), document.getElementById("m"+tabref[1]), pesee_max, 0.0);
}
<commit_msg>transformation de l'interface de collecte de maniere à encaisser plusieurstypes_dechets_evac sur le meme bon de sortie recycleur et pour un meme recycleur...<commit_after>"use strict";

function verif_form_sortie() {
  if (parseInt(document.getElementById('najout').value) >= 1) {
    document.getElementById("formulaire").submit();
  }
}

function submanut(x) {
  var number = document.getElementById("number");

  if ((number.value - x) > 0 ) {
    number.value = (number.value - x).toFixed(2);
  }
}

function number_write(x) {
  var text_box = document.getElementById("number");
  text_box.value = text_box.value + x;
}

function number_clear() {
  const input = document.getElementById("number");
  input.value = "";
  input.setCustomValidity("");
}

/*
 * nom: HTML
 * id: HTML <input>
 * masse_max: float
 * masse_bac: float
 */

function masse_write(nom, id, masse_max, masse_bac) {
  const input = document.getElementById("number");
  const number_value = parseFloat(input.value);
  const masse_reele = number_value - masse_bac;

  if (masse_reele > 0.00) {
    if (number_value <= masse_max) {
      const masseTotale = document.getElementById("massetot");
      masseTotale.textContent = (parseFloat(masseTotale.textContent) + masse_reele).toFixed(2);

      id.value = (parseFloat(id.value) + masse_reele).toFixed(2);
      nom.textContent = (parseFloat(nom.textContent) + masse_reele).toFixed(2);
      input.value = "";

      const nAjout = document.getElementById("najout");
      nAjout.value = parseInt(nAjout.value) + 1;

      number_clear();
    } else {
      input.setCustomValidity("Masse supérieure aux limites de pesée de la balance.");
    }
  } else {
      input.setCustomValidity("Masse entrée inférieure au poids du conteneur ou inférieure ou égale à 0.");
  }
}

function aff_dechets_recycle() {
  var index, len, bname;
  const ref2 = document.getElementById("sel_filiere");
  const tabref2 = ref2.value.split('|');
  ref2.disabled = true;
    const id_filiere = document.getElementById("id_filiere");
  id_filiere.value = tabref2[0];

  const type_dechet = document.getElementById("type_dechet");
  type_dechet.value = tabref2[1];

  for (index = 0, len = tabref2[1].split('a').length; index < len; ++index) {

    console.log(tabref2[1].split('a'));

    bname = tabref2[1].split('a')[index];
    if(parseInt(bname > 0)){
document.getElementById(bname).style.display = "block";
    }
}

}

function masse_write_recycle(nom, id, masse_max, masse_bac) {
    const ref = document.getElementById("sel_filiere");
  const tabref = ref.value.split('|');

  const id_filiere = document.getElementById("id_filiere");
  id_filiere.value = tabref[0];

  const type_dechet = document.getElementById("type_dechet");
  type_dechet.value = tabref[1];

  ref.disabled = true;
  const input = document.getElementById("number");
  const number_value = parseFloat(input.value);
  const masse_reele = number_value - masse_bac;

  if (masse_reele > 0.00) {
    if (number_value <= masse_max) {
      const masseTotale = document.getElementById("massetot");
      masseTotale.textContent = (parseFloat(masseTotale.textContent) + masse_reele).toFixed(2);

      id.value = (parseFloat(id.value) + masse_reele).toFixed(2);
      nom.textContent = (parseFloat(nom.textContent) + masse_reele).toFixed(2);
      input.value = "";

      const nAjout = document.getElementById("najout");
      nAjout.value = parseInt(nAjout.value) + 1;

      number_clear();
    } else {
      input.setCustomValidity("Masse supérieure aux limites de pesée de la balance.");
    }
  } else {
      input.setCustomValidity("Masse entrée inférieure au poids du conteneur ou inférieure ou égale à 0.");
  }
}

function recocom() {
  document.getElementById("commentaire").value =
    document.getElementById("commentaireini").value;
}

function tdechet_add(pesee_max) {
  const ref = document.getElementById("sel_filiere");
  const tabref = ref.value.split('|');

  const id_filiere = document.getElementById("id_filiere");
  id_filiere.value = tabref[0];

  const id_type_dechet = document.getElementById("id_type_dechet");
  id_type_dechet.value = tabref[1];

  const type_dechet = document.getElementById("type_dechet");
  type_dechet.value = tabref[2];

  ref.disabled = true;
  masse_write(document.getElementById(tabref[1]), document.getElementById("m"+tabref[1]), pesee_max, 0.0);
}
<|endoftext|>


<commit_before>(function (a) {

  var participate;
  var commentList;
  var comment;
  var status = a.scope.status;

  // returns a hidden form to edit an event
  function editCommentFormFactory(comment_id) {

    var form = $('<form style="display:none" class="edit-comment form lefty">');

    var textArea = $('<textarea name="content">');

    form.append(textArea);

    form.append('<input class="btn btn-large" type="submit" value="Change" >');

    form.submit(function (e) {

      e.preventDefault();

      var content = textArea.val();

      // simply rewrite the paragraph on success.
      a.updateComment({comment_id: comment_id, content: content}, function () {

        $('#comment-' + comment_id + ' > p.content').text(content);

      });

    });

    return form;

  }

  var today = Date.parse('today');

  function makeCommentTimeStr (str) {

    var date = Date.parse(str);

    var result = date.toString("h:mm ");

    if (today.toString("M/d/yy") == date.toString("M/d/yy")) {

      result += " Today";
      return result;

    }

    result += date.toString("M/d/yyyy");
    return result;

  }

  function commentFactory (comment_id, user_id, user_name, date_posted, content) {

    var elem = $('
');
    // put in a link to the user including name. TODO retrieve name in comment data
    elem.append(''+user_name+'');

    var contentElem = $('
'+content+'
');

    // append comment content.
    elem.append('
'+content+'
');

    elem.append(''+date_posted+'');

    // check if the user viewing the page is the owner of the comment.
    // NOTE: this does not prevent CSRF or XSS attacks. remember that
    // anyone can send an arbitrary post request.
    if (user_id == a.scope.current_user_id) {

      var dropCommentBtn = $('
delete
');

      dropCommentBtn.click(function () {

        // sends comment id to api endpoint. user id is checked server side via session
        a.destroyComment({comment_id: comment_id}, function (data) {

          // remove the comment from page on success.
          elem.remove();

        });

      });

      elem.append(dropCommentBtn);

      var editCommentBtn = $('
edit
');

      var editCommentForm = editCommentFormFactory(comment_id);

      editCommentBtn.click(function () {

        var self = $(this);

        // switch the text.
        var text = self.text();

        (text == "edit" && self.text('close')) ||
        (text == "close" && self.text('edit'));

        var content = $('p.content', elem).text();
        $('textarea', editCommentForm).val(content);

        // show/hide the edit form.
        editCommentForm.slideToggle();

      });

      elem.append(editCommentBtn);

      elem.append(editCommentForm);

    }

    // TODO include timestamp in comment display

    return elem;

  }

  $(function () {

    var participating = false;
    // pnd or act.

    var participateForm = $('<form class="lefty" style="display:none" id="participate">');
    participateForm.append('<input class="btn btn-large" type="submit" value="Participate!">');

    if (status == "PND") {

      $('input[type=submit]', participateForm).addClass('disabled')
      .attr('disabled','disabled');

    }

    // clicking on the button will submit ajax request to participate in event.
    participateForm.submit(function (e) {

      e.preventDefault();

      a.participateInEvent(a.scope.event_id, function (data) {

        // refresh page with you as a participant.
        pollParticipants();

      });

    });

    $('div.participate').append(participateForm);

    var participantList = $('div.participants');

    // builds a frame with user name and link to account.
    function participantFactory (user_id, user_name) {

      var elem = $('
');

      // if the current user is the participant being rendered. create an 'unattend' button
      if (a.scope.current_user_id == user_id) {

        participating = true;

        var unattend = $('
X
');

        unattend.click(function () {

          var event_id = a.scope.event_id;

          a.unattendEvent(event_id, function () {

            // remove the element from the page.
            elem.remove();

          });

        });

        elem.append(unattend);

      }

      elem.append(''+user_name+'');

      return elem;

    }

    var pollParticipantsInterval;

    function pollParticipants () {

      // request list of participants.
      a.loadEventParticipants(a.scope.event_id, function (data) {

        data = data || '{}';
        data = JSON.parse(data);

        // assume the current user is not participating before each poll
        participating = false;

        for(var i = 0; i < data.length; i++) {

          var participant = data[i];
          var user_id = participant.user_id;
          var user_name = participant.user_name;

          // skip participants added to page.
          if ($('div#participant-' + user_id).length > 0) {

            if (user_id == a.scope.current_user_id) {

              participating = true;

            }

            continue;

          }

          participantList.append(participantFactory(user_id, user_name));

        }

        // show form if you're not participating
        if (!participating) {

          participateForm.show();

        }
        // otherwise hide the form.
        else {

          participateForm.hide();

        }

      });

    }

    // poll every two and a half seconds, but only if the status is active
    if (status == "ACT") {

      pollParticipantsInterval = window.setInterval(pollParticipants, 2500);

    }

    commentList = $("div.event-view.aggregate.comment");

    // interval to check for comments
    var pollComentInterval;

    function pollComments () {

      a.loadEventComments(a.scope.event_id, function (data) {

        data = JSON.parse(data);

        if (data.status == "fail") {

          commentList.html('
No comments on this event
');

          return;

        }
        else  {
          //remove no comments message.

          var errmsg = $('p.no-comments.error', commentList);
          // remove if message exists.
          errmsg.length > 0 && errmsg.remove();

        }

        // iterate through comments found.
        for (var i = 0; i < data.length; i ++) {

          var newComment = data[i];
          var comment_id = newComment.comment_id;

          // see if comment exists on page
          if ($("#comment-" + comment_id, commentList).length > 0) {

            // skip if it does.
            continue;

          }

          // add new comment.
          var user_id = newComment.user_id;
          var user_name = newComment.user_name;
          var date_posted = newComment.date_posted;
          var new_content = newComment.content;

          commentList.prepend(commentFactory(comment_id, user_id, user_name, date_posted, new_content));

        }

      });

    }

    status == "ACT" && pollComments();

    // check for comments every 3 seconds.
    // uncomment for production. commented out for testing.
    status == "ACT" && (pollCommentInterval = window.setInterval(pollComments, 3000));

    commentBody = $("#comment-body");

    var ratings = $('div#rating');
    var totalRating = $('span#total-rating');

    // creating a function to bind the value of i to the stars callback
    function bindStarHandler (star, rating) {

      star.click(function () {

        var event_id = a.scope.event_id;

        a.rateEvent({event_id: event_id, rating: rating}, function (data) {

          data = data || "{}";
          data = JSON.parse(data);
          console.log(data);

          totalRating.text(data.total_rating);

          // add class to rated.
          star.addClass('rated');

          // remove all other stars ratings.
          $('div.star', ratings).not(star).removeClass('rated');

        });

      });

    }

    for (var i = 0; i <= 5; i++) {

      var text;

      if( i == 0 ) {
        text = "X";
      }
      else {
        text = "★";
      }
      var star = $('
'+text+'
');

      // adding an event handler here normally will not
      // retain the value of i. passing i to this function binds
      // the value to the callback handler on the star.
      if (status == "ACT") {

        bindStarHandler(star, i);

      }
      else {

        star.addClass('disabled');

      }

      ratings.append(star);

    }

    if (status != "ACT") {

      $('form#new-comment > input[type=submit]').addClass('disabled')
        .attr('disabled', 'disabled');

    }

    // stop form from doing regular submit
    $('form#new-comment').submit(function (e) {

      e.preventDefault();

      content = commentBody.val();

      a.commentOnEvent({

        event_id: a.scope.event_id,

        content: content

      }, function (e) {

        // look for new ones, which should include ours.
        pollComments();

      });

    });

    var catAggregate = $('div.aggregate.categories');

    function categoryFactory (label) {

      var elem = $('
' + label + '
');

      return elem;

    }

    a.getCategories({event_id: a.scope.event_id}, function (data) {

      data = data || "{}";
      data = JSON.parse(data);

      console.log(data);

      if (data.fail) {

        catAggregate.append('
 This event is untagged
');
        return;

      }

      for (var i in data) {

        var category = data[i];
        var label = category.label;

        catAggregate.append(categoryFactory(label));

      }

    })

  });

})(app);
<commit_msg>added user portrait to event comments<commit_after>(function (a) {

  var participate;
  var commentList;
  var comment;
  var status = a.scope.status;

  // returns a hidden form to edit an event
  function editCommentFormFactory(comment_id) {

    var form = $('<form style="display:none" class="edit-comment form lefty">');

    var textArea = $('<textarea name="content">');

    form.append(textArea);

    form.append('<input class="btn btn-large" type="submit" value="Change" >');

    form.submit(function (e) {

      e.preventDefault();

      var content = textArea.val();

      // simply rewrite the paragraph on success.
      a.updateComment({comment_id: comment_id, content: content}, function () {

        $('#comment-' + comment_id + ' > p.content').text(content);

      });

    });

    return form;

  }

  var today = Date.parse('today');

  function makeCommentTimeStr (str) {

    var date = Date.parse(str);

    var result = date.toString("h:mm ");

    if (today.toString("M/d/yy") == date.toString("M/d/yy")) {

      result += " Today";
      return result;

    }

    result += date.toString("M/d/yyyy");
    return result;

  }

  function commentFactory (comment_id, user_id, user_name, date_posted, content) {

    var elem = $('
');
    var avatar = $('
' + user_name + '
');
    var img = a.util.loadEntityPic({type: 'users', id: user_id, style: "thumbnail", link: true});
    avatar.append(img);
    // put in a link to the user including name. TODO retrieve name in comment data
    elem.append(avatar);

    var contentElem = $('
'+content+'
');

    // append comment content.
    elem.append('
'+content+'
');

    elem.append(''+date_posted+'');

    // check if the user viewing the page is the owner of the comment.
    // NOTE: this does not prevent CSRF or XSS attacks. remember that
    // anyone can send an arbitrary post request.
    if (user_id == a.scope.current_user_id) {

      var dropCommentBtn = $('
delete
');

      dropCommentBtn.click(function () {

        // sends comment id to api endpoint. user id is checked server side via session
        a.destroyComment({comment_id: comment_id}, function (data) {

          // remove the comment from page on success.
          elem.remove();

        });

      });

      elem.append(dropCommentBtn);

      var editCommentBtn = $('
edit
');

      var editCommentForm = editCommentFormFactory(comment_id);

      editCommentBtn.click(function () {

        var self = $(this);

        // switch the text.
        var text = self.text();

        (text == "edit" && self.text('close')) ||
        (text == "close" && self.text('edit'));

        var content = $('p.content', elem).text();
        $('textarea', editCommentForm).val(content);

        // show/hide the edit form.
        editCommentForm.slideToggle();

      });

      elem.append(editCommentBtn);

      elem.append(editCommentForm);

    }

    // TODO include timestamp in comment display

    return elem;

  }

  $(function () {

    var participating = false;
    // pnd or act.

    var participateForm = $('<form class="lefty" style="display:none" id="participate">');
    participateForm.append('<input class="btn btn-large" type="submit" value="Participate!">');

    if (status == "PND") {

      $('input[type=submit]', participateForm).addClass('disabled')
      .attr('disabled','disabled');

    }

    // clicking on the button will submit ajax request to participate in event.
    participateForm.submit(function (e) {

      e.preventDefault();

      a.participateInEvent(a.scope.event_id, function (data) {

        // refresh page with you as a participant.
        pollParticipants();

      });

    });

    $('div.participate').append(participateForm);

    var participantList = $('div.participants');

    // builds a frame with user name and link to account.
    function participantFactory (user_id, user_name) {

      var elem = $('
');

      // if the current user is the participant being rendered. create an 'unattend' button
      if (a.scope.current_user_id == user_id) {

        participating = true;

        var unattend = $('
X
');

        unattend.click(function () {

          var event_id = a.scope.event_id;

          a.unattendEvent(event_id, function () {

            // remove the element from the page.
            elem.remove();

          });

        });

        elem.append(unattend);

      }

      elem.append(''+user_name+'');

      return elem;

    }

    var pollParticipantsInterval;

    function pollParticipants () {

      // request list of participants.
      a.loadEventParticipants(a.scope.event_id, function (data) {

        data = data || '{}';
        data = JSON.parse(data);

        // assume the current user is not participating before each poll
        participating = false;

        for(var i = 0; i < data.length; i++) {

          var participant = data[i];
          var user_id = participant.user_id;
          var user_name = participant.user_name;

          // skip participants added to page.
          if ($('div#participant-' + user_id).length > 0) {

            if (user_id == a.scope.current_user_id) {

              participating = true;

            }

            continue;

          }

          participantList.append(participantFactory(user_id, user_name));

        }

        // show form if you're not participating
        if (!participating) {

          participateForm.show();

        }
        // otherwise hide the form.
        else {

          participateForm.hide();

        }

      });

    }

    // poll every two and a half seconds, but only if the status is active
    if (status == "ACT") {

      pollParticipantsInterval = window.setInterval(pollParticipants, 2500);

    }

    commentList = $("div.event-view.aggregate.comment");

    // interval to check for comments
    var pollComentInterval;

    function pollComments () {

      a.loadEventComments(a.scope.event_id, function (data) {

        data = JSON.parse(data);

        if (data.status == "fail") {

          commentList.html('
No comments on this event
');

          return;

        }
        else  {
          //remove no comments message.

          var errmsg = $('p.no-comments.error', commentList);
          // remove if message exists.
          errmsg.length > 0 && errmsg.remove();

        }

        // iterate through comments found.
        for (var i = 0; i < data.length; i ++) {

          var newComment = data[i];
          var comment_id = newComment.comment_id;

          // see if comment exists on page
          if ($("#comment-" + comment_id, commentList).length > 0) {

            // skip if it does.
            continue;

          }

          // add new comment.
          var user_id = newComment.user_id;
          var user_name = newComment.user_name;
          var date_posted = newComment.date_posted;
          var new_content = newComment.content;

          commentList.prepend(commentFactory(comment_id, user_id, user_name, date_posted, new_content));

        }

      });

    }

    status == "ACT" && pollComments();

    // check for comments every 3 seconds.
    // uncomment for production. commented out for testing.
    status == "ACT" && (pollCommentInterval = window.setInterval(pollComments, 3000));

    commentBody = $("#comment-body");

    var ratings = $('div#rating');
    var totalRating = $('span#total-rating');

    // creating a function to bind the value of i to the stars callback
    function bindStarHandler (star, rating) {

      star.click(function () {

        var event_id = a.scope.event_id;

        a.rateEvent({event_id: event_id, rating: rating}, function (data) {

          data = data || "{}";
          data = JSON.parse(data);
          console.log(data);

          totalRating.text(data.total_rating);

          // add class to rated.
          star.addClass('rated');

          // remove all other stars ratings.
          $('div.star', ratings).not(star).removeClass('rated');

        });

      });

    }

    for (var i = 0; i <= 5; i++) {

      var text;

      if( i == 0 ) {
        text = "X";
      }
      else {
        text = "★";
      }
      var star = $('
'+text+'
');

      // adding an event handler here normally will not
      // retain the value of i. passing i to this function binds
      // the value to the callback handler on the star.
      if (status == "ACT") {

        bindStarHandler(star, i);

      }
      else {

        star.addClass('disabled');

      }

      ratings.append(star);

    }

    if (status != "ACT") {

      $('form#new-comment > input[type=submit]').addClass('disabled')
        .attr('disabled', 'disabled');

    }

    // stop form from doing regular submit
    $('form#new-comment').submit(function (e) {

      e.preventDefault();

      content = commentBody.val();

      a.commentOnEvent({

        event_id: a.scope.event_id,

        content: content

      }, function (e) {

        // look for new ones, which should include ours.
        pollComments();

      });

    });

    var catAggregate = $('div.aggregate.categories');

    function categoryFactory (label) {

      var elem = $('
' + label + '
');

      return elem;

    }

    a.getCategories({event_id: a.scope.event_id}, function (data) {

      data = data || "{}";
      data = JSON.parse(data);

      console.log(data);

      if (data.fail) {

        catAggregate.append('
 This event is untagged
');
        return;

      }

      for (var i in data) {

        var category = data[i];
        var label = category.label;

        catAggregate.append(categoryFactory(label));

      }

    })

  });

})(app);
<|endoftext|>


<commit_before>define(['jquery', 'underscore', 'backbone'],
function($      ,  _          ,  Backbone ) {
    var BaseView = Backbone.View.extend({
        setActivePage: function(name) {
            $('.navbar-tabs.nav li > a').removeClass('active');
            $('head title').html('Jon Lai | ' + name);
            var id = '#nav-' + name.toLowerCase();
            if ($(id) != []) { $(id).addClass('active'); }
        }
    });

    return BaseView;
});
<commit_msg>Remove unneeded brackets from one-line if clause<commit_after>define(['jquery', 'underscore', 'backbone'],
function($      ,  _          ,  Backbone ) {
    var BaseView = Backbone.View.extend({
        setActivePage: function(name) {
            $('.navbar-tabs.nav li > a').removeClass('active');
            $('head title').html('Jon Lai | ' + name);
            var id = '#nav-' + name.toLowerCase();
            if ($(id) != []) $(id).addClass('active');
        }
    });

    return BaseView;
});
<|endoftext|>


<commit_before>function VpmLoader(url, url_query, get_view_quaternion, callback, info_callback) {
	var m_url = url;
	var m_url_query = url_query;
	var m_get_view_quaternion = get_view_quaternion;
	var m_frame_callback = callback;
	var m_info_callback = info_callback;
	var m_options = {
			"num_per_quarter" : 3,
			"fps" : 10,
			"keyframe_interval" : 1,
			"keyframe_offset" : [],
	};
	var m_zip_entries = null;
	var m_frames = {};
	var m_loaded_framecount = 0;
	var m_request_framecount = 0;
	var m_stream_framecount = 0;
	var m_request_loop_timer = null;
	var m_stream_loop_timer = null;

	var m_view_quat = new THREE.Quaternion();
	var m_mbps = 0;
	var m_bytes_in_1000ms = 0;
	var m_timestamp = new Date().getTime();
	var m_x_deg = 0;
	var m_y_deg = 0;
	var m_eos = false;

	function loadFile(url, path, callback, error_callback) {
		if(m_zip_entries){
			loadFile_from_zip(url, path, callback, error_callback);
		}else{
			loadFile_from_dir(url, path, callback, error_callback);
		}
	}

	function loadFile_from_zip(url, path, callback, error_callback) {
		if(path[0] == '/'){
			path = path.substr(1);
		}
		if(m_zip_entries[path]){
			m_zip_entries[path].getData(null, callback, error_callback);
		}else{
			error_callback({code:"NO_ENTRY",responseURL:path});
		}
	}

	function loadFile_from_dir(url, path, callback, error_callback) {
		var req = new XMLHttpRequest();
		req.responseType = "arraybuffer";
		req.open("get", url + path, true);

		req.onerror = function() {
			if(error_callback){
				error_callback(req);
			}
			return;
		};
		req.onload = function() {
			if(req.status != 200){
				req.onerror();
				return;
			}
			callback(new Uint8Array(req.response));
		};
		req.send(null);
	}
	function request_frame(x_deg, y_deg, framecount, count){
		var path = "/" + x_deg + "_" + y_deg + "/" + framecount + ".pif";
		// console.log(path);
		loadFile(m_url, path, (data) => {
			if(m_loaded_framecount == 0){
				console.log("start");
				if(m_info_callback){
					m_info_callback("sos");
				}
			}
			m_loaded_framecount++;
			m_frames[framecount] = data;

			m_bytes_in_1000ms += data.byteLength;

			var now = new Date().getTime();
			var elapsed = now - m_timestamp;
			if(elapsed > 1000)
			{// bitrate
				var mbps = 8 * m_bytes_in_1000ms / elapsed / 1000;
				if(m_mbps == 0){
					m_mbps = mbps;
				}else{
					m_mbps = m_mbps*0.9+mbps*0.1;
				}
				m_timestamp = now;
				m_bytes_in_1000ms = 0;
			}
		}, (err) => {
			if(err.code != "NO_ENTRY" && count < 10){
				request_frame(x_deg, y_deg, framecount, count + 1);
				return;
			}
			if(m_loaded_framecount == 0){
				console.log("not found : " + err.responseURL);
				if(m_info_callback){
					m_info_callback("not_found");
				}
			}else if(!m_eos){
				m_eos = true;
				console.log("end");
				if(m_info_callback){
					m_info_callback("eos");
				}
			}
		});
	}

	function parseBoolean(str) {
		return str == "yes" || str == "on" || str == "true";
	}

	function start_request_loop(){
		m_request_loop_timer = setInterval(() => {
			if(m_eos){
				if(m_options.loop){
					m_eos = false;
					m_request_framecount = 0;
					m_stream_framecount = 0;
				}else{
					clearInterval(m_stream_loop_timer);
					clearInterval(m_stream_loop_timer);
				}
				return;
			}
			if(m_request_framecount > m_stream_framecount + 5){
				return;
			}
			if((m_request_framecount % m_options.keyframe_interval) == 0) {
				var q = m_get_view_quaternion();

				var v = new THREE.Vector3( 0, -1, 0 );
				v.applyQuaternion( q );
				v.r = Math.sqrt(v.x*v.x+v.z*v.z);

				var euler = {
					x:Math.atan2(v.r, -v.y),
					y:-Math.atan2(v.x, -v.z),
				};
				var x = parseInt(THREE.Math.radToDeg(euler.x));
				var y = parseInt(THREE.Math.radToDeg(euler.y));
				var p_angle = 90/m_options.num_per_quarter;
				var p = Math.round(x/p_angle);
				var _p = (p <= m_options.num_per_quarter) ? p : m_options.num_per_quarter * 2 - p;
				var split_y = (_p == 0) ? 1 : 4 * _p;
				var y_angle = 360/split_y;

				x = p*p_angle;
				y = Math.round(y/y_angle)*y_angle;
				x = (x+360)%360;
				y = (y+360)%360;
				if(x == 0 || x == 180) {
					y = 0;
				}
				m_x_deg = x;
				m_y_deg = y;
			}
			++m_request_framecount;// starts from 1
			request_frame(m_x_deg, m_y_deg, m_request_framecount, 1);
		}, 1000/m_options.fps);
	}

	function start_stream_loop(){
		m_stream_loop_timer = setInterval(() => {
			if(m_frames[m_stream_framecount+1] == null){
				return;
			}
			m_stream_framecount++;
			var data = m_frames[m_stream_framecount];
			m_frames[m_stream_framecount] = undefined;
			if(m_frame_callback){
				m_frame_callback(data);
			}
		}, 1000/m_options.fps);
	}

	var init = function(){
		loadFile(m_url, "/config.json", (data)=>{
			var options = {};
			var txt = (new TextDecoder).decode(data);
			if (txt) {
				options = JSON.parse(txt);
			}
			m_options = Object.assign(m_options, options);
			if(url_query['loop']){
				m_options.loop = parseBoolean(url_query['loop']);
			}
			if(url_query['fps']){
				m_options.fps = parseFloat(url_query['fps']);
			}
			start_request_loop();
			start_stream_loop();
			// request_new_frame();
		});
	};

	if(m_url.toLowerCase().endsWith('.pvf') || m_url.toLowerCase().endsWith('.zip')){
		zip.useWebWorkers = false;
		zip.createReader(new zip.HttpRangeReader(m_url, m_url_query), function(zipReader) {
			zipReader.getEntries(function(entries) {
				m_zip_entries = {};
				for(var entry of entries){
					if(entry.directory){
						continue;
					}
					m_zip_entries[entry.filename] = entry;
				}
				init();
			})
		});
	} else {
		init();
	}

	var self = {
		get_bitrate_mbps : () => {
			return m_mbps;
		},
	}; // self
	return self;
}<commit_msg>refactoring<commit_after>function VpmLoader(url, url_query, get_view_quaternion, callback, info_callback) {
	var m_url = url;
	var m_url_query = url_query;
	var m_get_view_quaternion = get_view_quaternion;
	var m_frame_callback = callback;
	var m_info_callback = info_callback;
	var m_options = {
			"num_per_quarter" : 3,
			"fps" : 10,
			"keyframe_interval" : 1,
			"keyframe_offset" : [],
	};
	var m_zip_entries = null;
	var m_frames = {};
	var m_loaded_framecount = 0;
	var m_request_framecount = 0;
	var m_stream_framecount = 0;
	var m_request_loop_timer = null;
	var m_stream_loop_timer = null;

	var m_view_quat = new THREE.Quaternion();
	var m_mbps = 0;
	var m_bytes_in_1000ms = 0;
	var m_timestamp = new Date().getTime();
	var m_x_deg = 0;
	var m_y_deg = 0;
	var m_eos = false;

	function loadFile(url, path, callback, error_callback) {
		if(m_zip_entries){
			loadFile_from_zip(url, path, callback, error_callback);
		}else{
			loadFile_from_dir(url, path, callback, error_callback);
		}
	}

	function loadFile_from_zip(url, path, callback, error_callback) {
		if(path[0] == '/'){
			path = path.substr(1);
		}
		if(m_zip_entries[path]){
			m_zip_entries[path].getData(null, callback, error_callback);
		}else{
			error_callback({code:"NO_ENTRY",responseURL:path});
		}
	}

	function loadFile_from_dir(url, path, callback, error_callback) {
		var req = new XMLHttpRequest();
		req.responseType = "arraybuffer";
		req.open("get", url + path, true);

		req.onerror = function() {
			if(error_callback){
				error_callback(req);
			}
			return;
		};
		req.onload = function() {
			if(req.status != 200){
				req.onerror();
				return;
			}
			callback(new Uint8Array(req.response));
		};
		req.send(null);
	}
	function request_frame(x_deg, y_deg, framecount, count){
		var path = "/" + x_deg + "_" + y_deg + "/" + framecount + ".pif";
		// console.log(path);
		loadFile(m_url, path, (data) => {
			if(m_loaded_framecount == 0){
				console.log("start");
				if(m_info_callback){
					m_info_callback("sos");
				}
			}
			m_loaded_framecount++;
			m_frames[framecount] = data;

			m_bytes_in_1000ms += data.byteLength;

			var now = new Date().getTime();
			var elapsed = now - m_timestamp;
			if(elapsed > 1000)
			{// bitrate
				var mbps = 8 * m_bytes_in_1000ms / elapsed / 1000;
				if(m_mbps == 0){
					m_mbps = mbps;
				}else{
					m_mbps = m_mbps*0.9+mbps*0.1;
				}
				m_timestamp = now;
				m_bytes_in_1000ms = 0;
			}
		}, (err) => {
			if(err.code != "NO_ENTRY" && count < 10){
				request_frame(x_deg, y_deg, framecount, count + 1);
				return;
			}
			if(m_loaded_framecount == 0){
				console.log("not found : " + err.responseURL);
				if(m_info_callback){
					m_info_callback("not_found");
				}
			}else if(!m_eos){
				m_eos = true;
				console.log("end");
				if(m_info_callback){
					m_info_callback("eos");
				}
			}
		});
	}

	function parseBoolean(str) {
		return str == "yes" || str == "on" || str == "true";
	}

	function start_request_loop(){
		m_request_loop_timer = setInterval(() => {
			if(m_eos){
				if(m_options.loop){
					m_eos = false;
					m_request_framecount = 0;
					m_stream_framecount = 0;
				}else{
					clearInterval(m_stream_loop_timer);
					clearInterval(m_stream_loop_timer);
				}
				return;
			}
			if(m_request_framecount > m_stream_framecount + 5){
				return;
			}
			if((m_request_framecount % m_options.keyframe_interval) == 0) {
				var q = m_get_view_quaternion();

				var v = new THREE.Vector3( 0, -1, 0 );
				v.applyQuaternion( q );
				v.r = Math.sqrt(v.x*v.x+v.z*v.z);

				var euler = {
					x:Math.atan2(v.r, -v.y),
					y:-Math.atan2(v.x, -v.z),
				};
				var x = parseInt(THREE.Math.radToDeg(euler.x));
				var y = parseInt(THREE.Math.radToDeg(euler.y));
				var p_angle = 90/m_options.num_per_quarter;
				var p = Math.round(x/p_angle);
				var _p = (p <= m_options.num_per_quarter) ? p : m_options.num_per_quarter * 2 - p;
				var split_y = (_p == 0) ? 1 : 4 * _p;
				var y_angle = 360/split_y;

				x = p*p_angle;
				y = Math.round(y/y_angle)*y_angle;
				x = (x+360)%360;
				y = (y+360)%360;
				if(x == 0 || x == 180) {
					y = 0;
				}
				m_x_deg = x;
				m_y_deg = y;
			}
			++m_request_framecount;// starts from 1
			request_frame(m_x_deg, m_y_deg, m_request_framecount, 1);
		}, 1000/m_options.fps);
	}

	function start_stream_loop(){
		m_stream_loop_timer = setInterval(() => {
			if(m_frames[m_stream_framecount+1] == null){
				return;
			}
			m_stream_framecount++;
			var data = m_frames[m_stream_framecount];
			m_frames[m_stream_framecount] = undefined;
			if(m_frame_callback){
				m_frame_callback(data);
			}
		}, 1000/m_options.fps);
	}

	var init = function(){
		loadFile(m_url, "/config.json", (data)=>{
			var options = {};
			var txt = (new TextDecoder).decode(data);
			if (txt) {
				options = JSON.parse(txt);
			}
			m_options = Object.assign(m_options, options);
			if(url_query['loop']){
				m_options.loop = parseBoolean(url_query['loop']);
			}
			if(url_query['fps']){
				m_options.fps = parseFloat(url_query['fps']);
			}
			start_request_loop();
			start_stream_loop();
		});
	};

	if(m_url.toLowerCase().endsWith('.pvf') || m_url.toLowerCase().endsWith('.zip')){
		zip.useWebWorkers = false;
		zip.createReader(new zip.HttpRangeReader(m_url, m_url_query), function(zipReader) {
			zipReader.getEntries(function(entries) {
				m_zip_entries = {};
				for(var entry of entries){
					if(entry.directory){
						continue;
					}
					m_zip_entries[entry.filename] = entry;
				}
				init();
			})
		});
	} else {
		init();
	}

	var self = {
		get_bitrate_mbps : () => {
			return m_mbps;
		},
	}; // self
	return self;
}<|endoftext|>


<commit_before>/*!
 * world.core.js
 * Define a world and manage its main loop.
 *
 * World JS: Evolution Simulator
 * https://github.com/anvoz/world-js
 * Copyright (c) 2013 An Vo - anvo4888@gmail.com
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 */

(function(window, undefined) {
    'use strict';

    var
        // Localise globals
        document = window.document,
        requestAnimationFrame = (function() {
            // Use requestAnimationFrame for better animation
            return (
                window.requestAnimationFrame       ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame    ||
                window.oRequestAnimationFrame      ||
                window.msRequestAnimationFrame     ||
                function(callback, element) {
                    window.setTimeout(function() {
                        callback(+new Date);
                    }, 1000 / 60);
                }
            );
        })();

    /**
     * WorldJS constructor
     * Define default properties of a world
     */
    var WorldJS = window.WorldJS = function() {
        // The value of this refers to a newly created world
        var world = this;

        // The world is currently not running
        world.running = false;
        // Trigger once after the world animation was stopped
        world.stopCallback = function() {};

        // Size of the world in pixels
        world.width = 640;
        world.height = 360;
        // Prevent objects from drawing outside the world
        world.padding = 10; // 10 pixels from border

        world.canvas = {
            wrapper: false, // DOM element that wraps the canvas
            context: false  // HTML5 drawing context
        };
        world.sprite = {
            src: 'images/seeds.png',
            image: false    // DOM image object of sprite
        };

        // World contains seeds
        // Each seed has an unique id
        world.nextSeedId = 1;

        // Don't draw every frame (tick) if total seeds > this value
        world.maxSafeSeedsForDisplay = 30000;

        // Used for calculating age of a seed
        world.tickPerYear = 60;

        /*
         * Used for indicating a year, +1 every frame (tick)
         * Example: tickPerYear = 60
         * tickMod: 0   60  120 180 ...
         * year:    0   1   2   3   ...
         */
        world.tickMod = 0;

        /*
         * Seeds don't trigger their main actions every frame.
         * Example: In 60 frames, a male only seeks for female twice (in 30th frame and 60th frame).
         * To make it's more efficient, main actions of all seeds will be distributed over all frames.
         * Example: male_1 will seek for female in 30th frame, 60th frame...
         *          male_2 will seek for female in 31th frame, 61th frame...
         *
         * distributedTicks property has its length equals <tickPerYear - 1> which means:
         * In every <tickPerYear> frames
         * <tickPerYear - 1> frames are used to trigger main actions of seeds.
         * Last frame is used for other calculations such as statistic, user interface update...
         */
        world.distributedTicks = [];
        for (var i = 0; i < world.tickPerYear - 1; i++) {
            world.distributedTicks.push(0);
        }

        // Used for calculating frames per second
        world.lastTickTime = 0;
        world.fps = 0;

        world.Tile = new WorldJS.Tile();
        world.Knowledge = new WorldJS.Knowledge();
        world.Statistic = new WorldJS.Statistic();
        world.Rules = new WorldJS.Rules();

        // Call once every <tickPerYear> ticks
        world.yearPassedCallback = function() {};
    };

    /**
     * Insert the world into a HTML wrapper
     * wrapperId: id of an HTML wrapper element
     */
    WorldJS.prototype.init = function(wrapperId) {
        var world = this;

        // Add a canvas that fits size of its wrapper
        var canvas = document.createElement('canvas'),
            wrapper = world.canvas.wrapper = document.getElementById(wrapperId),
            // Size of the wrapper is also size of the world
            width = world.width = canvas.width = wrapper.clientWidth || world.width,
            height = world.height = canvas.height = wrapper.clientHeight || world.height;
        wrapper.appendChild(canvas);

        world.canvas.context = canvas.getContext('2d');

        // Load and cache sprite
        var sprite = new Image();
        sprite.src = world.sprite.src;
        world.sprite.image = sprite;

        world.Tile.init(world, width, height);

        return world;
    };

    /**
     * Add a seed to the world
     * Seed: seed-based class
     * data: properties of seed
     */
    WorldJS.prototype.add = function(Seed, data) {
        var world = this,
            seed = new Seed(data);

        // Save a reference of the world where the seed belongs
        seed.world = world;

        // Set an unique id
        seed.id = world.nextSeedId++;

        seed.age = data.age || 0;
        if (seed.age > 0) {
            seed.tickCount = seed.age * world.tickPerYear;
        }

        seed.tickMod = world.tickMod;

        seed.IQ += world.Rules.baseIQ;

        // Randomly set coordinate of the seed
        var random = function(min, max) {
            return Math.floor(Math.random() * (max - min + 1) + min);
        };
        if (seed.x === false) {
            seed.x = random(0, world.width - 1 - Math.max(seed.appearance.width, world.padding));
        }
        if (seed.y === false) {
            seed.y = random(0, world.height - 1 - seed.appearance.height - world.padding);
        }

        // Calculate tile index
        seed.tileIndex = world.Tile.getIndex(seed);
        // and cache the seed
        world.Tile.set(seed);

        // Put the seed in a frame which has lowest number of seeds occupied
        // to avoid a single frame that needs to trigger too many seeds' main actions
        var distributedTicks = world.distributedTicks,
            minIndex = 0,
            minValue = distributedTicks[minIndex];
        for (var i = 0, len = distributedTicks.length; i < len; i++) {
            if (distributedTicks[i] < minValue) {
                minIndex = i;
                minValue = distributedTicks[i];
            }
        }
        distributedTicks[minIndex]++;
        seed.tickIndex = minIndex;

        // By modulusing actionInterval - which equals half of tickPerYear by default
        // we have two lowest frames index: 30th (half year passed) and 60th (full year passed)
        seed.tickCount += (world.tickMod + seed.tickCount + minIndex) % seed.actionInterval;
        // stepCount also need to use minIndex to avoid synchronized jumping
        // among all seeds that appeared in the same time
        seed.stepCount = seed.tickCount;

        world.Statistic.seedAdded(seed);

        return world;
    };

    /**
     * Remove a seed from the world
     * seed: seed-based instance
     */
    WorldJS.prototype.remove = function(seed) {
        var world = this;

        world.Statistic.seedRemoved(seed);

        if (seed.married) {
            seed.married = false;
            seed.relationSeed.married = false;

            // Remove the references
            seed.relationSeed.relationSeed = false;
            seed.relationSeed = false;
        }

        world.Tile.rem(seed);

        world.distributedTicks[seed.tickIndex]--;

        return world;
    };

    /**
     * Add random people to the world
     * count: number of people to add
     * minAge, maxAge (optional)
     * fromBorder: determine people randomly appear or come from border
     *      0: random, 1: top, 2: bottom, 3: left, 4: right, 5: random border
     */
    WorldJS.prototype.addRandomPeople = function(count, minAge, maxAge, fromBorder) {
        var world = this,
            tickPerYear = world.tickPerYear,
            random = function(min, max) {
                return Math.floor(Math.random() * (max - min + 1) + min);
            };

        minAge = (typeof minAge === 'undefined') ? 15 : minAge,
        maxAge = (typeof maxAge === 'undefined') ? 20 : maxAge,
        fromBorder = (typeof fromBorder === 'undefined') ? 0 : fromBorder;

        // Add people to the world
        for (var i = 0; i < count; i++) {
            // Random age
            var age = random(minAge, maxAge),
                data = {
                    age: age,
                    tickCount: age * tickPerYear
                };

            if (fromBorder > 0) {
                var border = (fromBorder == 5) ?
                    random(1, 4) : fromBorder;
                switch (border) {
                    case 1: // top
                        data.y = 0;
                        break;
                    case 2: // bottom
                        data.y = world.height - 1;
                        break;
                    case 3: // left
                        data.x = 0;
                        break;
                    case 4: // right
                        data.x = world.width - 1;
                        break;
                }
            }

            if (i < count / 2) {
                world.add(world.Male, data);
            } else {
                world.add(world.Female, data);
            }
        }

        return world;
    };

    /**
     * World process loop
     */
    WorldJS.prototype.run = function(time) {
        var world = this,
            Statistic = world.Statistic,
            context = world.canvas.context,
            spriteImage = world.sprite.image;

        // Used for indicating a year
        // tickMod == 0 -> a year
        world.tickMod = (++world.tickMod) % world.tickPerYear;
        var yearPassed = world.tickMod === 0;

        // Draw the world every year instead of every frame if total seeds is too large
        var reDraw = (Statistic.population <= world.maxSafeSeedsForDisplay || yearPassed);

        // Clear canvas
        if (reDraw) {
            context.clearRect(0, 0, world.width, world.height);
        }

        var listTile = world.Tile.list,
            maxDisplayedSeeds = world.Tile.maxDisplayedSeeds,

            // Statistic
            sPopulation = 0,
            sIQ = 0,
            sMen = 0, sWomen = 0,
            sBoys = 0, sGirls = 0,
            sFamilies = 0;

        for (var i = 0, len = listTile.length; i < len; i++) {
            var seeds = listTile[i],
                displayedSeeds = 0;
            for (var j = 0, len2 = seeds.length; j < len2; j++) {
                if (seeds[j]) {
                    var seed = seeds[j],
                        oldTileIndex = seed.tileIndex;

                    /*
                     * When a seed moves to a different tile, its reference may be inserted into
                     * an empty spot of tile array instead of just appending to the array.
                     * That reference will possibly be available on next pass of the current loop
                     * which could cause a seed ticks twice.
                     */
                    if (seed.tickMod == world.tickMod) {
                        continue;
                    }
                    seed.tickMod = world.tickMod;

                    if (yearPassed) {
                        seed.age++;

                        // Statistic
                        sPopulation++;
                        sIQ += seed.IQ;
                        if (seed instanceof world.Male) {
                            if (seed.age <= seed.maxChildAge) {
                                sBoys++;
                            } else {
                                sMen++;
                                if (seed.married) {
                                    sFamilies++;
                                }
                            }
                        } else {
                            if (seed.age <= seed.maxChildAge) {
                                sGirls++;
                            } else {
                                sWomen++;
                            }
                        }
                    }

                    if (reDraw) {
                        // Not draw all seeds in a tile
                        if (displayedSeeds < maxDisplayedSeeds) {
                            // Draw current state of the seed
                            seed.draw(context, spriteImage);
                            displayedSeeds++;
                        }
                    }
                    // Seed moves around or triggers some actions
                    seed.tick();

                    // Update tiles if seed moves
                    var newTileIndex = world.Tile.getIndex(seed);
                    if (newTileIndex !== oldTileIndex) {
                        // Remove seed reference from old tile
                        world.Tile.rem(seed);

                        // Add seed reference to new tile
                        seed.tileIndex = newTileIndex;
                        world.Tile.set(seed);
                    }
                }
            }
        }

        if (yearPassed) {
            Statistic.yearPassed({
                population: sPopulation,
                IQ: sIQ,
                men: sMen,
                women: sWomen,
                boys: sBoys,
                girls: sGirls,
                families: sFamilies
            });

            world.Rules.change(world);
            world.Knowledge.gain(world);

            // Execute asynchronous callback
            setTimeout(function() {
                // Do HTML DOM manipulation in the callback
                world.yearPassedCallback.call(world);
            }, 1);

            if (sPopulation === 0) {
                // Stop the world
                world.running = false;
                return;
            }
        }

        // Loop animation
        if (world.running) {
            world.fps = 1000 / (time - world.lastTickTime);
            world.lastTickTime = time;

            requestAnimationFrame(world.run.bind(world));
        } else {
            // Trigger once
            world.stopCallback.call(world);
            world.stopCallback = function() {};
        }

        return world;
    };

    /**
     * Start the world
     */
    WorldJS.prototype.start = function() {
        this.running = true;
        this.run();
    };

    /**
     * Stop the world
     * callback (optional): callback trigger after the world stopped
     */
    WorldJS.prototype.stop = function(callback) {
        this.running = false;
        if (typeof callback === 'function') {
            this.stopCallback = callback;
        }
    };
})(window);<commit_msg>Used random number to avoid people appeared in the same edge of border<commit_after>/*!
 * world.core.js
 * Define a world and manage its main loop.
 *
 * World JS: Evolution Simulator
 * https://github.com/anvoz/world-js
 * Copyright (c) 2013 An Vo - anvo4888@gmail.com
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 */

(function(window, undefined) {
    'use strict';

    var
        // Localise globals
        document = window.document,
        requestAnimationFrame = (function() {
            // Use requestAnimationFrame for better animation
            return (
                window.requestAnimationFrame       ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame    ||
                window.oRequestAnimationFrame      ||
                window.msRequestAnimationFrame     ||
                function(callback, element) {
                    window.setTimeout(function() {
                        callback(+new Date);
                    }, 1000 / 60);
                }
            );
        })();

    /**
     * WorldJS constructor
     * Define default properties of a world
     */
    var WorldJS = window.WorldJS = function() {
        // The value of this refers to a newly created world
        var world = this;

        // The world is currently not running
        world.running = false;
        // Trigger once after the world animation was stopped
        world.stopCallback = function() {};

        // Size of the world in pixels
        world.width = 640;
        world.height = 360;
        // Prevent objects from drawing outside the world
        world.padding = 10; // 10 pixels from border

        world.canvas = {
            wrapper: false, // DOM element that wraps the canvas
            context: false  // HTML5 drawing context
        };
        world.sprite = {
            src: 'images/seeds.png',
            image: false    // DOM image object of sprite
        };

        // World contains seeds
        // Each seed has an unique id
        world.nextSeedId = 1;

        // Don't draw every frame (tick) if total seeds > this value
        world.maxSafeSeedsForDisplay = 30000;

        // Used for calculating age of a seed
        world.tickPerYear = 60;

        /*
         * Used for indicating a year, +1 every frame (tick)
         * Example: tickPerYear = 60
         * tickMod: 0   60  120 180 ...
         * year:    0   1   2   3   ...
         */
        world.tickMod = 0;

        /*
         * Seeds don't trigger their main actions every frame.
         * Example: In 60 frames, a male only seeks for female twice (in 30th frame and 60th frame).
         * To make it's more efficient, main actions of all seeds will be distributed over all frames.
         * Example: male_1 will seek for female in 30th frame, 60th frame...
         *          male_2 will seek for female in 31th frame, 61th frame...
         *
         * distributedTicks property has its length equals <tickPerYear - 1> which means:
         * In every <tickPerYear> frames
         * <tickPerYear - 1> frames are used to trigger main actions of seeds.
         * Last frame is used for other calculations such as statistic, user interface update...
         */
        world.distributedTicks = [];
        for (var i = 0; i < world.tickPerYear - 1; i++) {
            world.distributedTicks.push(0);
        }

        // Used for calculating frames per second
        world.lastTickTime = 0;
        world.fps = 0;

        world.Tile = new WorldJS.Tile();
        world.Knowledge = new WorldJS.Knowledge();
        world.Statistic = new WorldJS.Statistic();
        world.Rules = new WorldJS.Rules();

        // Call once every <tickPerYear> ticks
        world.yearPassedCallback = function() {};
    };

    /**
     * Insert the world into a HTML wrapper
     * wrapperId: id of an HTML wrapper element
     */
    WorldJS.prototype.init = function(wrapperId) {
        var world = this;

        // Add a canvas that fits size of its wrapper
        var canvas = document.createElement('canvas'),
            wrapper = world.canvas.wrapper = document.getElementById(wrapperId),
            // Size of the wrapper is also size of the world
            width = world.width = canvas.width = wrapper.clientWidth || world.width,
            height = world.height = canvas.height = wrapper.clientHeight || world.height;
        wrapper.appendChild(canvas);

        world.canvas.context = canvas.getContext('2d');

        // Load and cache sprite
        var sprite = new Image();
        sprite.src = world.sprite.src;
        world.sprite.image = sprite;

        world.Tile.init(world, width, height);

        return world;
    };

    /**
     * Add a seed to the world
     * Seed: seed-based class
     * data: properties of seed
     */
    WorldJS.prototype.add = function(Seed, data) {
        var world = this,
            seed = new Seed(data);

        // Save a reference of the world where the seed belongs
        seed.world = world;

        // Set an unique id
        seed.id = world.nextSeedId++;

        seed.age = data.age || 0;
        if (seed.age > 0) {
            seed.tickCount = seed.age * world.tickPerYear;
        }

        seed.tickMod = world.tickMod;

        seed.IQ += world.Rules.baseIQ;

        // Randomly set coordinate of the seed
        var random = function(min, max) {
            return Math.floor(Math.random() * (max - min + 1) + min);
        };
        if (seed.x === false) {
            seed.x = random(0, world.width - 1 - Math.max(seed.appearance.width, world.padding));
        }
        if (seed.y === false) {
            seed.y = random(0, world.height - 1 - seed.appearance.height - world.padding);
        }

        // Calculate tile index
        seed.tileIndex = world.Tile.getIndex(seed);
        // and cache the seed
        world.Tile.set(seed);

        // Put the seed in a frame which has lowest number of seeds occupied
        // to avoid a single frame that needs to trigger too many seeds' main actions
        var distributedTicks = world.distributedTicks,
            minIndex = 0,
            minValue = distributedTicks[minIndex];
        for (var i = 0, len = distributedTicks.length; i < len; i++) {
            if (distributedTicks[i] < minValue) {
                minIndex = i;
                minValue = distributedTicks[i];
            }
        }
        distributedTicks[minIndex]++;
        seed.tickIndex = minIndex;

        // By modulusing actionInterval - which equals half of tickPerYear by default
        // we have two lowest frames index: 30th (half year passed) and 60th (full year passed)
        seed.tickCount += (world.tickMod + seed.tickCount + minIndex) % seed.actionInterval;
        // stepCount also need to use minIndex to avoid synchronized jumping
        // among all seeds that appeared in the same time
        seed.stepCount = seed.tickCount;

        world.Statistic.seedAdded(seed);

        return world;
    };

    /**
     * Remove a seed from the world
     * seed: seed-based instance
     */
    WorldJS.prototype.remove = function(seed) {
        var world = this;

        world.Statistic.seedRemoved(seed);

        if (seed.married) {
            seed.married = false;
            seed.relationSeed.married = false;

            // Remove the references
            seed.relationSeed.relationSeed = false;
            seed.relationSeed = false;
        }

        world.Tile.rem(seed);

        world.distributedTicks[seed.tickIndex]--;

        return world;
    };

    /**
     * Add random people to the world
     * count: number of people to add
     * minAge, maxAge (optional)
     * fromBorder: determine people randomly appear or come from border
     *      0: random, 1: top, 2: bottom, 3: left, 4: right, 5: random border
     */
    WorldJS.prototype.addRandomPeople = function(count, minAge, maxAge, fromBorder) {
        var world = this,
            tickPerYear = world.tickPerYear,
            random = function(min, max) {
                return Math.floor(Math.random() * (max - min + 1) + min);
            };

        minAge = (typeof minAge === 'undefined') ? 15 : minAge,
        maxAge = (typeof maxAge === 'undefined') ? 20 : maxAge,
        fromBorder = (typeof fromBorder === 'undefined') ? 0 : fromBorder;

        // Add people to the world
        for (var i = 0; i < count; i++) {
            // Random age
            var age = random(minAge, maxAge),
                data = {
                    age: age,
                    tickCount: age * tickPerYear
                };

            if (fromBorder > 0) {
                var border = (fromBorder == 5) ?
                    random(1, 4) : fromBorder,
                    padding = 10;
                // Used random number to avoid people appeared in the same edge
                switch (border) {
                    case 1: // top
                        data.y = random(0, padding);
                        break;
                    case 2: // bottom
                        data.y = random(world.height - padding - 1, world.height - 1);
                        break;
                    case 3: // left
                        data.x = random(0, padding);
                        break;
                    case 4: // right
                        data.x = random(world.width - padding - 1, world.width - 1);
                        break;
                }
            }

            if (i < count / 2) {
                world.add(world.Male, data);
            } else {
                world.add(world.Female, data);
            }
        }

        return world;
    };

    /**
     * World process loop
     */
    WorldJS.prototype.run = function(time) {
        var world = this,
            Statistic = world.Statistic,
            context = world.canvas.context,
            spriteImage = world.sprite.image;

        // Used for indicating a year
        // tickMod == 0 -> a year
        world.tickMod = (++world.tickMod) % world.tickPerYear;
        var yearPassed = world.tickMod === 0;

        // Draw the world every year instead of every frame if total seeds is too large
        var reDraw = (Statistic.population <= world.maxSafeSeedsForDisplay || yearPassed);

        // Clear canvas
        if (reDraw) {
            context.clearRect(0, 0, world.width, world.height);
        }

        var listTile = world.Tile.list,
            maxDisplayedSeeds = world.Tile.maxDisplayedSeeds,

            // Statistic
            sPopulation = 0,
            sIQ = 0,
            sMen = 0, sWomen = 0,
            sBoys = 0, sGirls = 0,
            sFamilies = 0;

        for (var i = 0, len = listTile.length; i < len; i++) {
            var seeds = listTile[i],
                displayedSeeds = 0;
            for (var j = 0, len2 = seeds.length; j < len2; j++) {
                if (seeds[j]) {
                    var seed = seeds[j],
                        oldTileIndex = seed.tileIndex;

                    /*
                     * When a seed moves to a different tile, its reference may be inserted into
                     * an empty spot of tile array instead of just appending to the array.
                     * That reference will possibly be available on next pass of the current loop
                     * which could cause a seed ticks twice.
                     */
                    if (seed.tickMod == world.tickMod) {
                        continue;
                    }
                    seed.tickMod = world.tickMod;

                    if (yearPassed) {
                        seed.age++;

                        // Statistic
                        sPopulation++;
                        sIQ += seed.IQ;
                        if (seed instanceof world.Male) {
                            if (seed.age <= seed.maxChildAge) {
                                sBoys++;
                            } else {
                                sMen++;
                                if (seed.married) {
                                    sFamilies++;
                                }
                            }
                        } else {
                            if (seed.age <= seed.maxChildAge) {
                                sGirls++;
                            } else {
                                sWomen++;
                            }
                        }
                    }

                    if (reDraw) {
                        // Not draw all seeds in a tile
                        if (displayedSeeds < maxDisplayedSeeds) {
                            // Draw current state of the seed
                            seed.draw(context, spriteImage);
                            displayedSeeds++;
                        }
                    }
                    // Seed moves around or triggers some actions
                    seed.tick();

                    // Update tiles if seed moves
                    var newTileIndex = world.Tile.getIndex(seed);
                    if (newTileIndex !== oldTileIndex) {
                        // Remove seed reference from old tile
                        world.Tile.rem(seed);

                        // Add seed reference to new tile
                        seed.tileIndex = newTileIndex;
                        world.Tile.set(seed);
                    }
                }
            }
        }

        if (yearPassed) {
            Statistic.yearPassed({
                population: sPopulation,
                IQ: sIQ,
                men: sMen,
                women: sWomen,
                boys: sBoys,
                girls: sGirls,
                families: sFamilies
            });

            world.Rules.change(world);
            world.Knowledge.gain(world);

            // Execute asynchronous callback
            setTimeout(function() {
                // Do HTML DOM manipulation in the callback
                world.yearPassedCallback.call(world);
            }, 1);

            if (sPopulation === 0) {
                // Stop the world
                world.running = false;
                return;
            }
        }

        // Loop animation
        if (world.running) {
            world.fps = 1000 / (time - world.lastTickTime);
            world.lastTickTime = time;

            requestAnimationFrame(world.run.bind(world));
        } else {
            // Trigger once
            world.stopCallback.call(world);
            world.stopCallback = function() {};
        }

        return world;
    };

    /**
     * Start the world
     */
    WorldJS.prototype.start = function() {
        this.running = true;
        this.run();
    };

    /**
     * Stop the world
     * callback (optional): callback trigger after the world stopped
     */
    WorldJS.prototype.stop = function(callback) {
        this.running = false;
        if (typeof callback === 'function') {
            this.stopCallback = callback;
        }
    };
})(window);<|endoftext|>