<commit_before>/*******************************************************************************
 * (C) Copyright 2016 Jerome Comte and Dorian Cransac
 *
 * This file is part of STEP
 *
 * STEP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * STEP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with STEP.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
angular.module('functionsControllers',['step'])

.run(function(FunctionTypeRegistry, ViewRegistry, EntityRegistry) {
  ViewRegistry.registerView('functions','partials/functionList.html');
  FunctionTypeRegistry.register('step.plugins.functions.types.CompositeFunction','Composite','partials/functions/forms/composite.html');
  EntityRegistry.registerEntity('Keyword', 'function', 'functions', 'rest/functions/', 'rest/functions/', 'st-table', '/partials/functions/functionSelectionTable.html', null, 'glyphicon glyphicon-record');
})

.factory('FunctionTypeRegistry', function() {

  var registry = {};

  var api = {};

  api.register = function(typeName,label,form) {
    registry[typeName] = {"label":label,"form":form};
  }

  api.getForm = function(typeName) {
    return registry[typeName].form;
  }

  api.getLabel = function(typeName) {
    return registry[typeName]?registry[typeName].label:"Unknown";
  }

  api.getTypes = function() {
    return _.keys(registry);
  }

  return api;
})

.factory('FunctionDialogs', function ($rootScope, $uibModal, $http, Dialogs, $location) {

  function openModal(function_) {
    var modalInstance = $uibModal.open({
      backdrop: 'static',
        templateUrl: 'partials/functions/functionConfigurationDialog.html',
        controller: 'newFunctionModalCtrl',
        resolve: {function_: function () {return function_;}}
      });

      return modalInstance.result;
  }

  var dialogs = {};

  dialogs.editFunction = function(id, callback) {
    $http.get("rest/functions/"+id).then(function(response) {
      openModal(response.data).then(function() {
        if(callback){callback()};
      })
    });
  }

  dialogs.addFunction = function(callback) {
    openModal().then(function() {
      if(callback){callback()};
    })
  }

  dialogs.openFunctionEditor = function(functionid) {
    $http.get("rest/functions/"+functionid+"/editor").then(function(response){
      var path = response.data
      if(path) {
        $location.path(path);
      } else {
        Dialogs.showErrorMsg("No editor configured for this function type");
      }
    })
  }

  return dialogs;
})

.controller('FunctionListCtrl', function($scope, $rootScope, $compile, $http, $interval, $uibModal, stateStorage, Dialogs, FunctionDialogs, $location, AuthService, FunctionTypeRegistry) {
  stateStorage.push($scope, 'functions', {});

  $scope.authService = AuthService;
  $scope.tableHandle = {};

  function reload() {
    $scope.tableHandle.reload();
  }

  $scope.editFunction = function(id) {
    FunctionDialogs.editFunction(id, function() {reload()});
  }

  $scope.copyFunction = function(id) {
    $rootScope.clipboard = {object:"function",id:id};
  }

  $scope.pasteFunction = function() {
    if($rootScope.clipboard && $rootScope.clipboard.object=="function") {
      $http.post("rest/functions/"+$rootScope.clipboard.id+"/copy")
      .then(function() {reload()});
    }
  }

  $scope.openFunctionEditor = function(functionid) {
    FunctionDialogs.openFunctionEditor(functionid);
  }

  $scope.addFunction = function() {
    FunctionDialogs.addFunction(function() {reload()});
  }

  $scope.executeFunction = function(id) {
    $http.post("rest/interactive/functiontest/"+id+"/start").then(function(response) {
      var result = response.data;
      $rootScope.planEditorInitialState = {
          interactive : true,
          selectedNode : result.callFunctionId
      }
      $location.path('/root/plans/editor/' + result.planId);
    });
  }

  $scope.deleteFunction = function(id) {
    Dialogs.showDeleteWarning().then(function() {
      $http.delete("rest/functions/"+id).then(function() {reload()});
    })
  }

  $scope.functionTypeLabel = function(type) {
    return FunctionTypeRegistry.getLabel(type);
  }
})

.controller('newFunctionModalCtrl', [ '$rootScope', '$scope', '$uibModalInstance', '$http', '$location', 'function_', 'Dialogs', 'AuthService','FunctionTypeRegistry',
function ($rootScope, $scope, $uibModalInstance, $http, $location, function_,Dialogs, AuthService, FunctionTypeRegistry) {
  $scope.functionTypeRegistry = FunctionTypeRegistry;

  var newFunction = function_==null;
  $scope.mode = newFunction?"add":"edit";

  $scope.isSchemaEnforced = AuthService.getConf().miscParams.enforceschemas;

  $scope.addRoutingCriteria = function() {
    $scope.criteria.push({key:"",value:""});
  }

  $scope.removeRoutingCriteria = function(key) {
    delete $scope.function_.tokenSelectionCriteria[key];
    loadTokenSelectionCriteria($scope.function_);
  }

  $scope.saveRoutingCriteria = function() {
    var tokenSelectionCriteria = {}
    _.each($scope.criteria, function(entry) {
      tokenSelectionCriteria[entry.key]=entry.value;
    });
    $scope.function_.tokenSelectionCriteria = tokenSelectionCriteria;
  }

  var loadTokenSelectionCriteria = function(function_) {
    $scope.criteria = [];
    if(function_ && function_.tokenSelectionCriteria) {
      _.mapObject(function_.tokenSelectionCriteria,function(val,key) {
        $scope.criteria.push({key:key,value:val});
      });
    }
  }

  loadTokenSelectionCriteria(function_);

  $scope.loadInitialFunction = function() {
    $http.get("rest/functions/types/"+$scope.function_.type).then(function(response){
      var initialFunction = response.data;
      if($scope.function_) {
        initialFunction.id = $scope.function_.id;
        if($scope.function_.attributes) {
          initialFunction.attributes = $scope.function_.attributes;
        }
        if($scope.function_.schema) {
          initialFunction.schema = $scope.function_.schema;
        }
      }

      loadTokenSelectionCriteria(initialFunction);

      $scope.function_ = initialFunction;
      $scope.schemaStr = JSON.stringify($scope.function_.schema);
    })
  }

  if(newFunction) {
    $scope.function_ = {type:'step.plugins.java.GeneralScriptFunction'}
    $scope.loadInitialFunction();
  } else {
    $scope.function_ = function_;
    $scope.schemaStr = JSON.stringify($scope.function_.schema);
  }

  $scope.save = function (editAfterSave) {

	var schemaJson;
	try {
  	schemaJson = JSON.parse($scope.schemaStr);
  	$scope.function_.schema = schemaJson;

  	$http.post("rest/functions",$scope.function_).then(function(response) {
  	  var function_ = response.data;
  	  $uibModalInstance.close(response.data);

  	  if(editAfterSave) {
  	    $http.get("rest/functions/"+function_.id+"/editor").then(function(response){
  	      var path = response.data;
  	      if(path) {
  	        $location.path(path);
  	      } else {
  	        Dialogs.showErrorMsg("No editor configured for this function type");
  	      }
  	    })
  	  }
  	})

  	}catch(e){
  		Dialogs.showErrorMsg("incorrect schema format (must be Json) : " + e);
  	}
   };

  $scope.cancel = function () {
    $uibModalInstance.dismiss('cancel');
  };
}])

.controller('selectFunctionModalCtrl', function ($scope, $uibModalInstance) {

  $scope.selectFunction = function(id) {
    $uibModalInstance.close(id);
  }

  $scope.table = {};

  $scope.tabledef = {}

  $scope.tabledef.columns = function(columns) {
    _.each(_.where(columns, { 'title' : 'ID' }), function(col) {
      col.visible = false
    });
    _.each(_.where(columns,{'title':'Actions'}),function(col){
        col.title="Actions";
        col.searchmode="none";
        col.width="160px";
        col.render = function ( data, type, row ) {
          var html = '
' +
            '
' +
            '<button type="button" class="btn btn-default" aria-label="Left Align" onclick="angular.element(\'#FunctionListCtrl\').scope().selectFunction(\''+row[0]+'\')">' +
            '';
          html+='

';
          return html;
        }
       });
    return columns;
  };

  $scope.cancel = function () {
    $uibModalInstance.dismiss('cancel');
  };
})
.controller('CompositeFunctionFormCtrl' , function($scope,PlanDialogs,$location,$http) {
  $scope.gotoArtefact = function() {
    $scope.save(false);
    $location.path('/root/artefacteditor/' + $scope.function_.artefactId);
  }

  $scope.selectPlan = function() {
    PlanDialogs.selectPlan(function(plan) {
      $scope.function_.planId = plan.id;
    })
  }
})

.directive('functionLink', function() {
  return {
    restrict: 'E',
    scope: {
      function_: '='
    },
    templateUrl: 'partials/functions/functionLink.html',
    controller: function($scope, FunctionDialogs) {
      $scope.openFunctionEditor = function() {
        FunctionDialogs.openFunctionEditor($scope.function_.id);
      }
    }
  };
})
<commit_msg>SED-344 Keywords list not reloaded when a keywords package is deleted<commit_after>/*******************************************************************************
 * (C) Copyright 2016 Jerome Comte and Dorian Cransac
 *
 * This file is part of STEP
 *
 * STEP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * STEP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with STEP.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
angular.module('functionsControllers',['step'])

.run(function(FunctionTypeRegistry, ViewRegistry, EntityRegistry) {
  ViewRegistry.registerView('functions','partials/functionList.html');
  FunctionTypeRegistry.register('step.plugins.functions.types.CompositeFunction','Composite','partials/functions/forms/composite.html');
  EntityRegistry.registerEntity('Keyword', 'function', 'functions', 'rest/functions/', 'rest/functions/', 'st-table', '/partials/functions/functionSelectionTable.html', null, 'glyphicon glyphicon-record');
})

.factory('FunctionTypeRegistry', function() {

  var registry = {};

  var api = {};

  api.register = function(typeName,label,form) {
    registry[typeName] = {"label":label,"form":form};
  }

  api.getForm = function(typeName) {
    return registry[typeName].form;
  }

  api.getLabel = function(typeName) {
    return registry[typeName]?registry[typeName].label:"Unknown";
  }

  api.getTypes = function() {
    return _.keys(registry);
  }

  return api;
})

.factory('FunctionDialogs', function ($rootScope, $uibModal, $http, Dialogs, $location) {

  function openModal(function_) {
    var modalInstance = $uibModal.open({
      backdrop: 'static',
        templateUrl: 'partials/functions/functionConfigurationDialog.html',
        controller: 'newFunctionModalCtrl',
        resolve: {function_: function () {return function_;}}
      });

      return modalInstance.result;
  }

  var dialogs = {};

  dialogs.editFunction = function(id, callback) {
    $http.get("rest/functions/"+id).then(function(response) {
      openModal(response.data).then(function() {
        if(callback){callback()};
      })
    });
  }

  dialogs.addFunction = function(callback) {
    openModal().then(function() {
      if(callback){callback()};
    })
  }

  dialogs.openFunctionEditor = function(functionid) {
    $http.get("rest/functions/"+functionid+"/editor").then(function(response){
      var path = response.data
      if(path) {
        $location.path(path);
      } else {
        Dialogs.showErrorMsg("No editor configured for this function type");
      }
    })
  }

  return dialogs;
})

.controller('FunctionListCtrl', function($scope, $rootScope, $compile, $http, $interval, $uibModal, stateStorage, Dialogs, FunctionDialogs, $location, AuthService, FunctionTypeRegistry) {
  stateStorage.push($scope, 'functions', {});

  $scope.authService = AuthService;
  $scope.tableHandle = {};

  function reload() {
    $scope.tableHandle.reload();
  }

  $scope.reload = reload;

  $scope.$on('functions.collection.change', function(evt, data){
    reload();
  })

  $scope.editFunction = function(id) {
    FunctionDialogs.editFunction(id, function() {reload()});
  }

  $scope.copyFunction = function(id) {
    $rootScope.clipboard = {object:"function",id:id};
  }

  $scope.pasteFunction = function() {
    if($rootScope.clipboard && $rootScope.clipboard.object=="function") {
      $http.post("rest/functions/"+$rootScope.clipboard.id+"/copy")
      .then(function() {reload()});
    }
  }

  $scope.openFunctionEditor = function(functionid) {
    FunctionDialogs.openFunctionEditor(functionid);
  }

  $scope.addFunction = function() {
    FunctionDialogs.addFunction(function() {reload()});
  }

  $scope.executeFunction = function(id) {
    $http.post("rest/interactive/functiontest/"+id+"/start").then(function(response) {
      var result = response.data;
      $rootScope.planEditorInitialState = {
          interactive : true,
          selectedNode : result.callFunctionId
      }
      $location.path('/root/plans/editor/' + result.planId);
    });
  }

  $scope.deleteFunction = function(id) {
    Dialogs.showDeleteWarning().then(function() {
      $http.delete("rest/functions/"+id).then(function() {reload()});
    })
  }

  $scope.functionTypeLabel = function(type) {
    return FunctionTypeRegistry.getLabel(type);
  }
})

.controller('newFunctionModalCtrl', [ '$rootScope', '$scope', '$uibModalInstance', '$http', '$location', 'function_', 'Dialogs', 'AuthService','FunctionTypeRegistry',
function ($rootScope, $scope, $uibModalInstance, $http, $location, function_,Dialogs, AuthService, FunctionTypeRegistry) {
  $scope.functionTypeRegistry = FunctionTypeRegistry;

  var newFunction = function_==null;
  $scope.mode = newFunction?"add":"edit";

  $scope.isSchemaEnforced = AuthService.getConf().miscParams.enforceschemas;

  $scope.addRoutingCriteria = function() {
    $scope.criteria.push({key:"",value:""});
  }

  $scope.removeRoutingCriteria = function(key) {
    delete $scope.function_.tokenSelectionCriteria[key];
    loadTokenSelectionCriteria($scope.function_);
  }

  $scope.saveRoutingCriteria = function() {
    var tokenSelectionCriteria = {}
    _.each($scope.criteria, function(entry) {
      tokenSelectionCriteria[entry.key]=entry.value;
    });
    $scope.function_.tokenSelectionCriteria = tokenSelectionCriteria;
  }

  var loadTokenSelectionCriteria = function(function_) {
    $scope.criteria = [];
    if(function_ && function_.tokenSelectionCriteria) {
      _.mapObject(function_.tokenSelectionCriteria,function(val,key) {
        $scope.criteria.push({key:key,value:val});
      });
    }
  }

  loadTokenSelectionCriteria(function_);

  $scope.loadInitialFunction = function() {
    $http.get("rest/functions/types/"+$scope.function_.type).then(function(response){
      var initialFunction = response.data;
      if($scope.function_) {
        initialFunction.id = $scope.function_.id;
        if($scope.function_.attributes) {
          initialFunction.attributes = $scope.function_.attributes;
        }
        if($scope.function_.schema) {
          initialFunction.schema = $scope.function_.schema;
        }
      }

      loadTokenSelectionCriteria(initialFunction);

      $scope.function_ = initialFunction;
      $scope.schemaStr = JSON.stringify($scope.function_.schema);
    })
  }

  if(newFunction) {
    $scope.function_ = {type:'step.plugins.java.GeneralScriptFunction'}
    $scope.loadInitialFunction();
  } else {
    $scope.function_ = function_;
    $scope.schemaStr = JSON.stringify($scope.function_.schema);
  }

  $scope.save = function (editAfterSave) {

	var schemaJson;
	try {
  	schemaJson = JSON.parse($scope.schemaStr);
  	$scope.function_.schema = schemaJson;

  	$http.post("rest/functions",$scope.function_).then(function(response) {
  	  var function_ = response.data;
  	  $uibModalInstance.close(response.data);

  	  if(editAfterSave) {
  	    $http.get("rest/functions/"+function_.id+"/editor").then(function(response){
  	      var path = response.data;
  	      if(path) {
  	        $location.path(path);
  	      } else {
  	        Dialogs.showErrorMsg("No editor configured for this function type");
  	      }
  	    })
  	  }
  	})

  	}catch(e){
  		Dialogs.showErrorMsg("incorrect schema format (must be Json) : " + e);
  	}
   };

  $scope.cancel = function () {
    $uibModalInstance.dismiss('cancel');
  };
}])

.controller('selectFunctionModalCtrl', function ($scope, $uibModalInstance) {

  $scope.selectFunction = function(id) {
    $uibModalInstance.close(id);
  }

  $scope.table = {};

  $scope.tabledef = {}

  $scope.tabledef.columns = function(columns) {
    _.each(_.where(columns, { 'title' : 'ID' }), function(col) {
      col.visible = false
    });
    _.each(_.where(columns,{'title':'Actions'}),function(col){
        col.title="Actions";
        col.searchmode="none";
        col.width="160px";
        col.render = function ( data, type, row ) {
          var html = '
' +
            '
' +
            '<button type="button" class="btn btn-default" aria-label="Left Align" onclick="angular.element(\'#FunctionListCtrl\').scope().selectFunction(\''+row[0]+'\')">' +
            '';
          html+='

';
          return html;
        }
       });
    return columns;
  };

  $scope.cancel = function () {
    $uibModalInstance.dismiss('cancel');
  };
})
.controller('CompositeFunctionFormCtrl' , function($scope,PlanDialogs,$location,$http) {
  $scope.gotoArtefact = function() {
    $scope.save(false);
    $location.path('/root/artefacteditor/' + $scope.function_.artefactId);
  }

  $scope.selectPlan = function() {
    PlanDialogs.selectPlan(function(plan) {
      $scope.function_.planId = plan.id;
    })
  }
})

.directive('functionLink', function() {
  return {
    restrict: 'E',
    scope: {
      function_: '='
    },
    templateUrl: 'partials/functions/functionLink.html',
    controller: function($scope, FunctionDialogs) {
      $scope.openFunctionEditor = function() {
        FunctionDialogs.openFunctionEditor($scope.function_.id);
      }
    }
  };
})
<|endoftext|>


<commit_before>import 'babel-polyfill';

import KoaStatic from 'koa-static';
import cluster from 'cluster';
import { cpus } from 'os';

import Server from '@r/platform/server';
import { dispatchInitialShell } from '@r/platform/plugins';
import APIOptions from '@r/api-client';

import routes from 'app/router';
import main from 'server/templates/main';
import allReducers from 'app/reducers';
import loginproxy from 'server/session/loginproxy';
import logoutproxy from 'server/session/logoutproxy';
import refreshproxy from 'server/session/refreshproxy';
import dispatchSession from 'server/session/dispatchSession';
import { dispatchInitialCompact } from 'server/initialState/dispatchInitialCompact';
import { dispatchInitialLoid } from 'server/initialState/dispatchInitialLoid';
import { dispatchInitialTheme } from 'server/initialState/dispatchInitialTheme';

import dispatchInitialCollapsedComments from
  'server/initialState/dispatchInitialCollapsedComments';

const binFiles = KoaStatic('bin');
const assetFiles = KoaStatic('assets');

const processes = process.env.PROCESSES || cpus().length;

// set up the private API
const ConfigedAPIOptions = {
  ...APIOptions,
  origin: 'https://www.reddit.com',
  oauthAppOrigin: 'https://m.reddit.com',
  clientId: process.env.SECRET_OAUTH_CLIENT_ID,
  clientSecret: process.env.OAUTH_SECRET,
};

export function startServer() {
  console.log(`Started server at PID ${process.pid}`);
  // Create and launch the server
  return Server({
    routes,
    template: main,
    reducers: allReducers,
    dispatchBeforeNavigation: async (ctx, dispatch/*, getState, utils*/) => {
      dispatchInitialShell(ctx, dispatch);
      dispatchInitialLoid(ctx, dispatch);
      await dispatchSession(ctx, dispatch, ConfigedAPIOptions);
      dispatchInitialTheme(ctx, dispatch);
      dispatchInitialCollapsedComments(ctx, dispatch);
      dispatchInitialCompact(ctx, dispatch);
    },
    preRouteServerMiddleware: [
      binFiles,
      assetFiles,
    ],
    getServerRouter: router => {
      // private routes for login, logout, register, and token refresh
      loginproxy(router, ConfigedAPIOptions);
      logoutproxy(router, ConfigedAPIOptions);
      // registerproxy(router, ConfigedAPIOptions);
      refreshproxy(router, ConfigedAPIOptions);
    },
  })();
}

