<commit_before>import * as $j from 'jquery';
import { Damage } from '../damage';
import { Team, isTeam } from '../utility/team';
import * as matrices from '../utility/matrices';
import * as arrayUtils from '../utility/arrayUtils';
import { Creature } from '../creature';

/** Creates the abilities
 * @param {Object} G the game object
 * @return {void}
 */
export default (G) => {
	G.abilities[31] = [
		//	First Ability: Bad Doggie
		{
			triggerFunc: function () {
				// When upgraded, trigger both at start and end of turn
				// Otherwise just trigger at start
				if (this.isUpgraded()) {
					return 'onStartPhase onEndPhase';
				}
				return 'onStartPhase';
			},

			require: function () {
				// Check requirements in activate() so the ability is always highlighted
				return this.testRequirements();
			},

			activate: function () {
				// Check if there's an enemy creature in front
				let hexesInFront = this.creature.getHexMap(matrices.inlinefront2hex);
				if (hexesInFront.length < 1) {
					return;
				}
				let target = hexesInFront[0].creature;
				if (!target) {
					return;
				}
				if (!isTeam(this.creature, target, Team.Enemy)) {
					return;
				}

				this.end();

				let damage = new Damage(
					this.creature, // Attacker
					this.damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				target.takeDamage(damage);

				// Keep highlighted in UI
				this.setUsed(false);
			},
		},

		//	Second Ability: Metal Hand
		{
			//	Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			_targetTeam: Team.Enemy,

			//	require() :
			require: function () {
				if (!this.testRequirements()) {
					return false;
				}

				if (
					!this.atLeastOneTarget(this.creature.getHexMap(matrices.frontnback2hex), {
						team: this._targetTeam,
					})
				) {
					return false;
				}
				return true;
			},

			//	query() :
			query: function () {
				let ability = this;
				let crea = this.creature;

				G.grid.queryCreature({
					fnOnConfirm: function () {
						ability.animation(...arguments);
					}, // fnOnConfirm
					team: this._targetTeam,
					id: crea.id,
					flipped: crea.player.flipped,
					hexes: crea.getHexMap(matrices.frontnback2hex),
				});
			},

			//	activate() :
			activate: function (target) {
				let ability = this;
				ability.end();
				G.Phaser.camera.shake(0.01, 150, true, G.Phaser.camera.SHAKE_HORIZONTAL, true);

				let damage = new Damage(
					ability.creature, // Attacker
					ability.damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				target.takeDamage(damage);

				// If upgrade, also steal up to 8 energy
				if (this.isUpgraded()) {
					let energySteal = Math.min(8, target.energy);
					target.energy -= energySteal;
					this.creature.recharge(energySteal);
					G.log(
						'%CreatureName' +
							this.creature.id +
							'% steals ' +
							energySteal +
							' energy from %CreatureName' +
							target.id +
							'%',
					);
				}
			},
		},

		// 	Third Ability: Rocket Launcher
		{
			//	Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			require: function () {
				// Recalculate energy requirements/costs based on whether this is ugpraded
				if (this.isUpgraded()) {
					this.requirements = {
						energy: 30,
					};
					this.costs = {
						energy: 30,
					};
				} else {
					this.requirements = {
						energy: 40,
					};
					this.costs = {
						energy: 40,
					};
				}
				return this.testRequirements();
			},

			// 	query() :
			query: function () {
				let ability = this;
				let crea = this.creature;

				let straitrow = matrices.straitrow;
				let bellowrow = matrices.bellowrow;

				let choices = [
					//Front
					arrayUtils
						.filterCreature(
							G.grid.getHexMap(crea.x, crea.y - 2, 0, false, bellowrow),
							true,
							true,
							crea.id,
						)
						.concat(
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x, crea.y, 0, false, straitrow),
								true,
								true,
								crea.id,
							),
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x, crea.y, 0, false, bellowrow),
								true,
								true,
								crea.id,
							),
						),
					//Behind
					arrayUtils
						.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y - 2, 0, true, bellowrow),
							true,
							true,
							crea.id,
						)
						.concat(
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x - 1, crea.y, 0, true, straitrow),
								true,
								true,
								crea.id,
							),
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x - 1, crea.y, 0, true, bellowrow),
								true,
								true,
								crea.id,
							),
						),
				];

				choices[0].choiceId = 0;
				choices[1].choiceId = 1;

				G.grid.queryChoice({
					fnOnCancel: function () {
						G.activeCreature.queryMove();
					},
					fnOnConfirm: function () {
						ability.animation(...arguments);
					},
					team: Team.Both,
					id: crea.id,
					requireCreature: false,
					choices: choices,
				});
			},

			//	activate() :
			activate: function (choice) {
				let ability = this;
				ability.end();
				G.Phaser.camera.shake(0.02, 350, true, G.Phaser.camera.SHAKE_HORIZONTAL, true);

				let crea = this.creature;

				let straitrow = matrices.straitrow;
				let bellowrow = matrices.bellowrow;

				let rows;
				if (choice.choiceId === 0) {
					// Front
					rows = [
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x, crea.y - 2, 0, false, bellowrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x, crea.y, 0, false, straitrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x, crea.y, 0, false, bellowrow),
							true,
							true,
							crea.id,
						),
					];
				} else {
					// Back
					rows = [
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y - 2, 0, true, bellowrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y, 0, true, straitrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y, 0, true, bellowrow),
							true,
							true,
							crea.id,
						),
					];
				}

				for (let i = 0; i < rows.length; i++) {
					if (rows[i].length === 0 || !(rows[i][rows[i].length - 1].creature instanceof Creature)) {
						//Miss
						this.token += 1;
						continue;
					}

					let target = rows[i][rows[i].length - 1].creature;

					let damage = new Damage(
						ability.creature, //Attacker
						ability.damages, //Damage Type
						1, //Area
						[], //Effects
						G,
					);
					target.takeDamage(damage);
				}

				if (this.token > 0) {
					G.log('%CreatureName' + this.creature.id + '% missed ' + this.token + ' rocket(s)');
				}
			},
		},

		// 	Fourth Ability: Target Locking
		{
			//	Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			// 	require() :
			require: function () {
				if (!this.testRequirements()) {
					return false;
				}

				if (this.creature.abilities[2].token === 0) {
					this.message = 'No rocket launched.';
					return false;
				}

				return true;
			},

			// 	query() :
			query: function () {
				let ability = this;
				let crea = this.creature;

				let hexes = G.grid.allhexes.slice(0); // Copy array

				G.grid.queryCreature({
					fnOnConfirm: function () {
						ability.animation(...arguments);
					}, // fnOnConfirm
					team: Team.Enemy,
					id: crea.id,
					flipped: crea.player.flipped,
					hexes: hexes,
				});
			},

			//	activate() :
			activate: function (crea) {
				let ability = this;
				ability.end();
				G.Phaser.camera.shake(0.03, 333, true, G.Phaser.camera.SHAKE_VERTICAL, true);

				let target = crea;

				// Use all rockets if upgraded, or up to 2 if not
				let rocketLauncherAbility = this.creature.abilities[2];
				let rocketsToUse = rocketLauncherAbility.token;
				if (!this.isUpgraded()) {
					rocketsToUse = Math.min(rocketsToUse, 2);
				}
				rocketLauncherAbility.token -= rocketsToUse;

				// Multiply damage by number of rockets
				let damages = $j.extend({}, rocketLauncherAbility.damages);
				for (let key in damages) {
					if ({}.hasOwnProperty.call(damages, key)) {
						damages[key] *= rocketsToUse;
					}
				}

				G.log('%CreatureName' + this.creature.id + '% redirects ' + rocketsToUse + ' rocket(s)');
				let damage = new Damage(
					ability.creature, // Attacker
					damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				target.takeDamage(damage);
			},
		},
	];
};
<commit_msg>added Bad Doggie camera shake effect<commit_after>import * as $j from 'jquery';
import { Damage } from '../damage';
import { Team, isTeam } from '../utility/team';
import * as matrices from '../utility/matrices';
import * as arrayUtils from '../utility/arrayUtils';
import { Creature } from '../creature';

/** Creates the abilities
 * @param {Object} G the game object
 * @return {void}
 */
export default (G) => {
	G.abilities[31] = [
		//	First Ability: Bad Doggie
		{
			triggerFunc: function () {
				// When upgraded, trigger both at start and end of turn
				// Otherwise just trigger at start
				if (this.isUpgraded()) {
					return 'onStartPhase onEndPhase';
				}
				return 'onStartPhase';
			},

			require: function () {
				// Check requirements in activate() so the ability is always highlighted
				return this.testRequirements();
			},

			activate: function () {
				// Check if there's an enemy creature in front
				let hexesInFront = this.creature.getHexMap(matrices.inlinefront2hex);
				if (hexesInFront.length < 1) {
					return;
				}
				let target = hexesInFront[0].creature;
				if (!target) {
					return;
				}
				if (!isTeam(this.creature, target, Team.Enemy)) {
					return;
				}

				this.end();

				let damage = new Damage(
					this.creature, // Attacker
					this.damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				target.takeDamage(damage);
				G.Phaser.camera.shake(0.01, 123, true, G.Phaser.camera.SHAKE_HORIZONTAL, true);

				// Keep highlighted in UI
				this.setUsed(false);
			},
		},

		//	Second Ability: Metal Hand
		{
			//	Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			_targetTeam: Team.Enemy,

			//	require() :
			require: function () {
				if (!this.testRequirements()) {
					return false;
				}

				if (
					!this.atLeastOneTarget(this.creature.getHexMap(matrices.frontnback2hex), {
						team: this._targetTeam,
					})
				) {
					return false;
				}
				return true;
			},

			//	query() :
			query: function () {
				let ability = this;
				let crea = this.creature;

				G.grid.queryCreature({
					fnOnConfirm: function () {
						ability.animation(...arguments);
					}, // fnOnConfirm
					team: this._targetTeam,
					id: crea.id,
					flipped: crea.player.flipped,
					hexes: crea.getHexMap(matrices.frontnback2hex),
				});
			},

			//	activate() :
			activate: function (target) {
				let ability = this;
				ability.end();
				G.Phaser.camera.shake(0.01, 150, true, G.Phaser.camera.SHAKE_HORIZONTAL, true);

				let damage = new Damage(
					ability.creature, // Attacker
					ability.damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				target.takeDamage(damage);

				// If upgrade, also steal up to 8 energy
				if (this.isUpgraded()) {
					let energySteal = Math.min(8, target.energy);
					target.energy -= energySteal;
					this.creature.recharge(energySteal);
					G.log(
						'%CreatureName' +
							this.creature.id +
							'% steals ' +
							energySteal +
							' energy from %CreatureName' +
							target.id +
							'%',
					);
				}
			},
		},

		// 	Third Ability: Rocket Launcher
		{
			//	Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			require: function () {
				// Recalculate energy requirements/costs based on whether this is ugpraded
				if (this.isUpgraded()) {
					this.requirements = {
						energy: 30,
					};
					this.costs = {
						energy: 30,
					};
				} else {
					this.requirements = {
						energy: 40,
					};
					this.costs = {
						energy: 40,
					};
				}
				return this.testRequirements();
			},

			// 	query() :
			query: function () {
				let ability = this;
				let crea = this.creature;

				let straitrow = matrices.straitrow;
				let bellowrow = matrices.bellowrow;

				let choices = [
					//Front
					arrayUtils
						.filterCreature(
							G.grid.getHexMap(crea.x, crea.y - 2, 0, false, bellowrow),
							true,
							true,
							crea.id,
						)
						.concat(
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x, crea.y, 0, false, straitrow),
								true,
								true,
								crea.id,
							),
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x, crea.y, 0, false, bellowrow),
								true,
								true,
								crea.id,
							),
						),
					//Behind
					arrayUtils
						.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y - 2, 0, true, bellowrow),
							true,
							true,
							crea.id,
						)
						.concat(
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x - 1, crea.y, 0, true, straitrow),
								true,
								true,
								crea.id,
							),
							arrayUtils.filterCreature(
								G.grid.getHexMap(crea.x - 1, crea.y, 0, true, bellowrow),
								true,
								true,
								crea.id,
							),
						),
				];

				choices[0].choiceId = 0;
				choices[1].choiceId = 1;

				G.grid.queryChoice({
					fnOnCancel: function () {
						G.activeCreature.queryMove();
					},
					fnOnConfirm: function () {
						ability.animation(...arguments);
					},
					team: Team.Both,
					id: crea.id,
					requireCreature: false,
					choices: choices,
				});
			},

			//	activate() :
			activate: function (choice) {
				let ability = this;
				ability.end();
				G.Phaser.camera.shake(0.02, 350, true, G.Phaser.camera.SHAKE_HORIZONTAL, true);

				let crea = this.creature;

				let straitrow = matrices.straitrow;
				let bellowrow = matrices.bellowrow;

				let rows;
				if (choice.choiceId === 0) {
					// Front
					rows = [
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x, crea.y - 2, 0, false, bellowrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x, crea.y, 0, false, straitrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x, crea.y, 0, false, bellowrow),
							true,
							true,
							crea.id,
						),
					];
				} else {
					// Back
					rows = [
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y - 2, 0, true, bellowrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y, 0, true, straitrow),
							true,
							true,
							crea.id,
						),
						arrayUtils.filterCreature(
							G.grid.getHexMap(crea.x - 1, crea.y, 0, true, bellowrow),
							true,
							true,
							crea.id,
						),
					];
				}

				for (let i = 0; i < rows.length; i++) {
					if (rows[i].length === 0 || !(rows[i][rows[i].length - 1].creature instanceof Creature)) {
						//Miss
						this.token += 1;
						continue;
					}

					let target = rows[i][rows[i].length - 1].creature;

					let damage = new Damage(
						ability.creature, //Attacker
						ability.damages, //Damage Type
						1, //Area
						[], //Effects
						G,
					);
					target.takeDamage(damage);
				}

				if (this.token > 0) {
					G.log('%CreatureName' + this.creature.id + '% missed ' + this.token + ' rocket(s)');
				}
			},
		},

		// 	Fourth Ability: Target Locking
		{
			//	Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			// 	require() :
			require: function () {
				if (!this.testRequirements()) {
					return false;
				}

				if (this.creature.abilities[2].token === 0) {
					this.message = 'No rocket launched.';
					return false;
				}

				return true;
			},

			// 	query() :
			query: function () {
				let ability = this;
				let crea = this.creature;

				let hexes = G.grid.allhexes.slice(0); // Copy array

				G.grid.queryCreature({
					fnOnConfirm: function () {
						ability.animation(...arguments);
					}, // fnOnConfirm
					team: Team.Enemy,
					id: crea.id,
					flipped: crea.player.flipped,
					hexes: hexes,
				});
			},

			//	activate() :
			activate: function (crea) {
				let ability = this;
				ability.end();
				G.Phaser.camera.shake(0.03, 333, true, G.Phaser.camera.SHAKE_VERTICAL, true);

				let target = crea;

				// Use all rockets if upgraded, or up to 2 if not
				let rocketLauncherAbility = this.creature.abilities[2];
				let rocketsToUse = rocketLauncherAbility.token;
				if (!this.isUpgraded()) {
					rocketsToUse = Math.min(rocketsToUse, 2);
				}
				rocketLauncherAbility.token -= rocketsToUse;

				// Multiply damage by number of rockets
				let damages = $j.extend({}, rocketLauncherAbility.damages);
				for (let key in damages) {
					if ({}.hasOwnProperty.call(damages, key)) {
						damages[key] *= rocketsToUse;
					}
				}

				G.log('%CreatureName' + this.creature.id + '% redirects ' + rocketsToUse + ' rocket(s)');
				let damage = new Damage(
					ability.creature, // Attacker
					damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				target.takeDamage(damage);
			},
		},
	];
};
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');