export function workerExit(failedProcesses, worker) {
  if (failedProcesses < 20) {
    console.log(`Worker ${worker.process.pid} died, restarting.`);
    cluster.fork();
    failedProcesses++;
  } else {
    console.log('Workers died too many times, exiting.');
    process.exit();
  }
}

export function startCluster() {
  let failedProcesses = 0;

  cluster.setupMaster();

  for (let i = 0; i < processes; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker) => workerExit(failedProcesses, worker));
  cluster.on('exit', () => failedProcesses++);

  console.log(`Started cluster with ${processes} processes.`);
}

if (cluster.isMaster && processes > 1) {
  startCluster();
} else {
  startServer();
}
<commit_msg>Fix @r/platform/Server casing<commit_after>import 'babel-polyfill';

import KoaStatic from 'koa-static';
import cluster from 'cluster';
import { cpus } from 'os';

import Server from '@r/platform/Server';
import { dispatchInitialShell } from '@r/platform/plugins';
import APIOptions from '@r/api-client';

import routes from 'app/router';
import main from 'server/templates/main';
import allReducers from 'app/reducers';
import loginproxy from 'server/session/loginproxy';
import logoutproxy from 'server/session/logoutproxy';
import refreshproxy from 'server/session/refreshproxy';
import dispatchSession from 'server/session/dispatchSession';
import { dispatchInitialCompact } from 'server/initialState/dispatchInitialCompact';
import { dispatchInitialLoid } from 'server/initialState/dispatchInitialLoid';
import { dispatchInitialTheme } from 'server/initialState/dispatchInitialTheme';

import dispatchInitialCollapsedComments from
  'server/initialState/dispatchInitialCollapsedComments';

const binFiles = KoaStatic('bin');
const assetFiles = KoaStatic('assets');

const processes = process.env.PROCESSES || cpus().length;

// set up the private API
const ConfigedAPIOptions = {
  ...APIOptions,
  origin: 'https://www.reddit.com',
  oauthAppOrigin: 'https://m.reddit.com',
  clientId: process.env.SECRET_OAUTH_CLIENT_ID,
  clientSecret: process.env.OAUTH_SECRET,
};

export function startServer() {
  console.log(`Started server at PID ${process.pid}`);
  // Create and launch the server
  return Server({
    routes,
    template: main,
    reducers: allReducers,
    dispatchBeforeNavigation: async (ctx, dispatch/*, getState, utils*/) => {
      dispatchInitialShell(ctx, dispatch);
      dispatchInitialLoid(ctx, dispatch);
      await dispatchSession(ctx, dispatch, ConfigedAPIOptions);
      dispatchInitialTheme(ctx, dispatch);
      dispatchInitialCollapsedComments(ctx, dispatch);
      dispatchInitialCompact(ctx, dispatch);
    },
    preRouteServerMiddleware: [
      binFiles,
      assetFiles,
    ],
    getServerRouter: router => {
      // private routes for login, logout, register, and token refresh
      loginproxy(router, ConfigedAPIOptions);
      logoutproxy(router, ConfigedAPIOptions);
      // registerproxy(router, ConfigedAPIOptions);
      refreshproxy(router, ConfigedAPIOptions);
    },
  })();
}