module.exports = {
  loadPriority:  0,
  initialize: function(api, next){

    if(!api.utils){ api.utils = {}; }

    ////////////////////////////////////////////////////////////////////////////
    // merge two hashes recursively
    api.utils.hashMerge = function(a, b, arg){
      var c = {};
      var i;
      var response;

      for(i in a){
        if(api.utils.isPlainObject(a[i]) && Object.keys(a[i]).length > 0){
          c[i] = api.utils.hashMerge(c[i], a[i], arg);
        }else{
          if(typeof a[i] === 'function'){
            response = a[i](arg);
            if(api.utils.isPlainObject(response)){
              c[i] = api.utils.hashMerge(c[i], response, arg);
            }else{
              c[i] = response;
            }
          }else{
            c[i] = a[i];
          }
        }
      }
      for(i in b){
        if(api.utils.isPlainObject(b[i]) && Object.keys(b[i]).length > 0){
          c[i] = api.utils.hashMerge(c[i], b[i], arg);
        }else{
          if(typeof b[i] === 'function'){
            response = b[i](arg);
            if(api.utils.isPlainObject(response)){
              c[i] = api.utils.hashMerge(c[i], response, arg);
            }else{
              c[i] = response;
            }
          }else{
            c[i] = b[i];
          }
        }
      }
      return c;
    };

    api.utils.isPlainObject = function(o){
      var safeTypes     = [Boolean, Number, String, Function, Array, Date, RegExp, Buffer];
      var safeInstances = ['boolean', 'number', 'string', 'function'];
      var expandPreventMatchKey = '_toExpand'; // set `_toExpand = false` within an object if you don't want to expand it
      var i;

      if(!o){ return false; }
      if((o instanceof Object) === false){ return false; }
      for(i in safeTypes){
        if(o instanceof safeTypes[i]){ return false; }
      }
      for(i in safeInstances){
        if(typeof o === safeInstances[i]){ return false; }
      }
      if(o[expandPreventMatchKey] === false){ return false; }
      return (o.toString() === '[object Object]');
    };

    ////////////////////////////////////////////////////////////////////////////
    // string to hash
    // http://stackoverflow.com/questions/6393943/convert-javascript-string-in-dot-notation-into-an-object-reference
    api.utils.stringToHash = function(path, object){
      if(!object){ object = api; }
      function _index(obj, i){ return obj[i]; }
      return path.split('.').reduce(_index, object);
    };

    ////////////////////////////////////////////////////////////////////////////
    // unique-ify an array
    api.utils.arrayUniqueify = function(arr){
      var a = [];
      for(var i = 0; i < arr.length; i++){
        for(var j = i + 1; j < arr.length; j++){
          if(arr[i] === arr[j]){ j = ++i; }
        }
        a.push(arr[i]);
      }
      return a;
    };

    ////////////////////////////////////////////////////////////////////////////
    // get all .js files in a directory
    api.utils.recursiveDirectoryGlob = function(dir, extension, followLinkFiles){
      var results = [];

      if(!extension){ extension = '.js'; }
      if(!followLinkFiles){ followLinkFiles = true; }

      extension = extension.replace('.', '');
      if(dir[dir.length - 1] !== path.sep){ dir += path.sep; }

      if(fs.existsSync(dir)){
        fs.readdirSync(dir).forEach(function(file){
          var fullFilePath = path.normalize(dir + file);
          if(file[0] !== '.'){ // ignore 'system' files
            var stats = fs.statSync(fullFilePath);
            var child;
            if(stats.isDirectory()){
              child = api.utils.recursiveDirectoryGlob(fullFilePath, extension, followLinkFiles);
              child.forEach(function(c){ results.push(c); });
            }else if(stats.isSymbolicLink()){
              var realPath = fs.readlinkSync(fullFilePath);
              child = api.utils.recursiveDirectoryGlob(realPath, extension, followLinkFiles);
              child.forEach(function(c){ results.push(c); });
            }else if(stats.isFile()){
              var fileParts = file.split('.');
              var ext = fileParts[(fileParts.length - 1)];
              // real file match
              if(ext === extension){ results.push(fullFilePath); }
              // linkfile traversal
              if(ext === 'link' && followLinkFiles === true){
                var linkedPath = api.utils.sourceRelativeLinkPath(fullFilePath, api.config.general.paths.plugin);
                if(linkedPath){
                  child = api.utils.recursiveDirectoryGlob(linkedPath, extension, followLinkFiles);
                  child.forEach(function(c){ results.push(c); });
                }else{
                  try{
                    api.log(['cannot find linked refrence to `%s`', file], 'warning');
                  }catch(e){
                    throw('cannot find linked refrence to' + file);
                  }
                }
              }
            }
          }
        });
      }

      return results.sort();
    };

    api.utils.sourceRelativeLinkPath = function(linkfile, pluginPaths){
      var type = fs.readFileSync(linkfile).toString();
      var pathParts = linkfile.split(path.sep);
      var name = pathParts[(pathParts.length - 1)].split('.')[0];
      var pathsToTry = pluginPaths.slice(0);
      var pluginRoot;

      // TODO: always also try the local destination's `node_modules` to allow for nested plugins
      // This might be a security risk without requiring explicit sourcing

      pathsToTry.forEach(function(pluginPath){
        var pluginPathAttempt = path.normalize(pluginPath + path.sep + name);
        try{
          var stats = fs.lstatSync(pluginPathAttempt);
          if(!pluginRoot && stats.isDirectory()){ pluginRoot = pluginPathAttempt; }
        }catch(e){ }
      });

      if(!pluginRoot){ return false; }
      var pluginSection = path.normalize(pluginRoot + path.sep + type);
      return pluginSection;
    };

    ////////////////////////////////////////////////////////////////////////////
    // object Clone
    api.utils.objClone = function(obj){
      return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyNames(obj).reduce(function(memo, name){
        return (memo[name] = Object.getOwnPropertyDescriptor(obj, name)) && memo;
      }, {}));
    };

    ////////////////////////////////////////////////////////////////////////////
    // attempt to collapse this object to an array; ie: {"0": "a", "1": "b"}
    api.utils.collapseObjectToArray = function(obj){
      try{
        var keys = Object.keys(obj);
        if(keys.length < 1){ return false; }
        if(keys[0] !== '0'){ return false; }
        if(keys[(keys.length - 1)] !== String(keys.length - 1)){ return false; }

        var arr = [];
        for(var i in keys){
          var key = keys[i];
          if(String(parseInt(key)) !== key){ return false; }
          else{ arr.push(obj[key]); }
        }

        return arr;
      }catch(e){
        return false;
      }
    };

    ////////////////////////////////////////////////////////////////////////////
    // get this servers external interface
    api.utils.getExternalIPAddress = function(){
      var os = require('os');
      var ifaces = os.networkInterfaces();
      var ip = false;
      for(var dev in ifaces){
        ifaces[dev].forEach(function(details){
          if(details.family === 'IPv4' && details.address !== '127.0.0.1'){
            ip = details.address;
          }
        });
      }
      return ip;
    };

    ////////////////////////////////////////////////////////////////////////////
    // cookie parse from headers of http(s) requests
    api.utils.parseCookies = function(req){
      var cookies = {};
      if(req.headers.cookie){
        req.headers.cookie.split(';').forEach(function(cookie){
          var parts = cookie.split('=');
          cookies[parts[0].trim()] = (parts[1] || '').trim();
        });
      }
      return cookies;
    };

    ////////////////////////////////////////////////////////////////////////////
    // parse an IPv6 address
    // https://github.com/evantahler/actionhero/issues/275 && https://github.com/nullivex
    api.utils.parseIPv6URI = function(addr){
      var host = '::1';
      var port = '80';
      var regexp = new RegExp(/\[([0-9a-f:]+)\]:([0-9]{1,5})/);
      //if we have brackets parse them and find a port
      if(addr.indexOf('[') > -1 && addr.indexOf(']') > -1){
        var res = regexp.exec(addr);
        if(res === null){
          throw new Error('failed to parse address');
        }
        host = res[1];
        port = res[2];
      }else{
        host = addr;
      }
      return {host: host, port: parseInt(port, 10)};
    };

    next();
  }
};
<commit_msg>Electron support, avoid bug electron/electron#5454 - remove trailing path separator from recursive directory glob - use path.join(dir, file) instead of path.normalize(dir + file)<commit_after>'use strict';

var fs = require('fs');
var path = require('path');

module.exports = {
  loadPriority:  0,
  initialize: function(api, next){

    if(!api.utils){ api.utils = {}; }

    ////////////////////////////////////////////////////////////////////////////
    // merge two hashes recursively
    api.utils.hashMerge = function(a, b, arg){
      var c = {};
      var i;
      var response;

      for(i in a){
        if(api.utils.isPlainObject(a[i]) && Object.keys(a[i]).length > 0){
          c[i] = api.utils.hashMerge(c[i], a[i], arg);
        }else{
          if(typeof a[i] === 'function'){
            response = a[i](arg);
            if(api.utils.isPlainObject(response)){
              c[i] = api.utils.hashMerge(c[i], response, arg);
            }else{
              c[i] = response;
            }
          }else{
            c[i] = a[i];
          }
        }
      }
      for(i in b){
        if(api.utils.isPlainObject(b[i]) && Object.keys(b[i]).length > 0){
          c[i] = api.utils.hashMerge(c[i], b[i], arg);
        }else{
          if(typeof b[i] === 'function'){
            response = b[i](arg);
            if(api.utils.isPlainObject(response)){
              c[i] = api.utils.hashMerge(c[i], response, arg);
            }else{
              c[i] = response;
            }
          }else{
            c[i] = b[i];
          }
        }
      }
      return c;
    };

    api.utils.isPlainObject = function(o){
      var safeTypes     = [Boolean, Number, String, Function, Array, Date, RegExp, Buffer];
      var safeInstances = ['boolean', 'number', 'string', 'function'];
      var expandPreventMatchKey = '_toExpand'; // set `_toExpand = false` within an object if you don't want to expand it
      var i;

      if(!o){ return false; }
      if((o instanceof Object) === false){ return false; }
      for(i in safeTypes){
        if(o instanceof safeTypes[i]){ return false; }
      }
      for(i in safeInstances){
        if(typeof o === safeInstances[i]){ return false; }
      }
      if(o[expandPreventMatchKey] === false){ return false; }
      return (o.toString() === '[object Object]');
    };

    ////////////////////////////////////////////////////////////////////////////
    // string to hash
    // http://stackoverflow.com/questions/6393943/convert-javascript-string-in-dot-notation-into-an-object-reference
    api.utils.stringToHash = function(path, object){
      if(!object){ object = api; }
      function _index(obj, i){ return obj[i]; }
      return path.split('.').reduce(_index, object);
    };

    ////////////////////////////////////////////////////////////////////////////
    // unique-ify an array
    api.utils.arrayUniqueify = function(arr){
      var a = [];
      for(var i = 0; i < arr.length; i++){
        for(var j = i + 1; j < arr.length; j++){
          if(arr[i] === arr[j]){ j = ++i; }
        }
        a.push(arr[i]);
      }
      return a;
    };

    ////////////////////////////////////////////////////////////////////////////
    // get all .js files in a directory
    api.utils.recursiveDirectoryGlob = function(dir, extension, followLinkFiles){
      var results = [];

      if(!extension){ extension = '.js'; }
      if(!followLinkFiles){ followLinkFiles = true; }

      extension = extension.replace('.', '');

      if(fs.existsSync(dir)){
        fs.readdirSync(dir).forEach(function(file){
          var fullFilePath = path.join(dir, file);
          if(file[0] !== '.'){ // ignore 'system' files
            var stats = fs.statSync(fullFilePath);
            var child;
            if(stats.isDirectory()){
              child = api.utils.recursiveDirectoryGlob(fullFilePath, extension, followLinkFiles);
              child.forEach(function(c){ results.push(c); });
            }else if(stats.isSymbolicLink()){
              var realPath = fs.readlinkSync(fullFilePath);
              child = api.utils.recursiveDirectoryGlob(realPath, extension, followLinkFiles);
              child.forEach(function(c){ results.push(c); });
            }else if(stats.isFile()){
              var fileParts = file.split('.');
              var ext = fileParts[(fileParts.length - 1)];
              // real file match
              if(ext === extension){ results.push(fullFilePath); }
              // linkfile traversal
              if(ext === 'link' && followLinkFiles === true){
                var linkedPath = api.utils.sourceRelativeLinkPath(fullFilePath, api.config.general.paths.plugin);
                if(linkedPath){
                  child = api.utils.recursiveDirectoryGlob(linkedPath, extension, followLinkFiles);
                  child.forEach(function(c){ results.push(c); });
                }else{
                  try{
                    api.log(['cannot find linked refrence to `%s`', file], 'warning');
                  }catch(e){
                    throw('cannot find linked refrence to' + file);
                  }
                }
              }
            }
          }
        });
      }

      return results.sort();
    };

    api.utils.sourceRelativeLinkPath = function(linkfile, pluginPaths){
      var type = fs.readFileSync(linkfile).toString();
      var pathParts = linkfile.split(path.sep);
      var name = pathParts[(pathParts.length - 1)].split('.')[0];
      var pathsToTry = pluginPaths.slice(0);
      var pluginRoot;

      // TODO: always also try the local destination's `node_modules` to allow for nested plugins
      // This might be a security risk without requiring explicit sourcing

      pathsToTry.forEach(function(pluginPath){
        var pluginPathAttempt = path.normalize(pluginPath + path.sep + name);
        try{
          var stats = fs.lstatSync(pluginPathAttempt);
          if(!pluginRoot && stats.isDirectory()){ pluginRoot = pluginPathAttempt; }
        }catch(e){ }
      });

      if(!pluginRoot){ return false; }
      var pluginSection = path.normalize(pluginRoot + path.sep + type);
      return pluginSection;
    };

    ////////////////////////////////////////////////////////////////////////////
    // object Clone
    api.utils.objClone = function(obj){
      return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyNames(obj).reduce(function(memo, name){
        return (memo[name] = Object.getOwnPropertyDescriptor(obj, name)) && memo;
      }, {}));
    };

    ////////////////////////////////////////////////////////////////////////////
    // attempt to collapse this object to an array; ie: {"0": "a", "1": "b"}
    api.utils.collapseObjectToArray = function(obj){
      try{
        var keys = Object.keys(obj);
        if(keys.length < 1){ return false; }
        if(keys[0] !== '0'){ return false; }
        if(keys[(keys.length - 1)] !== String(keys.length - 1)){ return false; }

        var arr = [];
        for(var i in keys){
          var key = keys[i];
          if(String(parseInt(key)) !== key){ return false; }
          else{ arr.push(obj[key]); }
        }

        return arr;
      }catch(e){
        return false;
      }
    };

    ////////////////////////////////////////////////////////////////////////////
    // get this servers external interface
    api.utils.getExternalIPAddress = function(){
      var os = require('os');
      var ifaces = os.networkInterfaces();
      var ip = false;
      for(var dev in ifaces){
        ifaces[dev].forEach(function(details){
          if(details.family === 'IPv4' && details.address !== '127.0.0.1'){
            ip = details.address;
          }
        });
      }
      return ip;
    };

    ////////////////////////////////////////////////////////////////////////////
    // cookie parse from headers of http(s) requests
    api.utils.parseCookies = function(req){
      var cookies = {};
      if(req.headers.cookie){
        req.headers.cookie.split(';').forEach(function(cookie){
          var parts = cookie.split('=');
          cookies[parts[0].trim()] = (parts[1] || '').trim();
        });
      }
      return cookies;
    };

    ////////////////////////////////////////////////////////////////////////////
    // parse an IPv6 address
    // https://github.com/evantahler/actionhero/issues/275 && https://github.com/nullivex
    api.utils.parseIPv6URI = function(addr){
      var host = '::1';
      var port = '80';
      var regexp = new RegExp(/\[([0-9a-f:]+)\]:([0-9]{1,5})/);
      //if we have brackets parse them and find a port
      if(addr.indexOf('[') > -1 && addr.indexOf(']') > -1){
        var res = regexp.exec(addr);
        if(res === null){
          throw new Error('failed to parse address');
        }
        host = res[1];
        port = res[2];
      }else{
        host = addr;
      }
      return {host: host, port: parseInt(port, 10)};
    };

    next();
  }
};
<|endoftext|>


<commit_before>/*globals console sparks $ breadModel getBreadBoard */

(function() {

  sparks.SparksQuestionView = function(question){
    this.question = question;
    this.$view = null;
  };

  sparks.SparksQuestionView.prototype = {

    getView: function() {
      var question = this.question;

      var $question = $("
").addClass("question");

      if (!!question.image){
        var $div = $("
").addClass("question-image");
        $div.append(
          $("").attr('src', this._getImgSrc(question.image))
        );
        $question.append($div);
      }

      var prompt = question.isSubQuestion ? question.prompt : (question.shownId+1) + ".  " + question.prompt;

      $question.append(
        $("").addClass("prompt").html(prompt), "   "
      );

      var self = this;

      if (!question.options){
        var $input = $("<input>").attr("id",question.id+"_input");
        $question.append($input);
        $input.change(function(args){
          self.valueChanged(args);
        });
      } else {
        if (!!question.checkbox || !!question.radio){
          $.each(question.options, function(i,answer_option){
            if (!answer_option.option){
              // answer_option = sparks.mathParser.calculateMeasurement(answer_option);
            } else {
              // answer_option = sparks.mathParser.calculateMeasurement(answer_option.option);
              answer_option = answer_option.option;
            }

            var type = question.checkbox ? "checkbox" : "radio";

            var groupName = type + "Group" + question.id;

            $question.append($("
"));
            var $input = $("<input>").attr("type", type).attr("name", groupName).attr("value", answer_option);
            $question.append($input);
            $question.append(" " + answer_option + "");

            $input.change(function(args){
              self.valueChanged(args);
            });
          });
          $question.append('       ');
        } else {
          var $select = $("<select>").attr("id",question.id+"_options");

          $select.append($("<option>").attr("value", "").html("").attr("defaultSelected",true));

          console.log("question.keepOrder = "+question.keepOrder);
          if (!question.keepOrder){
            question.options = sparks.util.shuffle(question.options);
          }

          $.each(question.options, function(i,answer_option){
            if (!answer_option.option){
              answer_option = sparks.mathParser.calculateMeasurement(answer_option);
            } else {
              answer_option = sparks.mathParser.calculateMeasurement(answer_option.option);
            }
            $select.append($("<option>").attr("value", answer_option).html(answer_option).attr("defaultSelected",false));
          });
          $question.append($select, "   ");
          $select.change(function(args){
            self.valueChanged(args);
          });
        }
      }

      if (!!question.correct_units){
         var $unitsSelect = $("<select>").attr("id", question.id+"_units");
         var options = ["Units...","µV","mV","V","Ω","kΩ","MΩ","µA","mA","A"];
         $.each(options, function(i, val){
           $unitsSelect.append($("<option>").html(val).attr("defaultSelected", i===0));
         });
         $question.append($unitsSelect, "   ");
      }

      return $question;
    },

    _getImgSrc: function(fileName) {
      if (fileName.indexOf("http") > -1){
        return fileName;
      } else if (!!this.jsonActivity.images_url) {
        return this.jsonActivity.images_url + "/" + fileName;
      }
      console.log(fileName + " appears to be a relative filename, but there is no base activity url.");
      return "";
    },

    valueChanged: function(args) {
      var value = $(args.target).val();
      this.question.answer = value;
    }

  };

})();
  <commit_msg>Shuffle questions correctly, even if radio or checkbox<commit_after>/*globals console sparks $ breadModel getBreadBoard */

(function() {

  sparks.SparksQuestionView = function(question){
    this.question = question;
    this.$view = null;
  };

  sparks.SparksQuestionView.prototype = {

    getView: function() {
      var question = this.question;

      var $question = $("
").addClass("question");

      if (!!question.image){
        var $div = $("
").addClass("question-image");
        $div.append(
          $("").attr('src', this._getImgSrc(question.image))
        );
        $question.append($div);
      }

      var prompt = question.isSubQuestion ? question.prompt : (question.shownId+1) + ".  " + question.prompt;

      $question.append(
        $("").addClass("prompt").html(prompt), "   "
      );

      var self = this;

      if (!question.options){
        var $input = $("<input>").attr("id",question.id+"_input");
        $question.append($input);
        $input.change(function(args){
          self.valueChanged(args);
        });
      } else {

        if (!question.keepOrder){
          question.options = sparks.util.shuffle(question.options);
        }

        if (!!question.checkbox || !!question.radio){
          $.each(question.options, function(i,answer_option){
            if (!answer_option.option){
              // answer_option = sparks.mathParser.calculateMeasurement(answer_option);
            } else {
              // answer_option = sparks.mathParser.calculateMeasurement(answer_option.option);
              answer_option = answer_option.option;
            }

            var type = question.checkbox ? "checkbox" : "radio";

            var groupName = type + "Group" + question.id;

            $question.append($("
"));
            var $input = $("<input>").attr("type", type).attr("name", groupName).attr("value", answer_option);
            $question.append($input);
            $question.append(" " + answer_option + "");

            $input.change(function(args){
              self.valueChanged(args);
            });
          });
          $question.append('       ');
        } else {
          var $select = $("<select>").attr("id",question.id+"_options");

          $select.append($("<option>").attr("value", "").html("").attr("defaultSelected",true));

          $.each(question.options, function(i,answer_option){
            if (!answer_option.option){
              answer_option = sparks.mathParser.calculateMeasurement(answer_option);
            } else {
              answer_option = sparks.mathParser.calculateMeasurement(answer_option.option);
            }
            $select.append($("<option>").attr("value", answer_option).html(answer_option).attr("defaultSelected",false));
          });
          $question.append($select, "   ");
          $select.change(function(args){
            self.valueChanged(args);
          });
        }
      }

      if (!!question.correct_units){
         var $unitsSelect = $("<select>").attr("id", question.id+"_units");
         var options = ["Units...","µV","mV","V","Ω","kΩ","MΩ","µA","mA","A"];
         $.each(options, function(i, val){
           $unitsSelect.append($("<option>").html(val).attr("defaultSelected", i===0));
         });
         $question.append($unitsSelect, "   ");
      }

      return $question;
    },

    _getImgSrc: function(fileName) {
      if (fileName.indexOf("http") > -1){
        return fileName;
      } else if (!!this.jsonActivity.images_url) {
        return this.jsonActivity.images_url + "/" + fileName;
      }
      console.log(fileName + " appears to be a relative filename, but there is no base activity url.");
      return "";
    },

    valueChanged: function(args) {
      var value = $(args.target).val();
      this.question.answer = value;
    }

  };

})();
  <|endoftext|>


<commit_before>
// MODULES //

var // Expectation library:
	chai = require( 'chai' ),

	// Test utilities:
	utils = require( './utils' ),

	// Module to be tested:
	hStream = require( './../lib/stats/histc' );

// VARIABLES //

var expect = chai.expect,
	assert = chai.assert;

// TESTS //

describe( 'stats/histc', function tests() {
	'use strict';

	it( 'should export a factory function', function test() {
		expect( hStream ).to.be.a( 'function' );
	});

	it( 'should provide a method to get the edges', function test() {
		var rStream = hStream();

		expect( rStream.edges() ).to.be.an( 'array' );
	});

	it( 'should provide a method to set the edges', function test() {
		var rStream = hStream(),
			expected = [ 0, 1, 2, 3, 4, 5 ];
		rStream.edges( expected );
		assert.deepEqual( rStream.edges(), expected );
	});

	it( 'should not allow edges to be set as a non-array', function test() {
		var rStream = hStream();

		expect( badValue( '5' ) ).to.throw( Error );
		expect( badValue( 99 ) ).to.throw( Error );
		expect( badValue( {} ) ).to.throw( Error );
		expect( badValue( null ) ).to.throw( Error );
		expect( badValue( undefined ) ).to.throw( Error );
		expect( badValue( NaN ) ).to.throw( Error );

		function badValue( value ) {
			return function() {
				rStream.value( value );
			};
		}
	});

	it( 'should compute the histogram counts of piped data', function test( done ) {
		var data, expected, rStream,
			edges = [ -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5 ];

		// Simulate some data...
		data = [ 1, 1, 1, 2, 3, 3, 3, 3, 4 ];

		// Expected counts (histc also returns negative and positive infinity bins which include data which does not fall into any bin defined by the edge vector):
		expected = [
			[ Number.NEGATIVE_INFINITY, 0, -0.5 ], // -infinity
			[ -0.5, 0, 0.5 ],
			[ 0.5, 3, 1.5 ],
			[ 1.5, 1, 2.5 ],
			[ 2.5, 4, 3.5 ],
			[ 3.5, 1, 4.5 ],
			[ 4.5, 0, 5.5 ],
			[ 5.5, 0, Number.POSITIVE_INFINITY ] // +infinity
		];

		// Create a new histc stream:
		rStream = hStream()
			.edges( edges )
			.stream();

		// Mock reading from the stream:
		utils.readStream( rStream, onRead );

		// Mock piping a data to the stream:
		utils.writeStream( data, rStream );

		return;

		/**
		* FUNCTION: onRead( error, actual )
		*	Read event handler. Checks for errors and compares streamed data to expected data.
		*/
		function onRead( error, actual ) {
			expect( error ).to.not.exist;
			assert.deepEqual( actual[ 0 ], expected );
			done();
		} // end FUNCTION onRead()
	});

});<commit_msg>[UPDATE] add histc out-of-bounds test.<commit_after>
// MODULES //

var // Expectation library:
	chai = require( 'chai' ),

	// Test utilities:
	utils = require( './utils' ),

	// Module to be tested:
	hStream = require( './../lib/stats/histc' );

// VARIABLES //

var expect = chai.expect,
	assert = chai.assert;

// TESTS //

describe( 'stats/histc', function tests() {
	'use strict';

	it( 'should export a factory function', function test() {
		expect( hStream ).to.be.a( 'function' );
	});

	it( 'should provide a method to get the edges', function test() {
		var rStream = hStream();

		expect( rStream.edges() ).to.be.an( 'array' );
	});

	it( 'should provide a method to set the edges', function test() {
		var rStream = hStream(),
			expected = [ 0, 1, 2, 3, 4, 5 ];
		rStream.edges( expected );
		assert.deepEqual( rStream.edges(), expected );
	});

	it( 'should not allow edges to be set as a non-array', function test() {
		var rStream = hStream();

		expect( badValue( '5' ) ).to.throw( Error );
		expect( badValue( 99 ) ).to.throw( Error );
		expect( badValue( {} ) ).to.throw( Error );
		expect( badValue( null ) ).to.throw( Error );
		expect( badValue( undefined ) ).to.throw( Error );
		expect( badValue( NaN ) ).to.throw( Error );

		function badValue( value ) {
			return function() {
				rStream.value( value );
			};
		}
	});

	it( 'should compute the histogram counts of piped data', function test( done ) {
		var data, expected, rStream,
			edges = [ -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5 ];

		// Simulate some data...
		data = [ 1, 1, 1, 2, 3, 3, 3, 3, 4 ];

		// Expected counts (histc also returns negative and positive infinity bins which include data which does not fall into any bin defined by the edge vector):
		expected = [
			[ Number.NEGATIVE_INFINITY, 0, -0.5 ], // -infinity
			[ -0.5, 0, 0.5 ],
			[ 0.5, 3, 1.5 ],
			[ 1.5, 1, 2.5 ],
			[ 2.5, 4, 3.5 ],
			[ 3.5, 1, 4.5 ],
			[ 4.5, 0, 5.5 ],
			[ 5.5, 0, Number.POSITIVE_INFINITY ] // +infinity
		];

		// Create a new histc stream:
		rStream = hStream()
			.edges( edges )
			.stream();

		// Mock reading from the stream:
		utils.readStream( rStream, onRead );

		// Mock piping a data to the stream:
		utils.writeStream( data, rStream );

		return;

		/**
		* FUNCTION: onRead( error, actual )
		*	Read event handler. Checks for errors and compares streamed data to expected data.
		*/
		function onRead( error, actual ) {
			expect( error ).to.not.exist;
			assert.deepEqual( actual[ 0 ], expected );
			done();
		} // end FUNCTION onRead()
	});

	it( 'should, when computing the histogram counts of piped data, assign out-of-bounds data to appropriate bins', function test( done ) {
		var data, expected, rStream,
			edges = [ -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5 ];

		// Simulate some data...
		data = [ -1, -100, Number.NEGATIVE_INFINITY, 6, 100, Number.POSITIVE_INFINITY ];

		// Expected counts (histc also returns negative and positive infinity bins which include data which does not fall into any bin defined by the edge vector):
		expected = [
			[ Number.NEGATIVE_INFINITY, 3, -0.5 ], // -infinity
			[ -0.5, 0, 0.5 ],
			[ 0.5, 0, 1.5 ],
			[ 1.5, 0, 2.5 ],
			[ 2.5, 0, 3.5 ],
			[ 3.5, 0, 4.5 ],
			[ 4.5, 0, 5.5 ],
			[ 5.5, 3, Number.POSITIVE_INFINITY ] // +infinity
		];

		// Create a new histc stream:
		rStream = hStream()
			.edges( edges )
			.stream();

		// Mock reading from the stream:
		utils.readStream( rStream, onRead );

		// Mock piping a data to the stream:
		utils.writeStream( data, rStream );

		return;

		/**
		* FUNCTION: onRead( error, actual )
		*	Read event handler. Checks for errors and compares streamed data to expected data.
		*/
		function onRead( error, actual ) {
			expect( error ).to.not.exist;
			assert.deepEqual( actual[ 0 ], expected );
			done();
		} // end FUNCTION onRead()
	});

});<|endoftext|>


<commit_before>goog.provide('olcs.AbstractSynchronizer');

goog.require('ol.Observable');
goog.require('ol.layer.Group');
goog.require('ol.layer.Layer');

/**
 * @param {!ol.Map} map
 * @param {!Cesium.Scene} scene
 * @constructor
 * @template T
 * @api
 */
olcs.AbstractSynchronizer = function(map, scene) {
  /**
   * @type {!ol.Map}
   * @protected
   */
  this.map = map;

  /**
   * @type {ol.View}
   * @protected
   */
  this.view = null;

  /**
   * @type {!Cesium.Scene}
   * @protected
   */
  this.scene = scene;

  /**
   * @type {ol.Collection.<ol.layer.Base>}
   * @protected
   */
  this.olLayers = null;

  /**
   * @type {ol.layer.Group}
   */
  this.mapLayerGroup = null;

  /**
   * @type {!Array.<goog.events.Key>}
   * @private
   */
  this.olLayersListenKeys_ = [];

  /**
   * Map of ol3 layer ids (from goog.getUid) to the Cesium ImageryLayers.
   * null value means, that we are unable to create equivalent layer.
   * @type {Object.<number, ?T>}
   * @protected
   */
  this.layerMap = {};

  /**
   * Map of listen keys for ol3 layer groups ids (from goog.getUid).
   * @type {!Object.<number, !Array.<goog.events.Key>>}
   * @private
   */
  this.olGroupListenKeys_ = {};

  /**
   * @type {Object.<number, !Array.<goog.events.Key>>}
   * @private
   */
  this.unusedGroups_ = null;

  /**
   * @type {Object.<?T, number>}
   * @private
   */
  this.unusedCesiumObjects_ = null;

  this.map.on('change:view', function(e) {
    this.setView_(this.map.getView());
  }, this);
  this.setView_(this.map.getView());

  this.map.on('change:layergroup', function(e) {
    this.setLayerGroup_(this.map.getLayerGroup());
  }, this);
  this.setLayerGroup_(this.map.getLayerGroup());
};

/**
 * @param {ol.View} view New view to use.
 * @private
 */
olcs.AbstractSynchronizer.prototype.setView_ = function(view) {
  this.view = view;

  // destroy all, the change of view can affect which layers are synced
  this.synchronize();
};

/**
 * @param {ol.layer.Group} layerGroup New layers to use.
 * @private
 */
olcs.AbstractSynchronizer.prototype.setLayerGroup_ = function(layerGroup) {

  this.mapLayerGroup = layerGroup;
  var layers = layerGroup.getLayers();
  if (!goog.isNull(this.olLayers)) {
    goog.array.forEach(this.olLayersListenKeys_, ol.Observable.unByKey);
  }

  this.olLayers = layers;
  if (!goog.isNull(layers)) {
    var handleCollectionEvent_ = goog.bind(function(e) {
      this.synchronize_();
    }, this);

    this.olLayersListenKeys_ = [
      layers.on('add', handleCollectionEvent_),
      layers.on('remove', handleCollectionEvent_)
    ];
  } else {
    this.olLayersListenKeys_ = [];
  }

  this.synchronize();
};

/**
 * Remove all and perform complete synchronization of the layers.
 * @api
 */
olcs.AbstractSynchronizer.prototype.synchronize = function() {
  this.destroyAll();
  this.synchronize_();
};

/**
 * @param {ol.layer.Base} layer
 * @param {Array.<ol.layer.Layer>} foundLayers
 * @param {Array.<ol.layer.Group>} foundGroups
 * @private
 */
olcs.AbstractSynchronizer.flattenLayers_ =
    function(layer, foundLayers, foundGroups) {
  if (layer instanceof ol.layer.Group) {
    foundGroups.push(layer);
    var sublayers = layer.getLayers();
    if (goog.isDef(sublayers)) {
      sublayers.forEach(function(el) {
        olcs.AbstractSynchronizer.flattenLayers_(el, foundLayers, foundGroups);
      });
    }
  } else {
    foundLayers.push(layer);
  }
};

/**
 * Perform complete synchronization of the layers.
 * @private
 */
olcs.AbstractSynchronizer.prototype.synchronize_ = function() {
  if (goog.isNull(this.view) || goog.isNull(this.olLayers)) {
    return;
  }
  this.unusedGroups_ = goog.object.clone(this.olGroupListenKeys_);
  this.unusedCesiumObjects_ = goog.object.transpose(this.layerMap);
  this.removeAllCesiumObjects(false); // only remove, don't destroy

  var layers = [];
  var groups = [];
  olcs.AbstractSynchronizer.flattenLayers_(this.mapLayerGroup, layers, groups);

  layers.forEach(function(el) {
    this.synchronizeSingle(el);
  }, this);

  groups.forEach(function(el) {
    this.listenForGroupChanges_(el);
  }, this);
};

/**
 * Destroy single layer.
 * @param {ol.layer.Layer} layer
 * @private
 */
olcs.AbstractSynchronizer.prototype.removeAndDestroySingleLayer_ =
    function(layer) {
  var uid = goog.getUid(layer);
  var counterpart = this.layerMap[uid];
  if (!!counterpart) {
    this.removeSingleCesiumObject(counterpart, false);
    this.destroyCesiumObject(counterpart);
  }
  delete this.layerMap[uid];
};

/**
 * Destroy single group.
 * @param {ol.layer.Group} group
 * @private
 */
olcs.AbstractSynchronizer.prototype.unlistenSingleGroup_ =
    function(group) {
  if (group === this.mapLayerGroup) {
    return;
  }
  var uid = goog.getUid(group);
  var keys = this.olGroupListenKeys_[uid];
  keys = keys || []; // FIXME: why+
  keys.forEach(function(key) {
    ol.Observable.unByKey(key);
  });
  delete this.olGroupListenKeys_[uid];
};

/**
 * Remove layer.
 * @param {ol.layer.Base} removeRoot
 * @private
 */
olcs.AbstractSynchronizer.prototype.removeLayer_ = function(removeRoot) {
  if (!removeRoot) {
    return;
  }
  var layers = [];
  var groups = [];
  olcs.AbstractSynchronizer.flattenLayers_(removeRoot, layers, groups);

  layers.forEach(function(el) {
    this.removeAndDestroySingleLayer_(el);
  }, this);

  groups.forEach(function(el) {
    this.unlistenSingleGroup_(el);
  }, this);
};

/**
 * Synchronizes single layer.
 * @param {ol.layer.Group} group
 * @private
 */
olcs.AbstractSynchronizer.prototype.listenForGroupChanges_ = function(group) {
  var uuid = goog.getUid(group);

  if (!goog.isDef(this.olGroupListenKeys_[uuid])) {
    var listenKeyArray = [];
    this.olGroupListenKeys_[uuid] = listenKeyArray;

    // only the keys that need to be relistened when collection changes
    var contentKeys = [];
    var listenAddRemove = goog.bind(function() {
      var collection = group.getLayers();
      if (goog.isDef(collection)) {
        var handleContentChange_ = goog.bind(function(e) {
          // TODO: should remove the subtree
          // should synchronize the subtree
          this.synchronize_();
        }, this);
        contentKeys = [
          collection.on('add', handleContentChange_),
          collection.on('remove', function(event) {
            this.removeAndDestroySingleLayer_(event.element);
          }, this)
        ];
        listenKeyArray.push.apply(listenKeyArray, contentKeys);
      }
    }, this);
    listenAddRemove();

    listenKeyArray.push(group.on('change:layers', function(e) {
      goog.array.forEach(contentKeys, function(el) {
        goog.array.remove(listenKeyArray, el);
        ol.Observable.unByKey(el);
      });
      listenAddRemove();
    }));
  }

  delete this.unusedGroups_[uuid];
};

/**
 * Synchronizes single layer.
 * @param {ol.layer.Base} olLayer
 * @protected
 */
olcs.AbstractSynchronizer.prototype.synchronizeSingle = function(olLayer) {
  if (goog.isNull(olLayer)) {
    return;
  }
  var olLayerId = goog.getUid(olLayer);

  // handle layer groups
  goog.asserts.assert(!(olLayer instanceof ol.layer.Group));
  goog.asserts.assert(olLayer instanceof ol.layer.Layer);

  var cesiumObject = this.layerMap[olLayerId];

  // no mapping -> create new layer and set up synchronization
  if (!goog.isDef(cesiumObject)) {
    cesiumObject = this.createSingleCounterpart(olLayer);
    this.layerMap[olLayerId] = cesiumObject;
  }

  // add Cesium layers
  if (goog.isDefAndNotNull(cesiumObject)) {
    this.addCesiumObject(cesiumObject);
    delete this.unusedCesiumObjects_[cesiumObject];
  }
};

/**
 * Destroys all the created Cesium objects.
 * @protected
 */
olcs.AbstractSynchronizer.prototype.destroyAll = function() {
  this.removeLayer_(this.mapLayerGroup);
  this.layerMap = {};
};

/**
 * Adds a single Cesium object to the collection.
 * @param {!T} object
 * @protected
 */
olcs.AbstractSynchronizer.prototype.addCesiumObject = goog.abstractMethod;

/**
 * @param {!T} object
 * @protected
 */
olcs.AbstractSynchronizer.prototype.destroyCesiumObject = goog.abstractMethod;

/**
 * Remove single Cesium object from the collection.
 * @param {!T} object
 * @param {boolean} destroy
 * @protected
 */
olcs.AbstractSynchronizer.prototype.removeSingleCesiumObject =
    goog.abstractMethod;

/**
 * Remove all Cesium objects from the collection.
 * @param {boolean} destroy
 * @protected
 */
olcs.AbstractSynchronizer.prototype.removeAllCesiumObjects =
    goog.abstractMethod;

/**
 * @param {!ol.layer.Layer} olLayer
 * @return {T}
 * @protected
 */
olcs.AbstractSynchronizer.prototype.createSingleCounterpart =
    goog.abstractMethod;
<commit_msg>Use listenForGroupChanges for root layerGroup.<commit_after>goog.provide('olcs.AbstractSynchronizer');

goog.require('ol.Observable');
goog.require('ol.layer.Group');
goog.require('ol.layer.Layer');

/**
 * @param {!ol.Map} map
 * @param {!Cesium.Scene} scene
 * @constructor
 * @template T
 * @api
 */
olcs.AbstractSynchronizer = function(map, scene) {
  /**
   * @type {!ol.Map}
   * @protected
   */
  this.map = map;

  /**
   * @type {ol.View}
   * @protected
   */
  this.view = null;

  /**
   * @type {!Cesium.Scene}
   * @protected
   */
  this.scene = scene;

  /**
   * @type {ol.Collection.<ol.layer.Base>}
   * @protected
   */
  this.olLayers = null;

  /**
   * @type {ol.layer.Group}
   */
  this.mapLayerGroup = null;

  /**
   * @type {!Array.<goog.events.Key>}
   * @private
   */
  this.olLayersListenKeys_ = [];

  /**
   * Map of ol3 layer ids (from goog.getUid) to the Cesium ImageryLayers.
   * null value means, that we are unable to create equivalent layer.
   * @type {Object.<number, ?T>}
   * @protected
   */
  this.layerMap = {};

  /**
   * Map of listen keys for ol3 layer groups ids (from goog.getUid).
   * @type {!Object.<number, !Array.<goog.events.Key>>}
   * @private
   */
  this.olGroupListenKeys_ = {};

  /**
   * @type {Object.<number, !Array.<goog.events.Key>>}
   * @private
   */
  this.unusedGroups_ = {};

  /**
   * @type {Object.<?T, number>}
   * @private
   */
  this.unusedCesiumObjects_ = null;

  this.map.on('change:view', function(e) {
    this.setView_(this.map.getView());
  }, this);
  this.setView_(this.map.getView());

  this.map.on('change:layergroup', function(e) {
    this.setLayerGroup_(this.map.getLayerGroup());
  }, this);
  this.setLayerGroup_(this.map.getLayerGroup());
};

/**
 * @param {ol.View} view New view to use.
 * @private
 */
olcs.AbstractSynchronizer.prototype.setView_ = function(view) {
  this.view = view;

  // destroy all, the change of view can affect which layers are synced
  this.synchronize();
};

/**
 * @param {ol.layer.Group} layerGroup New layers to use.
 * @private
 */
olcs.AbstractSynchronizer.prototype.setLayerGroup_ = function(layerGroup) {
  this.mapLayerGroup = layerGroup;
  var layers = layerGroup.getLayers();
  if (!goog.isNull(this.olLayers)) {
    goog.array.forEach(this.olLayersListenKeys_, ol.Observable.unByKey);
  }

  this.olLayers = layers;

  this.listenForGroupChanges_(layerGroup);

  this.synchronize();
};

/**
 * Remove all and perform complete synchronization of the layers.
 * @api
 */
olcs.AbstractSynchronizer.prototype.synchronize = function() {
  this.destroyAll();
  this.synchronize_();
};

/**
 * @param {ol.layer.Base} layer
 * @param {Array.<ol.layer.Layer>} foundLayers
 * @param {Array.<ol.layer.Group>} foundGroups
 * @private
 */
olcs.AbstractSynchronizer.flattenLayers_ =
    function(layer, foundLayers, foundGroups) {
  if (layer instanceof ol.layer.Group) {
    foundGroups.push(layer);
    var sublayers = layer.getLayers();
    if (goog.isDef(sublayers)) {
      sublayers.forEach(function(el) {
        olcs.AbstractSynchronizer.flattenLayers_(el, foundLayers, foundGroups);
      });
    }
  } else {
    foundLayers.push(layer);
  }
};

/**
 * Perform complete synchronization of the layers.
 * @private
 */
olcs.AbstractSynchronizer.prototype.synchronize_ = function() {
  if (goog.isNull(this.view) || goog.isNull(this.olLayers)) {
    return;
  }
  this.unusedGroups_ = goog.object.clone(this.olGroupListenKeys_);
  this.unusedCesiumObjects_ = goog.object.transpose(this.layerMap);
  this.removeAllCesiumObjects(false); // only remove, don't destroy

  var layers = [];
  var groups = [];
  olcs.AbstractSynchronizer.flattenLayers_(this.mapLayerGroup, layers, groups);

  layers.forEach(function(el) {
    this.synchronizeSingle(el);
  }, this);

  groups.forEach(function(el) {
    this.listenForGroupChanges_(el);
  }, this);
};

/**
 * Destroy single layer.
 * @param {ol.layer.Layer} layer
 * @private
 */
olcs.AbstractSynchronizer.prototype.removeAndDestroySingleLayer_ =
    function(layer) {
  var uid = goog.getUid(layer);
  var counterpart = this.layerMap[uid];
  if (!!counterpart) {
    this.removeSingleCesiumObject(counterpart, false);
    this.destroyCesiumObject(counterpart);
  }
  delete this.layerMap[uid];
};

/**
 * Destroy single group.
 * @param {ol.layer.Group} group
 * @private
 */
olcs.AbstractSynchronizer.prototype.unlistenSingleGroup_ =
    function(group) {
  if (group === this.mapLayerGroup) {
    return;
  }
  var uid = goog.getUid(group);
  var keys = this.olGroupListenKeys_[uid];
  keys = keys || []; // FIXME: why+
  keys.forEach(function(key) {
    ol.Observable.unByKey(key);
  });
  delete this.olGroupListenKeys_[uid];
};

/**
 * Remove layer.
 * @param {ol.layer.Base} removeRoot
 * @private
 */
olcs.AbstractSynchronizer.prototype.removeLayer_ = function(removeRoot) {
  if (!removeRoot) {
    return;
  }
  var layers = [];
  var groups = [];
  olcs.AbstractSynchronizer.flattenLayers_(removeRoot, layers, groups);

  layers.forEach(function(el) {
    this.removeAndDestroySingleLayer_(el);
  }, this);

  groups.forEach(function(el) {
    this.unlistenSingleGroup_(el);
  }, this);
};

/**
 * Synchronizes single layer.
 * @param {ol.layer.Group} group
 * @private
 */
olcs.AbstractSynchronizer.prototype.listenForGroupChanges_ = function(group) {
  var uuid = goog.getUid(group);

  if (!goog.isDef(this.olGroupListenKeys_[uuid])) {
    var listenKeyArray = [];
    this.olGroupListenKeys_[uuid] = listenKeyArray;

    // only the keys that need to be relistened when collection changes
    var contentKeys = [];
    var listenAddRemove = goog.bind(function() {
      var collection = group.getLayers();
      if (goog.isDef(collection)) {
        var handleContentChange_ = goog.bind(function(e) {
          // TODO: should remove the subtree
          // should synchronize the subtree
          this.synchronize_();
        }, this);
        contentKeys = [
          collection.on('add', handleContentChange_),
          collection.on('remove', function(event) {
            this.removeAndDestroySingleLayer_(event.element);
          }, this)
        ];
        listenKeyArray.push.apply(listenKeyArray, contentKeys);
      }
    }, this);
    listenAddRemove();

    listenKeyArray.push(group.on('change:layers', function(e) {
      goog.array.forEach(contentKeys, function(el) {
        goog.array.remove(listenKeyArray, el);
        ol.Observable.unByKey(el);
      });
      listenAddRemove();
    }));
  }

  delete this.unusedGroups_[uuid];
};

/**
 * Synchronizes single layer.
 * @param {ol.layer.Base} olLayer
 * @protected
 */
olcs.AbstractSynchronizer.prototype.synchronizeSingle = function(olLayer) {
  if (goog.isNull(olLayer)) {
    return;
  }
  var olLayerId = goog.getUid(olLayer);

  // handle layer groups
  goog.asserts.assert(!(olLayer instanceof ol.layer.Group));
  goog.asserts.assert(olLayer instanceof ol.layer.Layer);

  var cesiumObject = this.layerMap[olLayerId];

  // no mapping -> create new layer and set up synchronization
  if (!goog.isDef(cesiumObject)) {
    cesiumObject = this.createSingleCounterpart(olLayer);
    this.layerMap[olLayerId] = cesiumObject;
  }

  // add Cesium layers
  if (goog.isDefAndNotNull(cesiumObject)) {
    this.addCesiumObject(cesiumObject);
    delete this.unusedCesiumObjects_[cesiumObject];
  }
};

/**
 * Destroys all the created Cesium objects.
 * @protected
 */
olcs.AbstractSynchronizer.prototype.destroyAll = function() {
  this.removeLayer_(this.mapLayerGroup);
  this.layerMap = {};
};

/**
 * Adds a single Cesium object to the collection.
 * @param {!T} object
 * @protected
 */
olcs.AbstractSynchronizer.prototype.addCesiumObject = goog.abstractMethod;

/**
 * @param {!T} object
 * @protected
 */
olcs.AbstractSynchronizer.prototype.destroyCesiumObject = goog.abstractMethod;

/**
 * Remove single Cesium object from the collection.
 * @param {!T} object
 * @param {boolean} destroy
 * @protected
 */
olcs.AbstractSynchronizer.prototype.removeSingleCesiumObject =
    goog.abstractMethod;

/**
 * Remove all Cesium objects from the collection.
 * @param {boolean} destroy
 * @protected
 */
olcs.AbstractSynchronizer.prototype.removeAllCesiumObjects =
    goog.abstractMethod;

/**
 * @param {!ol.layer.Layer} olLayer
 * @return {T}
 * @protected
 */
olcs.AbstractSynchronizer.prototype.createSingleCounterpart =
    goog.abstractMethod;
<|endoftext|>


<commit_before>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version).to.exist;
        expect(npmPackage.version).to.exist;

        if (!expect(ioPackage.common.version).to.be.equal(npmPackage.version)) {
            console.log('ERROR: Version numbers in package.json and io-package.json differ!!');
        }

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
        }

        expect(ioPackage.common.authors).to.exist;
        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length).to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0]).to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors).to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('Testing for set authors field in io-package skipped because template adapter');
        }
        done();
    });
});
<commit_msg>update basic package-file testing<commit_after>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        console.log();

        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        expect(fs.existsSync(__dirname + '/../LICENSE'), 'ERROR: LICENSE needs to exist! Please create one.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (ioPackage.common.name.indexOf('vis-') !== 0) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        expect(fs.existsSync(__dirname + '/../LICENSE'), 'A LICENSE must exist');
        var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        expect(fileContentReadme.indexOf('## License'), 'The README.md needs to have a section ## License').not.equal(-1);
        expect(fileContentReadme.indexOf('## Changelog'), 'The README.md needs to have a section ## Changelog').not.equal(-1);
        expect(fileContentReadme.indexOf('## Changelog'), 'The README.md needs to have a section ## License').to.be.below(fileContentReadme.indexOf('## License'));
        done();
    });
});
<|endoftext|>