export function workerExit(failedProcesses, worker) {
  if (failedProcesses < 20) {
    console.log(`Worker ${worker.process.pid} died, restarting.`);
    cluster.fork();
    failedProcesses++;
  } else {
    console.log('Workers died too many times, exiting.');
    process.exit();
  }
}

export function startCluster() {
  let failedProcesses = 0;

  cluster.setupMaster();

  for (let i = 0; i < processes; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker) => workerExit(failedProcesses, worker));
  cluster.on('exit', () => failedProcesses++);

  console.log(`Started cluster with ${processes} processes.`);
}

if (cluster.isMaster && processes > 1) {
  startCluster();
} else {
  startServer();
}
<|endoftext|>


<commit_before>KnugenGame.Splash = function(game){
};
KnugenGame.Splash.prototype = {
   create: function(){

      var logo = this.add.sprite(this.game.width/2, this.game.height/2, 'TNSlogo');
      logo.anchor.setTo(0.5);
      logo.scale.setTo(0.6);
      var fadeTween = this.game.add.tween(logo).to({alpha: 0}, 1000, Phaser.Easing.Linear.None, false, 3000);
      fadeTween.onComplete.add(this.startGame, this);
      fadeTween.start();

      this.game.input.onDown.add(this.startGame, this);

      var music = this.game.add.audio('bgMusic', 0.7, true);
      //music.play();
   },
   startGame: function() {
      // start the Game state
      this.state.start('MainMenu');
   }
};
<commit_msg>forgot music...<commit_after>KnugenGame.Splash = function(game){
};
KnugenGame.Splash.prototype = {
   create: function(){

      var logo = this.add.sprite(this.game.width/2, this.game.height/2, 'TNSlogo');
      logo.anchor.setTo(0.5);
      logo.scale.setTo(0.6);
      var fadeTween = this.game.add.tween(logo).to({alpha: 0}, 1000, Phaser.Easing.Linear.None, false, 3000);
      fadeTween.onComplete.add(this.startGame, this);
      fadeTween.start();

      this.game.input.onDown.add(this.startGame, this);

      var music = this.game.add.audio('bgMusic', 0.7, true);
      music.play();
   },
   startGame: function() {
      // start the Game state
      this.state.start('MainMenu');
   }
};
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import mapTree from './mapTree';
import noVelocity from './noVelocity';
import compareTrees from './compareTrees';
import mergeDiff from './mergeDiff';
import configAnimation from './animationLoop';
import zero from './zero';
import {interpolateValue, updateCurrValue, updateCurrVelocity} from './updateTree';

const startAnimation = configAnimation();

function animationStep(shouldMerge, stopAnimation, getProps, timestep, state) {
  let {currValue, currVelocity} = state;
  let {willEnter, willLeave, endValue} = getProps();

  if (typeof endValue === 'function') {
    endValue = endValue(currValue);
  }

  let mergedValue = endValue; // set mergedValue to endValue as the default
  let hasNewKey = false;

  if (shouldMerge) {
    mergedValue = mergeDiff(
      currValue,
      endValue,
      // TODO: stop allocating like crazy in this whole code path
      key => {
        const res = willLeave(key, currValue[key], endValue, currValue, currVelocity);
        if (res == null) {
          // For legacy reason. We won't allow returning null soon
          // TODO: remove, after next release
          return null;
        }

        if (noVelocity(currVelocity[key]) && compareTrees(currValue[key], res)) {
          return null;
        }
        return res;
      }
    );

    Object.keys(mergedValue)
      .filter(key => !currValue.hasOwnProperty(key))
      .forEach(key => {
        hasNewKey = true;
        const enterValue = willEnter(key, mergedValue[key], endValue, currValue, currVelocity);
        currValue[key] = enterValue;
        mergedValue[key] = enterValue;
        currVelocity[key] = mapTree(zero, currValue[key]);
      });
  }

  const newCurrValue = updateCurrValue(timestep, currValue, currVelocity, mergedValue);
  const newCurrVelocity = updateCurrVelocity(timestep, currValue, currVelocity, mergedValue);

  if (!hasNewKey && noVelocity(currVelocity) && noVelocity(newCurrVelocity)) {
    // check explanation in `Spring.animationRender`
    stopAnimation(); // Nasty side effects....
  }

  return {
    currValue: newCurrValue,
    currVelocity: newCurrVelocity,
  };
}

export const Spring = React.createClass({
  propTypes: {
    defaultValue: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.array,
    ]),
    endValue: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.object,
      PropTypes.array,
    ]).isRequired,
    children: PropTypes.func.isRequired,
  },

  getInitialState() {
    const {endValue, defaultValue} = this.props;
    let currValue;
    if (defaultValue == null) {
      if (typeof endValue === 'function') {
        // TODO: provide perf tip here when endValue argument count is 0
        // (meaning you could have passed an obj)
        currValue = endValue();
      } else {
        currValue = endValue;
      }
    } else {
      currValue = defaultValue;
    }
    return {
      currValue: currValue,
      currVelocity: mapTree(zero, currValue),
    };
  },

  componentDidMount() {
    this.animationStep = animationStep.bind(null, false, () => this.stopAnimation(), () => this.props);
    this.startAnimating();
  },

  componentWillReceiveProps() {
    this.startAnimating();
  },

  stopAnimation: null,

  // used in animationRender
  hasUnmounted: false,

  animationStep: null,

  componentWillUnmount() {
    this.stopAnimation();
    this.hasUnmounted = true;
  },

  startAnimating() {
    // Is smart enough to not start it twice
    this.stopAnimation = startAnimation(
      this.state,
      this.animationStep,
      this.animationRender,
    );
  },

  animationRender(alpha, nextState, prevState) {
    // `this.hasUnmounted` might be true in the following condition:
    // user does some checks in `endValue` and calls an owner handler
    // owner sets state in the callback, triggering a re-render
    // re-render unmounts the Spring
    if (!this.hasUnmounted) {
      this.setState({
        currValue: interpolateValue(alpha, nextState.currValue, prevState.currValue),
        currVelocity: nextState.currVelocity,
      });
    }
  },

  render() {
    const renderedChildren = this.props.children(this.state.currValue);
    return renderedChildren && React.Children.only(renderedChildren);
  },
});

export const TransitionSpring = React.createClass({
  propTypes: {
    defaultValue: PropTypes.objectOf(PropTypes.any),
    endValue: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.objectOf(PropTypes.any.isRequired),
      // PropTypes.arrayOf(PropTypes.shape({
      //   key: PropTypes.any.isRequired,
      // })),
      // PropTypes.arrayOf(PropTypes.element),
    ]).isRequired,
    willLeave: PropTypes.oneOfType([
      PropTypes.func,
      // PropTypes.object,
      // PropTypes.array,
    ]),
    willEnter: PropTypes.oneOfType([
      PropTypes.func,
      // PropTypes.object,
      // PropTypes.array,
    ]),
    children: PropTypes.func.isRequired,
  },

  getDefaultProps() {
    return {
      willEnter: (key, value) => value,
      willLeave: () => null,
    };
  },

  getInitialState() {
    const {endValue, defaultValue} = this.props;
    let currValue;
    if (defaultValue == null) {
      if (typeof endValue === 'function') {
        currValue = endValue();
      } else {
        currValue = endValue;
      }
    } else {
      currValue = defaultValue;
    }
    return {
      currValue: currValue,
      currVelocity: mapTree(zero, currValue),
    };
  },

  componentDidMount() {
    this.animationStep = animationStep.bind(null, true, () => this.stopAnimation(), () => this.props);
    this.startAnimating();
  },

  componentWillReceiveProps() {
    this.startAnimating();
  },

  stopAnimation: null,

  // used in animationRender
  hasUnmounted: false,

  animationStep: null,

  componentWillUnmount() {
    this.stopAnimation();
  },

  startAnimating() {
    this.stopAnimation = startAnimation(
      this.state,
      this.animationStep,
      this.animationRender,
    );
  },

  animationRender(alpha, nextState, prevState) {
    // See comment in Spring.
    if (!this.hasUnmounted) {
      this.setState({
        currValue: interpolateValue(alpha, nextState.currValue, prevState.currValue),
        currVelocity: nextState.currVelocity,
      });
    }
  },

  render() {
    const renderedChildren = this.props.children(this.state.currValue);
    return renderedChildren && React.Children.only(renderedChildren);
  },
});
<commit_msg>react-native 'compatibility'<commit_after>
let React;
try {
  React = require('react-native');
} catch(e) {
  React = require('react');
}

let {PropTypes} = React;

import mapTree from './mapTree';
import noVelocity from './noVelocity';
import compareTrees from './compareTrees';
import mergeDiff from './mergeDiff';
import configAnimation from './animationLoop';
import zero from './zero';
import {interpolateValue, updateCurrValue, updateCurrVelocity} from './updateTree';

const startAnimation = configAnimation();

function animationStep(shouldMerge, stopAnimation, getProps, timestep, state) {
  let {currValue, currVelocity} = state;
  let {willEnter, willLeave, endValue} = getProps();

  if (typeof endValue === 'function') {
    endValue = endValue(currValue);
  }

  let mergedValue = endValue; // set mergedValue to endValue as the default
  let hasNewKey = false;

  if (shouldMerge) {
    mergedValue = mergeDiff(
      currValue,
      endValue,
      // TODO: stop allocating like crazy in this whole code path
      key => {
        const res = willLeave(key, currValue[key], endValue, currValue, currVelocity);
        if (res == null) {
          // For legacy reason. We won't allow returning null soon
          // TODO: remove, after next release
          return null;
        }

        if (noVelocity(currVelocity[key]) && compareTrees(currValue[key], res)) {
          return null;
        }
        return res;
      }
    );

    Object.keys(mergedValue)
      .filter(key => !currValue.hasOwnProperty(key))
      .forEach(key => {
        hasNewKey = true;
        const enterValue = willEnter(key, mergedValue[key], endValue, currValue, currVelocity);
        currValue[key] = enterValue;
        mergedValue[key] = enterValue;
        currVelocity[key] = mapTree(zero, currValue[key]);
      });
  }

  const newCurrValue = updateCurrValue(timestep, currValue, currVelocity, mergedValue);
  const newCurrVelocity = updateCurrVelocity(timestep, currValue, currVelocity, mergedValue);

  if (!hasNewKey && noVelocity(currVelocity) && noVelocity(newCurrVelocity)) {
    // check explanation in `Spring.animationRender`
    stopAnimation(); // Nasty side effects....
  }

  return {
    currValue: newCurrValue,
    currVelocity: newCurrVelocity,
  };
}

export const Spring = React.createClass({
  propTypes: {
    defaultValue: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.array,
    ]),
    endValue: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.object,
      PropTypes.array,
    ]).isRequired,
    children: PropTypes.func.isRequired,
  },

  getInitialState() {
    const {endValue, defaultValue} = this.props;
    let currValue;
    if (defaultValue == null) {
      if (typeof endValue === 'function') {
        // TODO: provide perf tip here when endValue argument count is 0
        // (meaning you could have passed an obj)
        currValue = endValue();
      } else {
        currValue = endValue;
      }
    } else {
      currValue = defaultValue;
    }
    return {
      currValue: currValue,
      currVelocity: mapTree(zero, currValue),
    };
  },

  componentDidMount() {
    this.animationStep = animationStep.bind(null, false, () => this.stopAnimation(), () => this.props);
    this.startAnimating();
  },

  componentWillReceiveProps() {
    this.startAnimating();
  },

  stopAnimation: null,

  // used in animationRender
  hasUnmounted: false,

  animationStep: null,

  componentWillUnmount() {
    this.stopAnimation();
    this.hasUnmounted = true;
  },

  startAnimating() {
    // Is smart enough to not start it twice
    this.stopAnimation = startAnimation(
      this.state,
      this.animationStep,
      this.animationRender,
    );
  },

  animationRender(alpha, nextState, prevState) {
    // `this.hasUnmounted` might be true in the following condition:
    // user does some checks in `endValue` and calls an owner handler
    // owner sets state in the callback, triggering a re-render
    // re-render unmounts the Spring
    if (!this.hasUnmounted) {
      this.setState({
        currValue: interpolateValue(alpha, nextState.currValue, prevState.currValue),
        currVelocity: nextState.currVelocity,
      });
    }
  },

  render() {
    const renderedChildren = this.props.children(this.state.currValue);
    return renderedChildren && React.Children.only(renderedChildren);
  },
});