<commit_before>export const ACTION = 'ACTION';

export const FETCH_BOOKS = 'FETCH_BOOKS';
export const SELECT_BOOK = 'SELECT_BOOK';
<commit_msg>Remove boilerplate action constant<commit_after>export const FETCH_BOOKS = 'FETCH_BOOKS';
export const SELECT_BOOK = 'SELECT_BOOK';
<|endoftext|>


<commit_before>import extend from 'extend';
import Recorder from 'recorderjs';
import audioBufferToWav from 'audiobuffer-to-wav';

const DEBUG_DOWNLOAD_WAVS=false;

var downloadWav = undefined;
if (DEBUG_DOWNLOAD_WAVS) {
  downloadWav = (function () {
      var a = document.createElement("a");
      document.body.appendChild(a);
      a.style = "display: none";
      return function (data, fileName) {
          var blob = new Blob([data], {type: "octet/stream"}),
              url = window.URL.createObjectURL(blob);
          a.href = url;
          a.download = fileName;
          a.click();
          window.URL.revokeObjectURL(url);
      };
  }());
}

function arraysToAudioBuffer(ctx, channelArray) {
  var buf = ctx.createBuffer(channelArray.length, channelArray[0].length, ctx.sampleRate);
  for (var i=0; i < channelArray.length; i++) {
    buf.copyToChannel(channelArray[i], i);
  }
  return buf;
}

function convolve (convolveBy, toConvolve, sampleRate, cb) {
  var numChannels = 1;
  var numSamples = Math.max(inArray[0].length, outArray[0].length);

  var offlineCtx = new OfflineAudioContext(numChannels, numSamples, sampleRate);

  var convolveByBuf = arraysToAudioBuffer(offlineCtx, convolveBy);
  var toConvolveBuf = arraysToAudioBuffer(offlineCtx, toConvolve);

  if (DEBUG_DOWNLOAD_WAVS) {
    // Save raw recordings for inspection
    window.setTimeout(function () {
      var wav = audioBufferToWav(convolveByBuf);
      downloadWav(wav, "convolve-by.wav");

      window.setTimeout(function () {
        var wav = audioBufferToWav(toConvolveBuf);
        downloadWav(wav, "to-convolve.wav");
      }, 1000);
    }, 1000);
  }

  var convolver = offlineCtx.createConvolver();
  convolver.normalize = true;
  convolver.buffer = convolveByBuf;
  convolver.connect(offlineCtx.destination)

  var toConvolveBufSource = offlineCtx.createBufferSource();
  toConvolveBufSource.buffer = toConvolveBuf;
  toConvolveBufSource.connect(convolver);
  toConvolveBufSource.start(0);

  offlineCtx.oncomplete = function (e) {
    var renderedBuffer = e.renderedBuffer;
    var wav = audioBufferToWav(renderedBuffer, { float32: true });

    if (DEBUG_DOWNLOAD_WAVS) {
      downloadWav(wav, "convolved.wav");
    }

    cb(wav, {
      irBuffer: renderedBuffer,
      toConvolveBuf: toConvolveBuf,
      convolveByBuf: convolveByBuf
    });
  };

  offlineCtx.startRendering();
}