export const TransitionSpring = React.createClass({
  propTypes: {
    defaultValue: PropTypes.objectOf(PropTypes.any),
    endValue: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.objectOf(PropTypes.any.isRequired),
      // PropTypes.arrayOf(PropTypes.shape({
      //   key: PropTypes.any.isRequired,
      // })),
      // PropTypes.arrayOf(PropTypes.element),
    ]).isRequired,
    willLeave: PropTypes.oneOfType([
      PropTypes.func,
      // PropTypes.object,
      // PropTypes.array,
    ]),
    willEnter: PropTypes.oneOfType([
      PropTypes.func,
      // PropTypes.object,
      // PropTypes.array,
    ]),
    children: PropTypes.func.isRequired,
  },

  getDefaultProps() {
    return {
      willEnter: (key, value) => value,
      willLeave: () => null,
    };
  },

  getInitialState() {
    const {endValue, defaultValue} = this.props;
    let currValue;
    if (defaultValue == null) {
      if (typeof endValue === 'function') {
        currValue = endValue();
      } else {
        currValue = endValue;
      }
    } else {
      currValue = defaultValue;
    }
    return {
      currValue: currValue,
      currVelocity: mapTree(zero, currValue),
    };
  },

  componentDidMount() {
    this.animationStep = animationStep.bind(null, true, () => this.stopAnimation(), () => this.props);
    this.startAnimating();
  },

  componentWillReceiveProps() {
    this.startAnimating();
  },

  stopAnimation: null,

  // used in animationRender
  hasUnmounted: false,

  animationStep: null,

  componentWillUnmount() {
    this.stopAnimation();
  },

  startAnimating() {
    this.stopAnimation = startAnimation(
      this.state,
      this.animationStep,
      this.animationRender,
    );
  },

  animationRender(alpha, nextState, prevState) {
    // See comment in Spring.
    if (!this.hasUnmounted) {
      this.setState({
        currValue: interpolateValue(alpha, nextState.currValue, prevState.currValue),
        currVelocity: nextState.currVelocity,
      });
    }
  },

  render() {
    const renderedChildren = this.props.children(this.state.currValue);
    return renderedChildren && React.Children.only(renderedChildren);
  },
});
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';

const Sprite = ({ filename, x, y, width, height }) => {
  if (!filename) {
    return null;
  }

  const style = {
    backgroundImage: `url(${filename})`,
    backgroundPosition: `${x * (-1)}px ${y * (-1)}px`,
    width,
    height,
  };

  return
;
};

Sprite.defaultProps = {
  x: 0,
  y: 0,
  width: 0,
  height: 0,
};

Sprite.propTypes = {
  filename: PropTypes.string,
  x: PropTypes.number,
  y: PropTypes.number,
  width: PropTypes.number,
  height: PropTypes.number,
};