function crossCorrelate(inArray, outArray, sampleRate, cb) {
  // Reverse signal to be convolved by so we get cross-correlation
  inArray.forEach(function (chan) {
    chan.reverse();
  });

  convolve(inArray, outArray, sampleRate, cb);
}

function measureImpulseResponse(inAudioNode, outAudioNode, config) {
  config = extend({
    startFreq: 60.0,
    endFreq: 8000.0,
    sineSweepLengthSecs: 30.0,
    impulseResponseLengthSecs: 2.0,
    sineSweepGain: 0.5
  }, config);

  if (inAudioNode.context !== outAudioNode.context) throw "inAudioNode and outAudioNode must be in the same AudioContext";

  var ctx = inAudioNode.context;

  var gain = ctx.createGain();
  gain.gain.value = config.sineSweepGain;
  gain.connect(outAudioNode);

  var sine = ctx.createOscillator();
  sine.type = "sine";
  sine.frequency.setValueAtTime(config.startFreq, 0);
  sine.frequency.exponentialRampToValueAtTime(config.endFreq, config.sineSweepLengthSecs);
  sine.connect(gain);

  // Record the sine sweep
  var recordIn = new Recorder(inAudioNode);
  var recordOut = new Recorder(gain);
  recordIn.record();
  recordOut.record();

  // Run the sweep from now until config.sineSweepLengthSecs
  sine.start(ctx.currentTime);
  sine.stop(ctx.currentTime + config.sineSweepLengthSecs);

  // Run the sweep, then give a little time to 'ring out'
  var recordingLength = config.sineSweepLengthSecs + config.impulseResponseLengthSecs;

  return new Promise(function (resolve, reject) {
    window.setTimeout(function () {
      recordIn.stop();
      recordOut.stop();

      recordIn.getBuffer(function (inChannelArray) {
        recordOut.getBuffer(function (outChannelArray) {

          crossCorrelate(inChannelArray, outChannelArray, ctx.sampleRate, function (impulseResponseWAV, etc) {
            return resolve(impulseResponseWAV, etc);
          });

        })
      });
    }, recordingLength * 1000.0);
  });
}

export default measureImpulseResponse;

export {
  measureImpulseResponse,
  convolve,
  crossCorrelate,
  downloadWav
};<commit_msg>Fix broken reference<commit_after>import extend from 'extend';
import Recorder from 'recorderjs';
import audioBufferToWav from 'audiobuffer-to-wav';

const DEBUG_DOWNLOAD_WAVS=false;

var downloadWav = undefined;
if (DEBUG_DOWNLOAD_WAVS) {
  downloadWav = (function () {
      var a = document.createElement("a");
      document.body.appendChild(a);
      a.style = "display: none";
      return function (data, fileName) {
          var blob = new Blob([data], {type: "octet/stream"}),
              url = window.URL.createObjectURL(blob);
          a.href = url;
          a.download = fileName;
          a.click();
          window.URL.revokeObjectURL(url);
      };
  }());
}

function arraysToAudioBuffer(ctx, channelArray) {
  var buf = ctx.createBuffer(channelArray.length, channelArray[0].length, ctx.sampleRate);
  for (var i=0; i < channelArray.length; i++) {
    buf.copyToChannel(channelArray[i], i);
  }
  return buf;
}

function convolve (convolveBy, toConvolve, sampleRate, cb) {
  var numChannels = 1;
  var numSamples = Math.max(convolveBy[0].length, toConvolve[0].length);

  var offlineCtx = new OfflineAudioContext(numChannels, numSamples, sampleRate);

  var convolveByBuf = arraysToAudioBuffer(offlineCtx, convolveBy);
  var toConvolveBuf = arraysToAudioBuffer(offlineCtx, toConvolve);

  if (DEBUG_DOWNLOAD_WAVS) {
    // Save raw recordings for inspection
    window.setTimeout(function () {
      var wav = audioBufferToWav(convolveByBuf);
      downloadWav(wav, "convolve-by.wav");

      window.setTimeout(function () {
        var wav = audioBufferToWav(toConvolveBuf);
        downloadWav(wav, "to-convolve.wav");
      }, 1000);
    }, 1000);
  }

  var convolver = offlineCtx.createConvolver();
  convolver.normalize = true;
  convolver.buffer = convolveByBuf;
  convolver.connect(offlineCtx.destination)

  var toConvolveBufSource = offlineCtx.createBufferSource();
  toConvolveBufSource.buffer = toConvolveBuf;
  toConvolveBufSource.connect(convolver);
  toConvolveBufSource.start(0);

  offlineCtx.oncomplete = function (e) {
    var renderedBuffer = e.renderedBuffer;
    var wav = audioBufferToWav(renderedBuffer, { float32: true });

    if (DEBUG_DOWNLOAD_WAVS) {
      downloadWav(wav, "convolved.wav");
    }

    cb(wav, {
      irBuffer: renderedBuffer,
      toConvolveBuf: toConvolveBuf,
      convolveByBuf: convolveByBuf
    });
  };

  offlineCtx.startRendering();
}

function crossCorrelate(inArray, outArray, sampleRate, cb) {
  // Reverse signal to be convolved by so we get cross-correlation
  inArray.forEach(function (chan) {
    chan.reverse();
  });

  convolve(inArray, outArray, sampleRate, cb);
}

function measureImpulseResponse(inAudioNode, outAudioNode, config) {
  config = extend({
    startFreq: 60.0,
    endFreq: 8000.0,
    sineSweepLengthSecs: 30.0,
    impulseResponseLengthSecs: 2.0,
    sineSweepGain: 0.5
  }, config);

  if (inAudioNode.context !== outAudioNode.context) throw "inAudioNode and outAudioNode must be in the same AudioContext";

  var ctx = inAudioNode.context;

  var gain = ctx.createGain();
  gain.gain.value = config.sineSweepGain;
  gain.connect(outAudioNode);

  var sine = ctx.createOscillator();
  sine.type = "sine";
  sine.frequency.setValueAtTime(config.startFreq, 0);
  sine.frequency.exponentialRampToValueAtTime(config.endFreq, config.sineSweepLengthSecs);
  sine.connect(gain);

  // Record the sine sweep
  var recordIn = new Recorder(inAudioNode);
  var recordOut = new Recorder(gain);
  recordIn.record();
  recordOut.record();

  // Run the sweep from now until config.sineSweepLengthSecs
  sine.start(ctx.currentTime);
  sine.stop(ctx.currentTime + config.sineSweepLengthSecs);

  // Run the sweep, then give a little time to 'ring out'
  var recordingLength = config.sineSweepLengthSecs + config.impulseResponseLengthSecs;

  return new Promise(function (resolve, reject) {
    window.setTimeout(function () {
      recordIn.stop();
      recordOut.stop();

      recordIn.getBuffer(function (inChannelArray) {
        recordOut.getBuffer(function (outChannelArray) {

          crossCorrelate(inChannelArray, outChannelArray, ctx.sampleRate, function (impulseResponseWAV, etc) {
            return resolve(impulseResponseWAV, etc);
          });

        })
      });
    }, recordingLength * 1000.0);
  });
}

export default measureImpulseResponse;

export {
  measureImpulseResponse,
  convolve,
  crossCorrelate,
  downloadWav
};<|endoftext|>


<commit_before>function httpGet(url) {
	var xhr = new XMLHttpRequest();
	xhr.open("GET", url, false);
	xhr.send(null);
	return xhr.responseText;
}

<commit_msg>no message<commit_after>function httpGet(url) {
	var xhr = new XMLHttpRequest();
	xhr.open("GET", url, false);
	xhr.send(null);
	return xhr.JSON.parse();
}

<|endoftext|>