export default Sprite;
<commit_msg>Make background image on no-repeat (#4)<commit_after>import React, { PropTypes } from 'react';

const Sprite = ({ filename, x, y, width, height }) => {
  if (!filename) {
    return null;
  }

  const style = {
    backgroundImage: `url(${filename})`,
    backgroundPosition: `${x * (-1)}px ${y * (-1)}px`,
    backgroundRepeat: 'no-repeat',
    width,
    height,
  };

  return
;
};

Sprite.defaultProps = {
  x: 0,
  y: 0,
  width: 0,
  height: 0,
};

Sprite.propTypes = {
  filename: PropTypes.string,
  x: PropTypes.number,
  y: PropTypes.number,
  width: PropTypes.number,
  height: PropTypes.number,
};

export default Sprite;
<|endoftext|>


<commit_before>const ANT = 0;
const BIRD = 1;
const GRAPE = 2;
const ELEPHANT = 3;
const BEE = 4;
const BUTTERFLY = 5;
const FROG = 6;
const OWL = 7;
const NUM_ANIMALS = 8;

var index = -1;
var animalArr = [];
var difficulty = 1;
var numIncorrectClicks = 0;
var soundToggle = "False";
var musicToggle = "False";
var endGameFuncCalls = 0;

/**
* Shuffles array in place.
* @param {Array} a items The array containing the items.
*/
function Shuffle(a) {
    var j, x, i;
    for (i = a.length; i; i--) {
        j = Math.floor(Math.random() * i);
        x = a[i - 1];
        a[i - 1] = a[j];
        a[j] = x;
    }
}

function MakeNotClickable() {
    document.getElementById("antContainer").onclick = null;
    document.getElementById("birdContainer").onclick = null;
    document.getElementById("grapeContainer").onclick = null;
    document.getElementById("elephantContainer").onclick = null;

    document.getElementById("beeContainer").onclick = null;
    document.getElementById("butterflyContainer").onclick = null;
    document.getElementById("frogContainer").onclick = null;
    document.getElementById("owlContainer").onclick = null;
}

function MakeClickable() {
    document.getElementById("antContainer").onclick = function () { Clicked(0); };
    document.getElementById("birdContainer").onclick = function () { Clicked(1); };
    document.getElementById("grapeContainer").onclick = function () { Clicked(2); };
    document.getElementById("elephantContainer").onclick = function () { Clicked(3); };

    document.getElementById("beeContainer").onclick = function () { Clicked(4); };
    document.getElementById("butterflyContainer").onclick = function () { Clicked(5); };
    document.getElementById("frogContainer").onclick = function () { Clicked(6); };
    document.getElementById("owlContainer").onclick = function () { Clicked(7); };
}

function Clicked(clickedAnimal) {
    var soundEffect;

    if (clickedAnimal === animalArr[index]) {

        if (soundToggle === "False") {
            soundEffect = new WebAudioAPISound("../../Audio/soundEffects/airPlaneDing.mp3");
            soundEffect.play(soundEffect);
        }

        setTimeout(function () {
            if (difficulty <= 1)
                GameLoopEasy();
            else
                GameLoopHard();
        }, 950);
    }
    else {
        if (soundToggle === "False") {
            //make the clank sound effect
            soundEffect = new WebAudioAPISound("../../Audio/soundEffects/metallicClank.mp3");
            soundEffect.play(soundEffect);
        }

        numIncorrectClicks++;

        //if the user incorrectly clicked 3 times, resay
        //what they are spying for
        if (numIncorrectClicks === 3) {
            index--;

            numIncorrectClicks = 0;

            setTimeout(function () {
                if (difficulty <= 1) {
                    GameLoopEasy();
                }
                else {
                    GameLoopHard();
                }
            }, 1000);
        }
    }

    setTimeout(function () {
        switch (clickedAnimal) {
            case ANT:
                responsiveVoice.speak("Ant.", "US English Female");
                break;
            case BIRD:
                responsiveVoice.speak("Bird.", "US English Female");
                break;
            case GRAPE:
                responsiveVoice.speak("Grape.", "US English Female");
                break;
            case ELEPHANT:
                responsiveVoice.speak("Elephant.", "US English Female");
                break;
            case BEE:
                responsiveVoice.speak("Bee.", "US English Female");
                break;
            case BUTTERFLY:
                responsiveVoice.speak("Butterfly.", "US English Female");
                break;
            case FROG:
                responsiveVoice.speak("Frog.", "US English Female");
                break;
            default:
                responsiveVoice.speak("Owl.", "US English Female");
        }
    }, 100);
}

function GameLoopHard() {
    MakeNotClickable();

    index++;

    if (index < 8) {
        switch (animalArr[index]) {
            case ANT:
                responsiveVoice.speak("I spy something red with antennas.", "US English Female");
                break;
            case BIRD:
                responsiveVoice.speak("I spy something blue with small eyes.", "US English Female");
                break;
            case GRAPE:
                responsiveVoice.speak("I spy something purple.", "US English Female");
                break;
            case ELEPHANT:
                responsiveVoice.speak("I spy something green with a trunk.", "US English Female");
                break;
            case BEE:
                responsiveVoice.speak("I spy something yellow that flys and can sting people.", "US English Female");
                break;
            case BUTTERFLY:
                responsiveVoice.speak("I spy something red that can fly.", "US English Female");
                break;
            case FROG:
                responsiveVoice.speak("I spy something green that leaps.", "US English Female");
                break;
            default:
                responsiveVoice.speak("I spy something blue that has big eyes.", "US English Female");
        }

        setTimeout(function () {
            MakeClickable();
        }, 2000);
    }
    else {
        //end the game
        EndGame();
    }
}

function GameLoopEasy() {
    MakeNotClickable();
    index++;

    if (index < 8) {
        switch (animalArr[index]) {
            case ANT:
                responsiveVoice.speak("I spy a red ant.", "US English Female");
                break;
            case BIRD:
                responsiveVoice.speak("I spy a blue bird with small eyes", "US English Female");
                break;
            case GRAPE:
                responsiveVoice.speak("I spy purple grapes.", "US English Female");
                break;
            case ELEPHANT:
                responsiveVoice.speak("I spy a green elephant.", "US English Female");
                break;
            case BEE:
                responsiveVoice.speak("I spy a yellow bee.", "US English Female");
                break;
            case BUTTERFLY:
                responsiveVoice.speak("I spy a red butterfly.", "US English Female");
                break;
            case FROG:
                responsiveVoice.speak("I spy a green frog.", "US English Female");
                break;
            default:
                responsiveVoice.speak("I spy a blue owl with big eyes.", "US English Female");
        }

        setTimeout(function () {
            MakeClickable();
        }, 2000);

    }
    else {
        //end the game
        EndGame();
    }
}

function CreateHtmlElemets() {
    var imgSrc = "../../MiniGames/ISpyGame/img/bg1.jpeg";

    //set up the CSS file needed for this minigame
    var fileRef = document.createElement("link");
    fileRef.setAttribute("rel", "stylesheet");
    fileRef.setAttribute("type", "text/css");

    fileRef.setAttribute("href", "../../MiniGames/ISpyGame/css/ISpy.css");
    //add the css file to the html
    document.getElementsByTagName("head")[0].appendChild(fileRef);

    //the container that will hold the game
    var divContainer = document.getElementById("BlocksGame");

    var bgContainer = document.createElement("div");
    bgContainer.setAttribute("id", "bgContainer");

    //the header
    var header = document.createElement("h1");
    header.setAttribute("id", "header");
    header.innerHTML = "i Spy!";

    bgContainer.appendChild(header);

    var bg = document.createElement("img");
    bg.setAttribute("id", "bg");
    bg.setAttribute("class", "imgs");
    bg.setAttribute("src", imgSrc);

    imgSrc = "../../MiniGames/ColorSortingGame/img/";

    bgContainer.appendChild(bg);

    //add the i spy animals
    var antContainer = document.createElement("div");
    antContainer.setAttribute("id", "antContainer");
    antContainer.setAttribute("onclick", "Clicked(0)");
    antContainer.setAttribute("class", "containers");

    var img1 = document.createElement("img");
    img1.setAttribute("class", "imgs");
    img1.setAttribute("src", imgSrc + "red/ant.png");

    antContainer.appendChild(img1);

    bgContainer.appendChild(antContainer);

    var birdContainer = document.createElement("div");
    birdContainer.setAttribute("id", "birdContainer");
    birdContainer.setAttribute("onclick", "Clicked(1)");
    birdContainer.setAttribute("class", "containers");

    var img2 = document.createElement("img");
    img2.setAttribute("class", "imgs");
    img2.setAttribute("src", imgSrc + "blue/bird.png");

    birdContainer.appendChild(img2);

    bgContainer.appendChild(birdContainer);

    var grapeContainer = document.createElement("div");
    grapeContainer.setAttribute("id", "grapeContainer");
    grapeContainer.setAttribute("onclick", "Clicked(2)");
    grapeContainer.setAttribute("class", "containers");

    var img3 = document.createElement("img");
    img3.setAttribute("class", "imgs");
    img3.setAttribute("src", imgSrc + "purple/grapes.png");

    grapeContainer.appendChild(img3);

    bgContainer.appendChild(grapeContainer);

    var elephantContainer = document.createElement("div");
    elephantContainer.setAttribute("id", "elephantContainer");
    elephantContainer.setAttribute("onclick", "Clicked(3)");
    elephantContainer.setAttribute("class", "containers");

    var img4 = document.createElement("img");
    img4.setAttribute("class", "imgs");
    img4.setAttribute("src", imgSrc + "green/elephant.png");

    elephantContainer.appendChild(img4);

    bgContainer.appendChild(elephantContainer);

    var beeContainer = document.createElement("div");
    beeContainer.setAttribute("id", "beeContainer");
    beeContainer.setAttribute("onclick", "Clicked(4)");
    beeContainer.setAttribute("class", "containers");

    var img5 = document.createElement("img");
    img5.setAttribute("class", "imgs");
    img5.setAttribute("src", imgSrc + "yellow/bee.png");

    beeContainer.appendChild(img5);

    bgContainer.appendChild(beeContainer);

    var butterflyContainer = document.createElement("div");
    butterflyContainer.setAttribute("id", "butterflyContainer");
    butterflyContainer.setAttribute("onclick", "Clicked(5)");
    butterflyContainer.setAttribute("class", "containers");

    var img6 = document.createElement("img");
    img6.setAttribute("class", "imgs");
    img6.setAttribute("src", imgSrc + "red/butterfly.png");

    butterflyContainer.appendChild(img6);

    bgContainer.appendChild(butterflyContainer);

    var frogContainer = document.createElement("div");
    frogContainer.setAttribute("id", "frogContainer");
    frogContainer.setAttribute("onclick", "Clicked(6)");
    frogContainer.setAttribute("class", "containers");

    var img7 = document.createElement("img");
    img7.setAttribute("class", "imgs");
    img7.setAttribute("src", imgSrc + "green/frog.png");

    frogContainer.appendChild(img7);

    bgContainer.appendChild(frogContainer);

    var owlContainer = document.createElement("div");
    owlContainer.setAttribute("id", "owlContainer");
    owlContainer.setAttribute("onclick", "Clicked(7)");
    owlContainer.setAttribute("class", "containers");

    var img8 = document.createElement("img");
    img8.setAttribute("class", "imgs");
    img8.setAttribute("src", imgSrc + "blue/owl.png");

    owlContainer.appendChild(img8);

    bgContainer.appendChild(owlContainer);

    divContainer.appendChild(bgContainer);
}

function EndGame() {
    MakeNotClickable();

    endGameFuncCalls++;

    var returnVal = null;

    //makes suure that great job is only said once
    if (endGameFuncCalls === 1)
        responsiveVoice.speak("Great job!", "US English Female");

    if (index === 0)
        returnVal = -5;
    else if (index === 1)
        returnVal = -3;
    else if (index === 2)
        returnVal = -2;
    else if (index === 3)
        returnVal = -1;
    else if (index === 4)
        returnVal = 0;
    else if (index === 5)
        returnVal = 2;
    else if (index === 6)
        returnVal = 3;
    else if (index === 7)
        returnVal = 4;
    else
        returnVal = 5;

    document.getElementById("score").value = returnVal;
    EndofGame(); //function displays good job message and returns to map
}

function Main() {
    var backgroundMusic = new WebAudioAPISound("../../Audio/backgroundMusic/bgSound6(africa).mp3", { loop: true });
    backgroundMusic.setVolume(10);

    difficulty = document.getElementById("minigameScript").getAttribute("difficulty");
    soundToggle = document.getElementById("minigameScript").getAttribute("toggleSound"); //True = sound off, False = sound on
    musicToggle = document.getElementById("minigameScript").getAttribute("toggleMusic");

    CreateHtmlElemets();

    if (musicToggle === "False") {
        //play background music
        backgroundMusic.play(backgroundMusic);
    }

    //if the user leaves the page
    window.onbeforeunload = function () {
        responsiveVoice.cancel(); //quit doing text to speech
        return null;
    };

    window.onload = function () {
        //play the instructions
        responsiveVoice.OnVoiceReady = function () {
            Shuffle(animalArr);

            responsiveVoice.speak("Click the thing you are spying for.", "US English Female");

            setTimeout(function () {
                if (difficulty <= 1) {
                    GameLoopEasy();
                }
                else {
                    GameLoopHard();
                }
            }, 4000);
        };
    };

    for (var i = 0; i < NUM_ANIMALS; i++) {
        animalArr.push(i);
    }

    //if the user didn't finish the game in 2 minuets
    setTimeout(function () {
        //end the game
        EndGame();
    }, 120000);
}

Main();<commit_msg>Fixed iSpy for real this time.<commit_after>const ANT = 0;
const BIRD = 1;
const GRAPE = 2;
const ELEPHANT = 3;
const BEE = 4;
const BUTTERFLY = 5;
const FROG = 6;
const OWL = 7;
const NUM_ANIMALS = 8;

var index = -1;
var animalArr = [];
var difficulty = 1;
var numIncorrectClicks = 0;
var soundToggle = "False";
var musicToggle = "False";
var endGameFuncCalls = 0;

/**
* Shuffles array in place.
* @param {Array} a items The array containing the items.
*/
function Shuffle(a) {
    var j, x, i;
    for (i = a.length; i; i--) {
        j = Math.floor(Math.random() * i);
        x = a[i - 1];
        a[i - 1] = a[j];
        a[j] = x;
    }
}

function MakeNotClickable() {
    document.getElementById("antContainer").onclick = null;
    document.getElementById("birdContainer").onclick = null;
    document.getElementById("grapeContainer").onclick = null;
    document.getElementById("elephantContainer").onclick = null;

    document.getElementById("beeContainer").onclick = null;
    document.getElementById("butterflyContainer").onclick = null;
    document.getElementById("frogContainer").onclick = null;
    document.getElementById("owlContainer").onclick = null;
}

function MakeClickable() {
    document.getElementById("antContainer").onclick = function () { Clicked(0); };
    document.getElementById("birdContainer").onclick = function () { Clicked(1); };
    document.getElementById("grapeContainer").onclick = function () { Clicked(2); };
    document.getElementById("elephantContainer").onclick = function () { Clicked(3); };

    document.getElementById("beeContainer").onclick = function () { Clicked(4); };
    document.getElementById("butterflyContainer").onclick = function () { Clicked(5); };
    document.getElementById("frogContainer").onclick = function () { Clicked(6); };
    document.getElementById("owlContainer").onclick = function () { Clicked(7); };
}

function Clicked(clickedAnimal) {
    var soundEffect;

    if (clickedAnimal === animalArr[index]) {

        if (soundToggle === "False") {
            soundEffect = new WebAudioAPISound("../../Audio/soundEffects/airPlaneDing.mp3");
            soundEffect.play(soundEffect);
        }

        setTimeout(function () {
            if (difficulty <= 1)
                GameLoopEasy();
            else
                GameLoopHard();
        }, 950);
    }
    else {
        if (soundToggle === "False") {
            //make the clank sound effect
            soundEffect = new WebAudioAPISound("../../Audio/soundEffects/metallicClank.mp3");
            soundEffect.play(soundEffect);
        }

        numIncorrectClicks++;

        //if the user incorrectly clicked 3 times, resay
        //what they are spying for
        if (numIncorrectClicks === 3) {
            index--;

            numIncorrectClicks = 0;

            setTimeout(function () {
                if (difficulty <= 1) {
                    GameLoopEasy();
                }
                else {
                    GameLoopHard();
                }
            }, 1000);
        }
    }

    setTimeout(function () {
        switch (clickedAnimal) {
            case ANT:
                responsiveVoice.speak("Ant.", "US English Female");
                break;
            case BIRD:
                responsiveVoice.speak("Bird.", "US English Female");
                break;
            case GRAPE:
                responsiveVoice.speak("Grape.", "US English Female");
                break;
            case ELEPHANT:
                responsiveVoice.speak("Elephant.", "US English Female");
                break;
            case BEE:
                responsiveVoice.speak("Bee.", "US English Female");
                break;
            case BUTTERFLY:
                responsiveVoice.speak("Butterfly.", "US English Female");
                break;
            case FROG:
                responsiveVoice.speak("Frog.", "US English Female");
                break;
            default:
                responsiveVoice.speak("Owl.", "US English Female");
        }
    }, 100);
}

function GameLoopHard() {
    MakeNotClickable();

    index++;

    if (index < 8) {
        switch (animalArr[index]) {
            case ANT:
                responsiveVoice.speak("I spy something red with antennas.", "US English Female");
                break;
            case BIRD:
                responsiveVoice.speak("I spy something blue with small eyes.", "US English Female");
                break;
            case GRAPE:
                responsiveVoice.speak("I spy something purple.", "US English Female");
                break;
            case ELEPHANT:
                responsiveVoice.speak("I spy something green with a trunk.", "US English Female");
                break;
            case BEE:
                responsiveVoice.speak("I spy something yellow that flys and can sting people.", "US English Female");
                break;
            case BUTTERFLY:
                responsiveVoice.speak("I spy something red that can fly.", "US English Female");
                break;
            case FROG:
                responsiveVoice.speak("I spy something green that leaps.", "US English Female");
                break;
            default:
                responsiveVoice.speak("I spy something blue that has big eyes.", "US English Female");
        }

        setTimeout(function () {
            MakeClickable();
        }, 2000);
    }
    else {
        //end the game
        EndGame();
    }
}

function GameLoopEasy() {
    MakeNotClickable();
    index++;

    if (index < 8) {
        switch (animalArr[index]) {
            case ANT:
                responsiveVoice.speak("I spy a red ant.", "US English Female");
                break;
            case BIRD:
                responsiveVoice.speak("I spy a blue bird with small eyes", "US English Female");
                break;
            case GRAPE:
                responsiveVoice.speak("I spy purple grapes.", "US English Female");
                break;
            case ELEPHANT:
                responsiveVoice.speak("I spy a green elephant.", "US English Female");
                break;
            case BEE:
                responsiveVoice.speak("I spy a yellow bee.", "US English Female");
                break;
            case BUTTERFLY:
                responsiveVoice.speak("I spy a red butterfly.", "US English Female");
                break;
            case FROG:
                responsiveVoice.speak("I spy a green frog.", "US English Female");
                break;
            default:
                responsiveVoice.speak("I spy a blue owl with big eyes.", "US English Female");
        }

        setTimeout(function () {
            MakeClickable();
        }, 2000);

    }
    else {
        //end the game
        EndGame();
    }
}

function CreateHtmlElemets() {
    var imgSrc = "../../MiniGames/ISpyGame/img/bg1.jpeg";

    //set up the CSS file needed for this minigame
    var fileRef = document.createElement("link");
    fileRef.setAttribute("rel", "stylesheet");
    fileRef.setAttribute("type", "text/css");

    fileRef.setAttribute("href", "../../MiniGames/ISpyGame/css/ISpy.css");
    //add the css file to the html
    document.getElementsByTagName("head")[0].appendChild(fileRef);

    //the container that will hold the game
    var divContainer = document.getElementById("BlocksGame");

    var bgContainer = document.createElement("div");
    bgContainer.setAttribute("id", "bgContainer");

    //the header
    var header = document.createElement("h1");
    header.setAttribute("id", "header");
    header.innerHTML = "i Spy!";

    bgContainer.appendChild(header);

    var bg = document.createElement("img");
    bg.setAttribute("id", "bg");
    bg.setAttribute("class", "imgs");
    bg.setAttribute("src", imgSrc);

    imgSrc = "../../MiniGames/ColorSortingGame/img/";

    bgContainer.appendChild(bg);

    //add the i spy animals
    var antContainer = document.createElement("div");
    antContainer.setAttribute("id", "antContainer");
    antContainer.setAttribute("onclick", "Clicked(0)");
    antContainer.setAttribute("class", "containers");

    var img1 = document.createElement("img");
    img1.setAttribute("class", "imgs");
    img1.setAttribute("src", imgSrc + "red/ant.png");

    antContainer.appendChild(img1);

    bgContainer.appendChild(antContainer);

    var birdContainer = document.createElement("div");
    birdContainer.setAttribute("id", "birdContainer");
    birdContainer.setAttribute("onclick", "Clicked(1)");
    birdContainer.setAttribute("class", "containers");

    var img2 = document.createElement("img");
    img2.setAttribute("class", "imgs");
    img2.setAttribute("src", imgSrc + "blue/bird.png");

    birdContainer.appendChild(img2);

    bgContainer.appendChild(birdContainer);

    var grapeContainer = document.createElement("div");
    grapeContainer.setAttribute("id", "grapeContainer");
    grapeContainer.setAttribute("onclick", "Clicked(2)");
    grapeContainer.setAttribute("class", "containers");

    var img3 = document.createElement("img");
    img3.setAttribute("class", "imgs");
    img3.setAttribute("src", imgSrc + "purple/grapes.png");

    grapeContainer.appendChild(img3);

    bgContainer.appendChild(grapeContainer);

    var elephantContainer = document.createElement("div");
    elephantContainer.setAttribute("id", "elephantContainer");
    elephantContainer.setAttribute("onclick", "Clicked(3)");
    elephantContainer.setAttribute("class", "containers");

    var img4 = document.createElement("img");
    img4.setAttribute("class", "imgs");
    img4.setAttribute("src", imgSrc + "green/elephant.png");

    elephantContainer.appendChild(img4);

    bgContainer.appendChild(elephantContainer);

    var beeContainer = document.createElement("div");
    beeContainer.setAttribute("id", "beeContainer");
    beeContainer.setAttribute("onclick", "Clicked(4)");
    beeContainer.setAttribute("class", "containers");

    var img5 = document.createElement("img");
    img5.setAttribute("class", "imgs");
    img5.setAttribute("src", imgSrc + "yellow/bee.png");

    beeContainer.appendChild(img5);

    bgContainer.appendChild(beeContainer);

    var butterflyContainer = document.createElement("div");
    butterflyContainer.setAttribute("id", "butterflyContainer");
    butterflyContainer.setAttribute("onclick", "Clicked(5)");
    butterflyContainer.setAttribute("class", "containers");

    var img6 = document.createElement("img");
    img6.setAttribute("class", "imgs");
    img6.setAttribute("src", imgSrc + "red/butterfly.png");

    butterflyContainer.appendChild(img6);

    bgContainer.appendChild(butterflyContainer);

    var frogContainer = document.createElement("div");
    frogContainer.setAttribute("id", "frogContainer");
    frogContainer.setAttribute("onclick", "Clicked(6)");
    frogContainer.setAttribute("class", "containers");

    var img7 = document.createElement("img");
    img7.setAttribute("class", "imgs");
    img7.setAttribute("src", imgSrc + "green/frog.png");

    frogContainer.appendChild(img7);

    bgContainer.appendChild(frogContainer);

    var owlContainer = document.createElement("div");
    owlContainer.setAttribute("id", "owlContainer");
    owlContainer.setAttribute("onclick", "Clicked(7)");
    owlContainer.setAttribute("class", "containers");

    var img8 = document.createElement("img");
    img8.setAttribute("class", "imgs");
    img8.setAttribute("src", imgSrc + "blue/owl.png");

    owlContainer.appendChild(img8);

    bgContainer.appendChild(owlContainer);

    divContainer.appendChild(bgContainer);
}

function EndGame() {
    MakeNotClickable();

    endGameFuncCalls++;

    var returnVal = null;

    //makes suure that great job is only said once
    if (endGameFuncCalls === 1)
        responsiveVoice.speak("Great job!", "US English Female");

    if (index === 0)
        returnVal = -5;
    else if (index === 1)
        returnVal = -3;
    else if (index === 2)
        returnVal = -2;
    else if (index === 3)
        returnVal = -1;
    else if (index === 4)
        returnVal = 0;
    else if (index === 5)
        returnVal = 2;
    else if (index === 6)
        returnVal = 3;
    else if (index === 7)
        returnVal = 4;
    else
        returnVal = 5;

    document.getElementById("score").value = returnVal;
    EndofGame(); //function displays good job message and returns to map
}

function Main() {
    var backgroundMusic = new WebAudioAPISound("../../Audio/backgroundMusic/bgSound6(africa).mp3", { loop: true });
    backgroundMusic.setVolume(10);

    difficulty = document.getElementById("minigameScript").getAttribute("difficulty");
    soundToggle = document.getElementById("minigameScript").getAttribute("toggleSound"); //True = sound off, False = sound on
    musicToggle = document.getElementById("minigameScript").getAttribute("toggleMusic");

    CreateHtmlElemets();

    if (musicToggle === "False") {
        //play background music
        backgroundMusic.play(backgroundMusic);
    }

    //if the user leaves the page
    window.onbeforeunload = function () {
        responsiveVoice.cancel(); //quit doing text to speech
        return null;
    };

    window.onload = function () {
        //play the instructions
        Shuffle(animalArr);

        responsiveVoice.speak("Click the thing you are spying for.", "US English Female");

        setTimeout(function () {
            if (difficulty <= 1) {
                GameLoopEasy();
            }
            else {
                GameLoopHard();
            }
        }, 4000);
    };

    for (var i = 0; i < NUM_ANIMALS; i++) {
        animalArr.push(i);
    }

    //if the user didn't finish the game in 2 minuets
    setTimeout(function () {
        //end the game
        EndGame();
    }, 120000);
}

Main();<|endoftext|>


<commit_before>import Wire from './Wire.js';
import Brick from './Brick.js';
import defaultBlocks from './blocks.js'
import { getParentSvg } from './utils.js';

export default class Sticky {
  constructor(id, { width, height } = { width: 800, height: 600 }) {
    this.el = document.getElementById(id);

    if (!this.el)
      throw "Couldn't find element :(";

    this._uid = 0;
    this._aux = {};
    this.blocks = {};
    this._objects = [];
    this._wires = [];
    this._state = null;

    const svg = Sticky.createElement('svg', { class: 'svg-content', preserveAspectRatio: "xMidYMid meet" });
    this._svg = svg;
    this.el.appendChild(this._svg);
    this.matchViewBox();

    // this.registerBlock('actuator', ActuatorBrick);

    this.clearCanvas();
    this.setCanvasSize({ width, height });

    this.colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
                   "#2E2C75", "#673A7E", "#CC0071", "#F80120",
                   "#F35B20", "#FB9A00", "#FFCC00", "#FEF200"];

    // DOM Events
    svg.addEventListener('mousedown', e => {
      normalizeEvent(e);
      this.lastSelected = null;
      const { target } = e;

      if (target.type === 'wire') {
        this.lastSelected = target.wrapper;
        return this.selectedWire = target.wrapper;
      }

      if (target.type === 'port' && target.dir === 'out') {
        return this.startAttach(target);
      }

      const parentSvg = getParentSvg(target);
      const shouldCapture = tagName => !['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(tagName);
      if (shouldCapture(target.tagName) && parentSvg && parentSvg.type == 'block') {
        const blockNode = parentSvg;
        console.debug('Block selected:', blockNode, 'Triggered by: ', target);
        var wrapper = blockNode.wrapper;
        this.lastSelected = wrapper;
        this.dragging = blockNode;
        var SVGbox = wrapper._svg.getBoundingClientRect();
        const offset = {
          x: e.x - SVGbox.left,
          y: e.y - SVGbox.top,
        };
        this._aux.mouseDown = { x: offset.x - wrapper.x, y: offset.y - wrapper.y };
        this._svg.appendChild(this.dragging);
        wrapper.wires.forEach(wire => this._svg.appendChild(wire._el));
      }

    }, false);

    document.addEventListener('keydown', e => {
      if (e.keyCode == 46) {
        console.debug('deleting', this.lastSelected);
        this.lastSelected.delete();
        // @TODO should remove from state (brick, wire, port)
      }
    }, false);

    svg.addEventListener('mouseup', e => {
      this.dragging = null;
      if(e.target.type === 'port')
        return this.endAttach(e.target);

      if(this.isState('attaching')) {
        this.setState(null)
        svg.removeChild(this._aux['wire']._el);
      }
    });

    svg.addEventListener('mousemove', e => {
      normalizeEvent(e);

      return this.attachMove(e);
    });

    svg.addEventListener('mousemove', e => {
      normalizeEvent(e);

      if (this.dragging) {
        var wrapper = this.dragging.wrapper;
        var SVGbox = wrapper._svg.getBoundingClientRect();
        var OffsetX = e.x - SVGbox.left;
        var OffsetY =  e.y - SVGbox.top;

        var firstState = this._aux.mouseDown;
        wrapper.x = OffsetX - firstState.x;
        wrapper.y = OffsetY - firstState.y;
        wrapper.updateWires();
      }
    });

    return this;
  }

  setCanvasSize({ width, height }) {
    this._svg.style.width = width;
    this._svg.style.height = height;
    this._svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
  }

//static methods
  static createElement(name, attrs) {
    var el = document.createElementNS('http://www.w3.org/2000/svg', name);

    for (let key in attrs)
      el.setAttribute(key, attrs[key]);

    return el;
  }

  matchViewBox() {
    const { width, height } = this._svg.getBoundingClientRect();

    this._svg.setAttribute('viewBox', `0, 0, ${width} ${height}`);
  }
  Brick(name, attrs) {
    var el = Sticky.createElement(name, attrs);
    var brick = new Brick(el, this);
    brick._id = this._uid++;
    this._objects.push(brick);

    return brick;
  }
  addObj(obj) {
    if (obj._id == null) obj._id = this._uid++;
    this._objects.push(obj);
    this.addElement(obj._el);
  }
  removeObj(obj, update) {
    let index = this._objects.indexOf(obj);
    if (index == -1) return;

    for (let port_type of Object.keys(obj._ports)) {
      if (obj._ports[port_type].length) { // if there's any connection
        for (let port of obj._ports[port_type]) {
          // port.dettach();
        }
      }
    }

    for (let wire of [...obj.wires]) {
      wire.delete();
    }

    this.removeElement(obj._el);
    if (update)
      // should splice wires too
      return this._objects.splice(index, 1);
  }
  addElement(el) {
    this._svg.appendChild(el);
  }
  removeElement(el) {
    if (this._svg.contains(el))
      this._svg.removeChild(el);
  }
  startAttach(port) {
    let wire = new Wire(port.wrapper);
    wire._inverted = port.wrapper.dir === 'in';
    this.setState('attaching');
    this._aux['wire'] = wire;
    this.addElement(wire._el);
  }
  startDrag(port) {
    this.setState('attaching');
    this._aux['wire'] = wire;
    this.addElement(wire._el);
  }
  endAttach(port) {
    if (this.isState('attaching')) {
      this.setState(null);
      var wire = this._aux['wire'];
      wire._cp2 = port.wrapper;

      if (wire.seal()) {
        wire.render();
        this._wires.push(wire);
      } else {
        this.removeElement(wire._el);
      }
      delete this._aux['wire'];
    }
  }
  setState(state) {
    return this._state = state;
  }
  isState(state) {
    return this._state === state;
  }
  attachMove(mouse) {
    if (this.isState('attaching')) {
      var wire = this._aux['wire'];
      var SVGbox = this._svg.getBoundingClientRect();
      //(below) pixel for removing the wire from the way so we can detect the event on port
      var offset = wire._inverted ? 4 : -4;
      var mouse = { x: mouse.x - SVGbox.left + offset, y: mouse.y - SVGbox.top };
      var port = wire._cp1.getPoint();

      wire._render(port, mouse, wire._inverted);
    }
  }
  clearCanvas(start = true) {
    for (let obj of this._objects) {
      // this.removeElement(obj._el);
      this.removeObj(obj, false);
    }

    this._objects = [];
    this._wires = [];

    if (!start) return;

    const startBlock = this.createBlock('start');
    startBlock.x = 10; startBlock.y = this._svg.getAttribute('height')/2;
    startBlock.behavior = () => 0;

    this.addObj(startBlock);
  }
  registerBlock(name, obj) {
    this.blocks[name] = {
      ...obj,
      id: name,
      behavior: typeof obj.behavior !== 'function' ? new Function('findById', obj.behavior) : obj.behavior
    }
  }
  static registerBlock(name, obj) {
    this.prototype.__blocks[name] = {
      ...obj,
      id: name,
      behavior: typeof obj.behavior !== 'function' ? new Function('findById', obj.behavior) : obj.behavior
    }
  }
  createBlock(name, data = {}) {
    const cfg = this.blocks[name] || this.__blocks[name];

    if (!cfg) throw `Block '${name}' not registered`;

    return new Brick({ ...cfg, ...data });
  }
  static createBlock(name, data = {}) {
    const cfg = this.prototype.__blocks[name];

    if (!cfg) throw "Block not registered";

    return new Brick({ ...cfg, ...data });
  }
  findById(id) {
    if (id == undefined) return null;
    for (let obj of this._objects) {
      if (obj._id === id) return obj;
    }
  }
  toJSON() {
    var fluxgram = this._objects.map(obj => {
      let object = {
        refBlock: obj._refBlock,
        inputs: obj.inputs,
        id: obj._id,
        x: obj.x,
        y: obj.y,
        ports: {} // flow_in, flow_out, in, out
      };

      for (let type in obj._ports) {
        console.log(type, obj._ports[type]);
        object.ports[type] = obj._ports[type].map(port => [...port._conn]);
      }

      return object;
    });

    let refBlock = Object.entries(this.__blocks).map(
      ([blockId, block]) => ({
        ...block,
        behavior: block.behavior.toString(),
      })
    );

    return { refBlock, fluxgram };
  }
  loadJSON(data) {
    this.clearCanvas(false);

    for (let block of data) {
      let refBlock = this.__blocks[block.refBlock];
      let obj = this.createBlock(block.refBlock, refBlock);
      obj.x = block.x;
      obj.y = block.y;
      obj.value = block.value;
      obj._id = block.id;
      console.log(block.id, obj._id);
      this.addObj(obj);
    }

    // load wires
    // PLEASE REFACTORATE ME

    for (let block of data) {
      let blocky = this.findById(block.id);
      console.log(blocky, block.id);

      for (let port of block.ports.out) {
        if (!port.length) {
          console.log('end of flux', block);
          break;
        }
        for (let conn of port) {
          let blocky2 = this.findById(conn.brick);
          let wire = new Wire(blocky._ports['out'][0], blocky2._ports['in'][conn.id]);
          this.addElement(wire._el);
          if (wire.seal()) {
            wire.render();
            this._wires.push(wire);
          } else {
            this.removeElement(wire._el);
          }
        }
      }

      for (let port of block.ports.flow_out) {
        if (!port.length) {
          console.log('end of flux', block);
          break;
        }
        for (let conn of port) {
          let blocky2 = this.findById(conn.brick);
          let wire = new Wire(blocky._ports['flow_out'][0], blocky2._ports['flow_in'][0]);
          this.addElement(wire._el);

          if (wire.seal()) {
            wire.render();
            this._wires.push(wire);
          } else {
            this.removeElement(wire._el);
          }
        }
      }

    }
  }
  reload () {
    const flow = this.toJSON().fluxgram;
    this.clearCanvas(true);
    this.loadJSON(flow);
  }
  run() {
    let flow, id, refBlock;
    let block = this._objects.find(({ _refBlock }) => _refBlock == 'start');

    if (!block) {
      console.warning('Start block not found');
      return false;
    }

    console.debug('Start block found:', block);

    // flow = start.behavior();
    // an ActuatorBrick should return the flow_out port id
    // it'll be useful for if block

    do {
      refBlock = this.blocks[block._refBlock] || this.__blocks[block._refBlock];
      flow = refBlock.behavior.call(block, this.findById.bind(this));
      // console.log(block._ports);
      id = (block._ports['flow_out'][flow]._conn[0]) ?
            (block._ports['flow_out'][flow]._conn[0]).brick :
              null;
      block = this.findById(id);
      console.log(refBlock, flow, block);
    } while(block);
  }
  compile() {

  }
}

Sticky.prototype.__blocks = defaultBlocks || {};

const normalizeEvent = e => {
  if (e.x == undefined) e.x = e.clientX;
  if (e.y == undefined) e.y = e.clientY;
};
<commit_msg>Refactor loadJSON<commit_after>import Wire from './Wire.js';
import Brick from './Brick.js';
import defaultBlocks from './blocks.js'
import { getParentSvg } from './utils.js';

export default class Sticky {
  constructor(id, { width, height } = { width: 800, height: 600 }) {
    this.el = document.getElementById(id);

    if (!this.el)
      throw "Couldn't find element :(";

    this._uid = 0;
    this._aux = {};
    this.blocks = {};
    this._objects = [];
    this._wires = [];
    this._state = null;

    const svg = Sticky.createElement('svg', { class: 'svg-content', preserveAspectRatio: "xMidYMid meet" });
    this._svg = svg;
    this.el.appendChild(this._svg);
    this.matchViewBox();

    // this.registerBlock('actuator', ActuatorBrick);

    this.clearCanvas();
    this.setCanvasSize({ width, height });

    this.colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
                   "#2E2C75", "#673A7E", "#CC0071", "#F80120",
                   "#F35B20", "#FB9A00", "#FFCC00", "#FEF200"];

    // DOM Events
    svg.addEventListener('mousedown', e => {
      normalizeEvent(e);
      this.lastSelected = null;
      const { target } = e;

      if (target.type === 'wire') {
        this.lastSelected = target.wrapper;
        return this.selectedWire = target.wrapper;
      }

      if (target.type === 'port' && target.dir === 'out') {
        return this.startAttach(target);
      }

      const parentSvg = getParentSvg(target);
      const shouldCapture = tagName => !['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(tagName);
      if (shouldCapture(target.tagName) && parentSvg && parentSvg.type == 'block') {
        const blockNode = parentSvg;
        console.debug('Block selected:', blockNode, 'Triggered by: ', target);
        var wrapper = blockNode.wrapper;
        this.lastSelected = wrapper;
        this.dragging = blockNode;
        var SVGbox = wrapper._svg.getBoundingClientRect();
        const offset = {
          x: e.x - SVGbox.left,
          y: e.y - SVGbox.top,
        };
        this._aux.mouseDown = { x: offset.x - wrapper.x, y: offset.y - wrapper.y };
        this._svg.appendChild(this.dragging);
        wrapper.wires.forEach(wire => this._svg.appendChild(wire._el));
      }

    }, false);

    document.addEventListener('keydown', e => {
      if (e.keyCode == 46) {
        console.debug('deleting', this.lastSelected);
        this.lastSelected.delete();
        // @TODO should remove from state (brick, wire, port)
      }
    }, false);

    svg.addEventListener('mouseup', e => {
      this.dragging = null;
      if(e.target.type === 'port')
        return this.endAttach(e.target);

      if(this.isState('attaching')) {
        this.setState(null)
        svg.removeChild(this._aux['wire']._el);
      }
    });

    svg.addEventListener('mousemove', e => {
      normalizeEvent(e);

      return this.attachMove(e);
    });

    svg.addEventListener('mousemove', e => {
      normalizeEvent(e);

      if (this.dragging) {
        var wrapper = this.dragging.wrapper;
        var SVGbox = wrapper._svg.getBoundingClientRect();
        var OffsetX = e.x - SVGbox.left;
        var OffsetY =  e.y - SVGbox.top;

        var firstState = this._aux.mouseDown;
        wrapper.x = OffsetX - firstState.x;
        wrapper.y = OffsetY - firstState.y;
        wrapper.updateWires();
      }
    });

    return this;
  }

  setCanvasSize({ width, height }) {
    this._svg.style.width = width;
    this._svg.style.height = height;
    this._svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
  }

//static methods
  static createElement(name, attrs) {
    var el = document.createElementNS('http://www.w3.org/2000/svg', name);

    for (let key in attrs)
      el.setAttribute(key, attrs[key]);

    return el;
  }

  matchViewBox() {
    const { width, height } = this._svg.getBoundingClientRect();

    this._svg.setAttribute('viewBox', `0, 0, ${width} ${height}`);
  }
  Brick(name, attrs) {
    var el = Sticky.createElement(name, attrs);
    var brick = new Brick(el, this);
    brick._id = this._uid++;
    this._objects.push(brick);

    return brick;
  }
  addObj(obj) {
    if (obj._id == null) obj._id = this._uid++;
    this._objects.push(obj);
    this.addElement(obj._el);
  }
  removeObj(obj, update) {
    let index = this._objects.indexOf(obj);
    if (index == -1) return;

    for (let port_type of Object.keys(obj._ports)) {
      if (obj._ports[port_type].length) { // if there's any connection
        for (let port of obj._ports[port_type]) {
          // port.dettach();
        }
      }
    }

    for (let wire of [...obj.wires]) {
      wire.delete();
    }

    this.removeElement(obj._el);
    if (update)
      // should splice wires too
      return this._objects.splice(index, 1);
  }
  addElement(el) {
    this._svg.appendChild(el);
  }
  removeElement(el) {
    if (this._svg.contains(el))
      this._svg.removeChild(el);
  }
  startAttach(port) {
    let wire = new Wire(port.wrapper);
    wire._inverted = port.wrapper.dir === 'in';
    this.setState('attaching');
    this._aux['wire'] = wire;
    this.addElement(wire._el);
  }
  startDrag(port) {
    this.setState('attaching');
    this._aux['wire'] = wire;
    this.addElement(wire._el);
  }
  endAttach(port) {
    if (this.isState('attaching')) {
      this.setState(null);
      var wire = this._aux['wire'];
      wire._cp2 = port.wrapper;

      if (wire.seal()) {
        wire.render();
        this._wires.push(wire);
      } else {
        this.removeElement(wire._el);
      }
      delete this._aux['wire'];
    }
  }
  setState(state) {
    return this._state = state;
  }
  isState(state) {
    return this._state === state;
  }
  attachMove(mouse) {
    if (this.isState('attaching')) {
      var wire = this._aux['wire'];
      var SVGbox = this._svg.getBoundingClientRect();
      //(below) pixel for removing the wire from the way so we can detect the event on port
      var offset = wire._inverted ? 4 : -4;
      var mouse = { x: mouse.x - SVGbox.left + offset, y: mouse.y - SVGbox.top };
      var port = wire._cp1.getPoint();

      wire._render(port, mouse, wire._inverted);
    }
  }
  clearCanvas(start = true) {
    for (let obj of this._objects) {
      // this.removeElement(obj._el);
      this.removeObj(obj, false);
    }

    this._objects = [];
    this._wires = [];

    if (!start) return;

    const startBlock = this.createBlock('start');
    startBlock.x = 10; startBlock.y = this._svg.getAttribute('height')/2;
    startBlock.behavior = () => 0;

    this.addObj(startBlock);
  }
  registerBlock(name, obj) {
    this.blocks[name] = {
      ...obj,
      id: name,
      behavior: typeof obj.behavior !== 'function' ? new Function('findById', obj.behavior) : obj.behavior
    }
  }
  static registerBlock(name, obj) {
    this.prototype.__blocks[name] = {
      ...obj,
      id: name,
      behavior: typeof obj.behavior !== 'function' ? new Function('findById', obj.behavior) : obj.behavior
    }
  }
  createBlock(name, data = {}) {
    const cfg = this.blocks[name] || this.__blocks[name];

    if (!cfg) throw `Block '${name}' not registered`;

    return new Brick({ ...cfg, ...data });
  }
  static createBlock(name, data = {}) {
    const cfg = this.prototype.__blocks[name];

    if (!cfg) throw "Block not registered";

    return new Brick({ ...cfg, ...data });
  }
  findById(id) {
    if (id == undefined) return null;
    for (let obj of this._objects) {
      if (obj._id === id) return obj;
    }
  }
  toJSON() {
    var fluxgram = this._objects.map(obj => {
      let object = {
        refBlock: obj._refBlock,
        inputs: obj.inputs,
        id: obj._id,
        x: obj.x,
        y: obj.y,
        ports: {} // flow_in, flow_out, in, out
      };

      for (let type in obj._ports) {
        console.log(type, obj._ports[type]);
        object.ports[type] = obj._ports[type].map(port => [...port._conn]);
      }

      return object;
    });

    let refBlock = Object.entries(this.__blocks).map(
      ([blockId, block]) => ({
        ...block,
        behavior: block.behavior.toString(),
      })
    );

    return JSON.parse(JSON.stringify({ refBlock, fluxgram }));
  }

  sealOrDiscard (...cps) {
    const wire = new Wire(...cps);
    this.addElement(wire._el);
    if (wire.seal()) {
      wire.render();
      this._wires.push(wire);
    } else {
      this.removeElement(wire._el);
    }
  }

  loadPorts (blocky, ports, [from, to]) {
    ports.forEach((port, index) => {
      for (let conn of port) {
        const blocky2 = this.findById(conn.brick);
        const cps = [
          blocky._ports[from][index],
          blocky2._ports[to][conn.id],
        ];
        this.sealOrDiscard(...cps)
      }
    });
  }

  loadJSON (data) {
    this.clearCanvas(false);

    for (let block of data) {
      const { refBlock, inputs, x, y, value, id } = block;
      const obj = this.createBlock(refBlock, { inputs });
      obj.x = x;
      obj.y = y;
      obj.value = value;
      obj._id = id;
      this.addObj(obj);
    }

    // load wires
    for (let block of data) {
      const blocky = this.findById(block.id);
      this.loadPorts(blocky, block.ports.out, ['out', 'in']);
      this.loadPorts(blocky, block.ports.flow_out, ['flow_out', 'flow_in']);
    }
  }
  reload () {
    const flow = this.toJSON().fluxgram;
    this.clearCanvas(true);
    this.loadJSON(flow);
  }
  run() {
    let flow, id, refBlock;
    let block = this._objects.find(({ _refBlock }) => _refBlock == 'start');

    if (!block) {
      console.warning('Start block not found');
      return false;
    }

    console.debug('Start block found:', block);

    // flow = start.behavior();
    // an ActuatorBrick should return the flow_out port id
    // it'll be useful for if block

    do {
      refBlock = this.blocks[block._refBlock] || this.__blocks[block._refBlock];
      flow = refBlock.behavior.call(block, this.findById.bind(this));
      // console.log(block._ports);
      id = (block._ports['flow_out'][flow]._conn[0]) ?
            (block._ports['flow_out'][flow]._conn[0]).brick :
              null;
      block = this.findById(id);
      console.log(refBlock, flow, block);
    } while(block);
  }
  compile() {

  }
}

Sticky.prototype.__blocks = defaultBlocks || {};

const normalizeEvent = e => {
  if (e.x == undefined) e.x = e.clientX;
  if (e.y == undefined) e.y = e.clientY;
};
<|endoftext|>


<commit_before>/*
  termap - Terminal Map Viewer
  by Michael Strassburger <codepoet@cpan.org>

  Minimalistic parser and compiler for Mapbox (Studio) Map Style files
  See: https://www.mapbox.com/mapbox-gl-style-spec/

  Compiles layer filter instructions into a chain of true/false returning
  anonymous functions to improve rendering speed compared to realtime parsing.
*/
'use strict';

const fs = require('fs');

class Styler {
  constructor(file) {
    this.styleById = {};
    this.styleByLayer = {};
    var base, name;
    const json = JSON.parse(fs.readFileSync(file).toString());
    this.styleName = json.name;
    if (json.constants) {
      this._replaceConstants(json.constants, json.layers);
    }

    for (const style of json.layers) {
      if (style.ref && this.styleById[style.ref]) {
        for (const ref of ['type', 'source-layer', 'minzoom', 'maxzoom', 'filter']) {
          if (this.styleById[style.ref][ref] && !style[ref]) {
            style[ref] = this.styleById[style.ref][ref];
          }
        }
      }

      style.appliesTo = this._compileFilter(style.filter);

      //TODO Better translation of: @styleByLayer[style['source-layer']] ?= []
      if ((base = this.styleByLayer)[name = style['source-layer']] == null) {
        base[name] = [];
      }
      this.styleByLayer[style['source-layer']].push(style);
      this.styleById[style.id] = style;
    }
  }

  getStyleFor(layer, feature, zoom) {
    if (!this.styleByLayer[layer]) {
      return false;
    }

    for (const style of this.styleByLayer[layer]) {
      if (style.appliesTo(feature)) {
        return style;
      }
    }

    return false;
  }

  _replaceConstants(constants, tree) {
    for (const id in tree) {
      const node = tree[id];
      switch (typeof node) {
        case 'object':
          if (node.constructor.name.match(/Stream/)) {
            continue;
          }
          this._replaceConstants(constants, node);
          break;
        case 'string':
          if (node.charAt(0) === '@') {
            tree[id] = constants[node];
          }
      }
    }
  }

  //TODO Better translation of the long cases.
  _compileFilter(filter) {
    let filters;
    switch (filter != null ? filter[0] : void 0) {
      case 'all':
        filter = filter.slice(1);
        filters = (() => {
          return filter.map((sub) => this._compileFilter(sub));
        }).call(this);
        return (feature) => !!filters.find((appliesTo) => {
          return !appliesTo(feature);
        });
      case 'any':
        filter = filter.slice(1);
        filters = (() => {
          return filter.map((sub) => this._compileFilter(sub));
        }).call(this);
        return (feature) => !!filters.find((appliesTo) => {
          return appliesTo(feature);
        });
      case 'none':
        filter = filter.slice(1);
        filters = (() => {
          return filter.map((sub) => this._compileFilter(sub));
        }).call(this);
        return (feature) => !filters.find((appliesTo) => {
          return !appliesTo(feature);
        });
      case '==':
        return (feature) => feature.properties[filter[1]] === filter[2];
      case '!=':
        return (feature) => feature.properties[filter[1]] !== filter[2];
      case 'in':
        return (feature) => !!filter.slice(2).find((value) => {
          return feature.properties[filter[1]] === value;
        });
      case '!in':
        return (feature) => !filter.slice(2).find((value) => {
          return feature.properties[filter[1]] === value;
        });
      case 'has':
        return (feature) => !!feature.properties[filter[1]];
      case '!has':
        return (feature) => !feature.properties[filter[1]];
      case '>':
        return (feature) => feature.properties[filter[1]] > filter[2];
      case '>=':
        return (feature) => feature.properties[filter[1]] >= filter[2];
      case '<':
        return (feature) => feature.properties[filter[1]] < filter[2];
      case '<=':
        return (feature) => feature.properties[filter[1]] <= filter[2];
      default:
        return () => true;
    }
  }
}

module.exports = Styler;
<commit_msg>Remove unused parameter in Styler<commit_after>/*
  termap - Terminal Map Viewer
  by Michael Strassburger <codepoet@cpan.org>

  Minimalistic parser and compiler for Mapbox (Studio) Map Style files
  See: https://www.mapbox.com/mapbox-gl-style-spec/

  Compiles layer filter instructions into a chain of true/false returning
  anonymous functions to improve rendering speed compared to realtime parsing.
*/
'use strict';

const fs = require('fs');

class Styler {
  constructor(file) {
    this.styleById = {};
    this.styleByLayer = {};
    var base, name;
    const json = JSON.parse(fs.readFileSync(file).toString());
    this.styleName = json.name;
    if (json.constants) {
      this._replaceConstants(json.constants, json.layers);
    }

    for (const style of json.layers) {
      if (style.ref && this.styleById[style.ref]) {
        for (const ref of ['type', 'source-layer', 'minzoom', 'maxzoom', 'filter']) {
          if (this.styleById[style.ref][ref] && !style[ref]) {
            style[ref] = this.styleById[style.ref][ref];
          }
        }
      }

      style.appliesTo = this._compileFilter(style.filter);

      //TODO Better translation of: @styleByLayer[style['source-layer']] ?= []
      if ((base = this.styleByLayer)[name = style['source-layer']] == null) {
        base[name] = [];
      }
      this.styleByLayer[style['source-layer']].push(style);
      this.styleById[style.id] = style;
    }
  }

  getStyleFor(layer, feature) {
    if (!this.styleByLayer[layer]) {
      return false;
    }

    for (const style of this.styleByLayer[layer]) {
      if (style.appliesTo(feature)) {
        return style;
      }
    }

    return false;
  }

  _replaceConstants(constants, tree) {
    for (const id in tree) {
      const node = tree[id];
      switch (typeof node) {
        case 'object':
          if (node.constructor.name.match(/Stream/)) {
            continue;
          }
          this._replaceConstants(constants, node);
          break;
        case 'string':
          if (node.charAt(0) === '@') {
            tree[id] = constants[node];
          }
      }
    }
  }

  //TODO Better translation of the long cases.
  _compileFilter(filter) {
    let filters;
    switch (filter != null ? filter[0] : void 0) {
      case 'all':
        filter = filter.slice(1);
        filters = (() => {
          return filter.map((sub) => this._compileFilter(sub));
        }).call(this);
        return (feature) => !!filters.find((appliesTo) => {
          return !appliesTo(feature);
        });
      case 'any':
        filter = filter.slice(1);
        filters = (() => {
          return filter.map((sub) => this._compileFilter(sub));
        }).call(this);
        return (feature) => !!filters.find((appliesTo) => {
          return appliesTo(feature);
        });
      case 'none':
        filter = filter.slice(1);
        filters = (() => {
          return filter.map((sub) => this._compileFilter(sub));
        }).call(this);
        return (feature) => !filters.find((appliesTo) => {
          return !appliesTo(feature);
        });
      case '==':
        return (feature) => feature.properties[filter[1]] === filter[2];
      case '!=':
        return (feature) => feature.properties[filter[1]] !== filter[2];
      case 'in':
        return (feature) => !!filter.slice(2).find((value) => {
          return feature.properties[filter[1]] === value;
        });
      case '!in':
        return (feature) => !filter.slice(2).find((value) => {
          return feature.properties[filter[1]] === value;
        });
      case 'has':
        return (feature) => !!feature.properties[filter[1]];
      case '!has':
        return (feature) => !feature.properties[filter[1]];
      case '>':
        return (feature) => feature.properties[filter[1]] > filter[2];
      case '>=':
        return (feature) => feature.properties[filter[1]] >= filter[2];
      case '<':
        return (feature) => feature.properties[filter[1]] < filter[2];
      case '<=':
        return (feature) => feature.properties[filter[1]] <= filter[2];
      default:
        return () => true;
    }
  }
}

module.exports = Styler;
<|endoftext|>