<commit_before>import store from '../store';
import {launch , launchReady, buildUp } from '../reducers/abilities';

import { scene } from './main';
const THREE = require('three');
const CANNON = require('../../public/cannon.min.js');

THREE.PlayerControls = function ( camera, player, cannonMesh, raycastReference , id) {
	this.domElement = document;
	this.camera = camera;
	this.player = player;
	this.cannonMesh = cannonMesh;
	this.id = id;
	this.cooldown = Date.now();
	this.scale;

	var keyState = {};

	var scope = this;

	var curCamZoom = 60;
	var curCamHeight = 70;

	this.left = false;
	this.right = false;

	this.speedMult = 1;
	this.launchMult = 1;

	// helpful dev tool, yo
	// var geometry = new THREE.BoxGeometry( 30,3,2 );
 //    var material = new THREE.MeshPhongMaterial({
 //                                             shading: THREE.FlatShading });
 //    this.devTestMesh = new THREE.Mesh( geometry, material );
 //    scene.add(this.devTestMesh)

 	this.playerRotation = new THREE.Quaternion();

	var cameraReferenceOrientation = new THREE.Quaternion();
	var cameraReferenceOrientationObj = new THREE.Object3D();
	var poleDir = new THREE.Vector3(1,0,0);

	this.update = function() {
		scope.scale = store.getState().players[scope.id].scale;

		this.camera.fov = Math.max(55, Math.min(45 + this.speedMult*10, 65/(1 + (scope.scale * 0.01) )));
		//console.log(this.camera.fov, this.speedMult)
		this.camera.updateProjectionMatrix();

		//cameraReferenceOrientation.copy(cameraReferenceOrientationObj.quaternion);

		//console.log(store.getState().abilities && )
		if(!store.getState().abilities.launch && Date.now() - scope.cooldown > 5000){
			// console.log("launchReady")
			store.dispatch(launchReady());
		}

		this.checkKeyStates();
		//console.log(this.speedMult);
	    var playerPosition = new THREE.Vector3(this.player.position.x, this.player.position.y, this.player.position.z);

			//var cameraReferenceOrientation = new THREE.Quaternion();
			var cameraPosition = this.player.position.clone();
			var poleDirection = new THREE.Vector3(1,0,0)

		    var localUp = cameraPosition.clone().normalize();

		 // no orientation for now, change cameraReforient if necessary
	 	if(this.left){
	 		// var cameraRot2 = new THREE.Matrix4();
	 		// cameraRot2.makeRotationY(0.005);
	 		// noClue.multiplyMatrices(noClue, cameraRot2)
	 		cameraReferenceOrientationObj.rotation.y = 0.02;// change to rotation
	 		this.left = false;
	 	}
	 	else if(this.right){
	 		// var cameraRot2 = new THREE.Matrix4();
	 		// cameraRot2.makeRotationY(-0.005);
	 		// noClue.multiplyMatrices(noClue, cameraRot2)
	 		cameraReferenceOrientationObj.rotation.y = -0.02;
	 		this.right = false;
	 	}

		var referenceForward = new THREE.Vector3(0, 0, 1);
		referenceForward.applyQuaternion(cameraReferenceOrientationObj.quaternion);
		var correctionAngle = Math.atan2(referenceForward.x, referenceForward.z)
		var cameraPoru = new THREE.Vector3(0,-1,0);
		cameraReferenceOrientationObj.quaternion.setFromAxisAngle(cameraPoru,correctionAngle);
		poleDir.applyAxisAngle(localUp,correctionAngle).normalize();

		cameraReferenceOrientationObj.quaternion.copy(cameraReferenceOrientation);

		var cross = new THREE.Vector3();
		cross.crossVectors(poleDir,localUp);

		var dot = localUp.dot(poleDir);
		poleDir.subVectors(poleDir , localUp.clone().multiplyScalar(dot));

	    var noClue = new THREE.Matrix4();
	    noClue.set(	poleDir.x,localUp.x,cross.x,cameraPosition.x,
	    			poleDir.y,localUp.y,cross.y,cameraPosition.y,
	    			poleDir.z,localUp.z,cross.z,cameraPosition.z,
	    			0,0,0,1);

	 	this.camera.matrixAutoUpdate = false;

		var cameraPlace = new THREE.Matrix4();
	    cameraPlace.makeTranslation ( 0, curCamHeight * scope.scale * .8, curCamZoom * scope.scale * .8)

	    var cameraRot = new THREE.Matrix4();
	    cameraRot.makeRotationX(-0.32 - (playerPosition.length()/1200));// scale this with height to planet!

	    var oneTwo = new THREE.Matrix4();
	    oneTwo.multiplyMatrices(noClue , cameraPlace);

		var oneTwoThree = new THREE.Matrix4();
	    oneTwoThree.multiplyMatrices(oneTwo, cameraRot);

	    this.camera.matrix = oneTwoThree;
	};

	this.checkKeyStates = function () {

		if(this.speedMult < 1){ this.speedMult = 1}
		if(this.speedMult < 1){ this.speedMult = 1}
	// get quaternion and position to apply impulse
	var playerPositionCannon = new CANNON.Vec3(scope.cannonMesh.position.x, scope.cannonMesh.position.y, scope.cannonMesh.position.z);

	// get unit (directional) vector for position
    var norm = playerPositionCannon.normalize();

   	var localTopPoint = new CANNON.Vec3(0,0,500);
   	var topVec = new CANNON.Vec3(0,0,1);
   	var quaternionOnPlanet = new CANNON.Quaternion();
    quaternionOnPlanet.setFromVectors(topVec, playerPositionCannon);
    var topOfBall = quaternionOnPlanet.vmult(new CANNON.Vec3(0,0,norm).vadd(new CANNON.Vec3(0,0, scope.scale * 10)));

	// this.devTestMesh.position.set(topOfBall.x, topOfBall.z, topOfBall.y)

	// find direction on planenormal by crossing the cross cross prods of localUp and camera dir
	var camVec = new THREE.Vector3();
    camera.getWorldDirection( camVec );
    camVec.normalize();

    // apply downward force to keep ball rolling
    var downforce = new THREE.Vector3(0,-1,0);
    var dfQuat = new THREE.Quaternion()
    camera.getWorldQuaternion( dfQuat );
    downforce.applyQuaternion(dfQuat);

    camVec.add(downforce.divideScalar(2));
    camVec.normalize()

	var playerPosition = new THREE.Vector3(this.player.position.x, this.player.position.y, this.player.position.z);
	playerPosition.normalize();

	// lateral directional vector
	var cross1 = new THREE.Vector3();
	cross1.crossVectors(playerPosition, camVec);
	//cross1.multiplyScalar(10);
	//cross1.addScalar(20 + Math.pow(this.scale, 3));

	// front/back vector
	var cross2 = new THREE.Vector3();
	cross2.crossVectors(playerPosition, cross1);
	//cross2.multiplyScalar(10);
	//cross2.addScalar(20 + Math.pow(this.scale, 3));

		if (keyState[32]) {
			if(store.getState().abilities.launch){
				if(this.launchMult < 6) this.launchMult += 0.04;

				var buildLaunch = ~~(this.launchMult * 3 - 3);
				//console.log(buildLaunch)
				store.dispatch(buildUp(buildLaunch));
			}
	    }

	    if (keyState[38] || keyState[87]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // up arrow or 'w' - move forward
          this.cannonMesh.applyImpulse(new CANNON.Vec3(-cross2.x * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, -cross2.z * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, -cross2.y * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/) ,topOfBall);
	    }

	    if (keyState[40] || keyState[83]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // down arrow or 's' - move backward
          this.cannonMesh.applyImpulse(new CANNON.Vec3(cross2.x * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross2.z * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross2.y * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/) ,topOfBall);

	    }

	    if (keyState[37] || keyState[65]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // left arrow or 'a' - rotate left
	        this.cannonMesh.applyImpulse(new CANNON.Vec3(cross1.x * 100 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross1.z * 100 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross1.y * 100 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/) ,topOfBall);
	        this.left = true;
	    }

	    if (keyState[39] || keyState[68]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // right arrow or 'd' - rotate right
            this.cannonMesh.applyImpulse(new CANNON.Vec3(-cross1.x * 120 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/,-cross1.z * 120 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/,-cross1.y * 120 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/), topOfBall);
            this.right = true;
	    }
	    if(!(keyState[38] || keyState[87] || keyState[40] || keyState[83] || keyState[37] || keyState[65] || keyState[39] || keyState[68])){
	    	this.speedMult -= .06;
	    }

	    // if(!keyState[32]){

	    // }

	    if (!keyState[32] && this.launchMult > 1) {
			if(this.launchMult < 3) this.launchMult += 0.1;

			var camVec2 = new THREE.Vector3();
		    camera.getWorldDirection( camVec2 );
		    camVec2.normalize();

		    // apply upward force to launch
		    var upforce = new THREE.Vector3(0,1,0);
		    var ufQuat = new THREE.Quaternion()
		    camera.getWorldQuaternion( ufQuat );
		    upforce.applyQuaternion(ufQuat);

		    camVec2.add(upforce.divideScalar(1));
		    camVec2.normalize();

		    var cross1o = new THREE.Vector3();
			cross1o.crossVectors(playerPosition, camVec2);
			var cross2o = new THREE.Vector3();
			cross2o.crossVectors(playerPosition, cross1o);

		    // spacebar - orbital launch
		    var launchIntoOrbit = new THREE.Vector3();
		     if(keyState[38] || keyState[87]){
				launchIntoOrbit.copy(cross2o).negate();
		    }else
		    if (keyState[40] || keyState[83]) {
		    	launchIntoOrbit.copy(cross2o);
		    }else
		    if (keyState[37] || keyState[65]) {
		    	launchIntoOrbit.copy(cross1o);
		    }else
		    if (keyState[39] || keyState[68]) {
		    	launchIntoOrbit.copy(cross1o).negate();
		    }
		    else{
		    	launchIntoOrbit.copy(playerPosition).normalize().divideScalar(1);
		    }

		  //  console.log(cannonMesh, launchIntoOrbit.x)

		    store.dispatch(launch());

	        this.cannonMesh.applyImpulse(new CANNON.Vec3(launchIntoOrbit.x * 3000 * this.launchMult , launchIntoOrbit.z * 3000 * this.launchMult , launchIntoOrbit.y * 3000 * this.launchMult ), topOfBall);
	        scope.cooldown = Date.now();

	        this.launchMult = 1;
		}
	};

	function onKeyDown( event ) {

    	event = event || window.event;

        keyState[event.keyCode || event.which] = true;

    }

    function onKeyUp( event ) {

        event = event || window.event;

        keyState[event.keyCode || event.which] = false;

    }

	this.domElement.addEventListener('contextmenu', function( event ) { event.preventDefault(); }, false );
	this.domElement.addEventListener('keydown', onKeyDown, false );
	this.domElement.addEventListener('keyup', onKeyUp, false );

};

THREE.PlayerControls.prototype = Object.create( THREE.EventDispatcher.prototype );<commit_msg>added logarithmic charging<commit_after>import store from '../store';
import {launch , launchReady, buildUp } from '../reducers/abilities';

import { scene } from './main';
const THREE = require('three');
const CANNON = require('../../public/cannon.min.js');

THREE.PlayerControls = function ( camera, player, cannonMesh, raycastReference , id) {
	this.domElement = document;
	this.camera = camera;
	this.player = player;
	this.cannonMesh = cannonMesh;
	this.id = id;
	this.cooldown = Date.now();
	this.scale;

	var keyState = {};

	var scope = this;

	var curCamZoom = 60;
	var curCamHeight = 70;

	this.left = false;
	this.right = false;

	this.speedMult = 1;
	this.launchMult = 1;
	this.i = 1;

	// helpful dev tool, yo
	// var geometry = new THREE.BoxGeometry( 30,3,2 );
 //    var material = new THREE.MeshPhongMaterial({
 //                                             shading: THREE.FlatShading });
 //    this.devTestMesh = new THREE.Mesh( geometry, material );
 //    scene.add(this.devTestMesh)

 	this.playerRotation = new THREE.Quaternion();

	var cameraReferenceOrientation = new THREE.Quaternion();
	var cameraReferenceOrientationObj = new THREE.Object3D();
	var poleDir = new THREE.Vector3(1,0,0);

	this.update = function() {
		scope.scale = store.getState().players[scope.id].scale;

		this.camera.fov = Math.max(55, Math.min(45 + this.speedMult*10, 65/(1 + (scope.scale * 0.01) )));
		//console.log(this.camera.fov, this.speedMult)
		this.camera.updateProjectionMatrix();

		//cameraReferenceOrientation.copy(cameraReferenceOrientationObj.quaternion);

		//console.log(store.getState().abilities && )
		if(!store.getState().abilities.launch && Date.now() - scope.cooldown > 8.5 * 1000){
			// console.log("launchReady")
			store.dispatch(launchReady());
		}

		this.checkKeyStates();
		//console.log(this.speedMult);
	    var playerPosition = new THREE.Vector3(this.player.position.x, this.player.position.y, this.player.position.z);

			//var cameraReferenceOrientation = new THREE.Quaternion();
			var cameraPosition = this.player.position.clone();
			var poleDirection = new THREE.Vector3(1,0,0)

		    var localUp = cameraPosition.clone().normalize();

		 // no orientation for now, change cameraReforient if necessary
	 	if(this.left){
	 		// var cameraRot2 = new THREE.Matrix4();
	 		// cameraRot2.makeRotationY(0.005);
	 		// noClue.multiplyMatrices(noClue, cameraRot2)
	 		cameraReferenceOrientationObj.rotation.y = 0.04;// change to rotation
	 		this.left = false;
	 	}
	 	else if(this.right){
	 		// var cameraRot2 = new THREE.Matrix4();
	 		// cameraRot2.makeRotationY(-0.005);
	 		// noClue.multiplyMatrices(noClue, cameraRot2)
	 		cameraReferenceOrientationObj.rotation.y = -0.04;
	 		this.right = false;
	 	}

		var referenceForward = new THREE.Vector3(0, 0, 1);
		referenceForward.applyQuaternion(cameraReferenceOrientationObj.quaternion);
		var correctionAngle = Math.atan2(referenceForward.x, referenceForward.z)
		var cameraPoru = new THREE.Vector3(0,-1,0);
		cameraReferenceOrientationObj.quaternion.setFromAxisAngle(cameraPoru,correctionAngle);
		poleDir.applyAxisAngle(localUp,correctionAngle).normalize();

		cameraReferenceOrientationObj.quaternion.copy(cameraReferenceOrientation);

		var cross = new THREE.Vector3();
		cross.crossVectors(poleDir,localUp);

		var dot = localUp.dot(poleDir);
		poleDir.subVectors(poleDir , localUp.clone().multiplyScalar(dot));

	    var noClue = new THREE.Matrix4();
	    noClue.set(	poleDir.x,localUp.x,cross.x,cameraPosition.x,
	    			poleDir.y,localUp.y,cross.y,cameraPosition.y,
	    			poleDir.z,localUp.z,cross.z,cameraPosition.z,
	    			0,0,0,1);

	 	this.camera.matrixAutoUpdate = false;

		var cameraPlace = new THREE.Matrix4();
	    cameraPlace.makeTranslation ( 0, curCamHeight * scope.scale * .8, curCamZoom * scope.scale * .8)

	    var cameraRot = new THREE.Matrix4();
	    cameraRot.makeRotationX(-0.32 - (playerPosition.length()/1200));// scale this with height to planet!

	    var oneTwo = new THREE.Matrix4();
	    oneTwo.multiplyMatrices(noClue , cameraPlace);

		var oneTwoThree = new THREE.Matrix4();
	    oneTwoThree.multiplyMatrices(oneTwo, cameraRot);

	    this.camera.matrix = oneTwoThree;
	};

	this.checkKeyStates = function () {

		if(this.speedMult < 1){ this.speedMult = 1}
		if(this.speedMult < 1){ this.speedMult = 1}
	// get quaternion and position to apply impulse
	var playerPositionCannon = new CANNON.Vec3(scope.cannonMesh.position.x, scope.cannonMesh.position.y, scope.cannonMesh.position.z);

	// get unit (directional) vector for position
    var norm = playerPositionCannon.normalize();

   	var localTopPoint = new CANNON.Vec3(0,0,500);
   	var topVec = new CANNON.Vec3(0,0,1);
   	var quaternionOnPlanet = new CANNON.Quaternion();
    quaternionOnPlanet.setFromVectors(topVec, playerPositionCannon);
    var topOfBall = quaternionOnPlanet.vmult(new CANNON.Vec3(0,0,norm).vadd(new CANNON.Vec3(0,0, scope.scale * 10)));

	// this.devTestMesh.position.set(topOfBall.x, topOfBall.z, topOfBall.y)

	// find direction on planenormal by crossing the cross cross prods of localUp and camera dir
	var camVec = new THREE.Vector3();
    camera.getWorldDirection( camVec );
    camVec.normalize();

    // apply downward force to keep ball rolling
    var downforce = new THREE.Vector3(0,-1,0);
    var dfQuat = new THREE.Quaternion()
    camera.getWorldQuaternion( dfQuat );
    downforce.applyQuaternion(dfQuat);

    camVec.add(downforce.divideScalar(2));
    camVec.normalize()

	var playerPosition = new THREE.Vector3(this.player.position.x, this.player.position.y, this.player.position.z);
	playerPosition.normalize();

	// lateral directional vector
	var cross1 = new THREE.Vector3();
	cross1.crossVectors(playerPosition, camVec);
	//cross1.multiplyScalar(10);
	//cross1.addScalar(20 + Math.pow(this.scale, 3));

	// front/back vector
	var cross2 = new THREE.Vector3();
	cross2.crossVectors(playerPosition, cross1);
	//cross2.multiplyScalar(10);
	//cross2.addScalar(20 + Math.pow(this.scale, 3));

		if (keyState[32]) {
			if(store.getState().abilities.launch){
				if(this.launchMult < 6) this.launchMult += 1/(this.i++ * 1.1);

				var buildLaunch = ~~(this.launchMult * 3 - 3);
				//console.log(buildLaunch)
				store.dispatch(buildUp(buildLaunch));
			}
	    }

	    if (keyState[38] || keyState[87]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // up arrow or 'w' - move forward
          this.cannonMesh.applyImpulse(new CANNON.Vec3(-cross2.x * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, -cross2.z * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, -cross2.y * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/) ,topOfBall);
	    }

	    if (keyState[40] || keyState[83]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // down arrow or 's' - move backward
          this.cannonMesh.applyImpulse(new CANNON.Vec3(cross2.x * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross2.z * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross2.y * 140 * (0.833 + this.scale/6) *this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/) ,topOfBall);

	    }

	    if (keyState[37] || keyState[65]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // left arrow or 'a' - rotate left
	        this.cannonMesh.applyImpulse(new CANNON.Vec3(cross1.x * 100 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross1.z * 100 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/, cross1.y * 100 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/) ,topOfBall);
	        this.left = true;
	    }

	    if (keyState[39] || keyState[68]) {
	    	if(this.speedMult < 3.5) this.speedMult += 0.005;
	        // right arrow or 'd' - rotate right
            this.cannonMesh.applyImpulse(new CANNON.Vec3(-cross1.x * 120 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/,-cross1.z * 120 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/,-cross1.y * 120 * (0.833 + this.scale/6) * this.speedMult /*Math.pow(scope.scale, 1 + (scope.scale * 1))*/), topOfBall);
            this.right = true;
	    }
	    if(!(keyState[38] || keyState[87] || keyState[40] || keyState[83] || keyState[37] || keyState[65] || keyState[39] || keyState[68])){
	    	this.speedMult -= .06;
	    }

	    // if(!keyState[32]){

	    // }

	    if (!keyState[32] && this.launchMult > 1) {
			if(this.launchMult < 3) this.launchMult += 0.1;

			var camVec2 = new THREE.Vector3();
		    camera.getWorldDirection( camVec2 );
		    camVec2.normalize();

		    // apply upward force to launch
		    var upforce = new THREE.Vector3(0,1,0);
		    var ufQuat = new THREE.Quaternion()
		    camera.getWorldQuaternion( ufQuat );
		    upforce.applyQuaternion(ufQuat);

		    camVec2.add(upforce.divideScalar(1));
		    camVec2.normalize();

		    var cross1o = new THREE.Vector3();
			cross1o.crossVectors(playerPosition, camVec2);
			var cross2o = new THREE.Vector3();
			cross2o.crossVectors(playerPosition, cross1o);

		    // spacebar - orbital launch
		    var launchIntoOrbit = new THREE.Vector3();
		     if(keyState[38] || keyState[87]){
				launchIntoOrbit.copy(cross2o).negate();
		    }else
		    if (keyState[40] || keyState[83]) {
		    	launchIntoOrbit.copy(cross2o);
		    }else
		    if (keyState[37] || keyState[65]) {
		    	launchIntoOrbit.copy(cross1o);
		    }else
		    if (keyState[39] || keyState[68]) {
		    	launchIntoOrbit.copy(cross1o).negate();
		    }
		    else{
		    	launchIntoOrbit.copy(playerPosition).normalize().divideScalar(1);
		    }

		  //  console.log(cannonMesh, launchIntoOrbit.x)

		    store.dispatch(launch());

	        this.cannonMesh.applyImpulse(new CANNON.Vec3(launchIntoOrbit.x * 3000 * this.launchMult , launchIntoOrbit.z * 3000 * this.launchMult , launchIntoOrbit.y * 3000 * this.launchMult ), topOfBall);
	        scope.cooldown = Date.now();

	        this.launchMult = 1;
	        this.i = 1;
		}
	};

	function onKeyDown( event ) {

    	event = event || window.event;

        keyState[event.keyCode || event.which] = true;

    }

    function onKeyUp( event ) {

        event = event || window.event;

        keyState[event.keyCode || event.which] = false;

    }

	this.domElement.addEventListener('contextmenu', function( event ) { event.preventDefault(); }, false );
	this.domElement.addEventListener('keydown', onKeyDown, false );
	this.domElement.addEventListener('keyup', onKeyUp, false );

};

THREE.PlayerControls.prototype = Object.create( THREE.EventDispatcher.prototype );<|endoftext|>


<commit_before>var server_ip = localStorage.getItem("ipaddr");

////////////////////////////////
// Event Listeners for pebble //
////////////////////////////////

Pebble.addEventListener('ready', function () {
    console.log("Pebble JS ready.");
    if (!server_ip)
	console.log("server_ip is not configured.");
});

Pebble.addEventListener('showConfiguration', function (e) {
    // Show config page
    console.log("Showing page...");
    var url = 'http://shotaroikeda.github.io/Pebble-CSGO/configure/conf.html?' +
	    encodeURIComponent(JSON.stringify({"ipaddr_default" : server_ip}));
    console.log(url);
    Pebble.openURL(url);
});

Pebble.addEventListener('webviewclosed', function (e) {
    console.log("Page closed.");
    var jsObj = JSON.parse(e.response);

    if (jsObj["ipaddr"])
    {
	localStorage.setItem("ipaddr", jsObj["ipaddr"]);
	server_ip = jsObj["ipaddr"];
    }
});
<commit_msg>added better error handling for http:// inserts<commit_after>var server_ip = localStorage.getItem("ipaddr");

////////////////////////////////
// Event Listeners for pebble //
////////////////////////////////

Pebble.addEventListener('ready', function () {
    console.log("Pebble JS ready.");
    if (!server_ip)
        console.log("server_ip is not configured.");
});

Pebble.addEventListener('showConfiguration', function (e) {
    // Show config page
    console.log("Showing page...");

    var url;
    if (server_ip) {
        url = 'http://shotaroikeda.github.io/Pebble-CSGO/configure/conf.html?' +
            encodeURIComponent(JSON.stringify({"ipaddr_default" : server_ip.substring(7)}));
    }
    else {
        url = 'http://shotaroikeda.github.io/Pebble-CSGO/configure/conf.html?' +
            encodeURIComponent(JSON.stringify({"ipaddr_default" : "xxx.xxx.x.x:yyyyy"}));
    }

    console.log(url);
    Pebble.openURL(url);
});

Pebble.addEventListener('webviewclosed', function (e) {
    console.log("Page closed.");
    var jsObj = JSON.parse(e.response);

    if (jsObj["ipaddr"])
    {
        server_ip = jsObj["ipaddr"].match("^(http://)") == null ?
            "http://" + jsObj["ipaddr"] : jsObj["ipaddr"];

        localStorage.setItem("ipaddr", server_ip);
    }
});

/////////////////////////////////
// App Message CS GO Functions //
/////////////////////////////////

function retrieveCSGO() {

}
<|endoftext|>


<commit_before>/*
    Rob Holme

    Class used to retrieve extension preferences

*/
const vscode = require('vscode');

//----------------------------------------------------
// class defining a single field value and the filename it was found in
class ExtensionPreferences {

	constructor() {
		this._config = vscode.workspace.getConfiguration('hl7tools');
	}

	get AddLineBreakOnActivation() {
		return this._config['AddLinebreakOnActivation']
	}

	get ConnectionTimeOut() {
		return this._config['ConnectionTimeout']
	}

	get DefaultListenerPort() {
		return this._config['DefaultListenerPort']
	}

	get DefaultRemoteHost() {
		return this._config['DefaultRemoteHost']
	}

	get FavouriteRemoteHosts() {
		// return null if no preferences set (based on the first item not containing a description)
		if (this._config['FavouriteRemoteHosts'][0].Description) {
			return this._config['FavouriteRemoteHosts']
		}
		else {
			return null;
		}
	}

	get HighlightBackgroundColour() {
		return this._config['highlightBackgroundColor']
	}

	get MaxLinesForFieldDescriptions() {
		return this._config['MaxLinesForFieldDescriptions']
	}

	get SendACK() {
		return this._config['SendACK']
	}

	get SocketEncodingPreference() {
		// select the socket encoding defined in the preferences
		switch (this._config['SocketEncoding']) {
			case "UTF-8":
				return "utf8";
			case "UTF-16LE":
				return "utf16le";
			case "ISO-8859-1":
				return "latin1";
			case "ASCII":
				return "ascii";
			default:
				return "utf8"
		}
	}

	get CustomSegmentSchema() {
		return this._config['CustomSegmentSchema']
	}

	get TrustedCertificateAuthorities() {
		return this._config['TrustedCertificateAuthorities']
	}

}

exports.ExtensionPreferences = ExtensionPreferences;<commit_msg>added TrustedCertificateAuthorities preference<commit_after>/*
    Rob Holme

    Class used to retrieve extension preferences

*/
const vscode = require('vscode');

//----------------------------------------------------
// class defining a single field value and the filename it was found in
class ExtensionPreferences {

	constructor() {
		this._config = vscode.workspace.getConfiguration('hl7tools');
	}

	get AddLineBreakOnActivation() {
		return this._config['AddLinebreakOnActivation'] ;
	}

	get ConnectionTimeOut() {
		return this._config['ConnectionTimeout'];
	}

	get DefaultListenerPort() {
		return this._config['DefaultListenerPort'];
	}

	get DefaultRemoteHost() {
		return this._config['DefaultRemoteHost'];
	}

	get FavouriteRemoteHosts() {
		// return null if no preferences set (based on the first item not containing a description)
		if (this._config['FavouriteRemoteHosts'][0].Description) {
			return this._config['FavouriteRemoteHosts'];
		}
		else {
			return null;
		}
	}

	get HighlightBackgroundColour() {
		return this._config['highlightBackgroundColor'];
	}

	get MaxLinesForFieldDescriptions() {
		return this._config['MaxLinesForFieldDescriptions'];
	}

	get SendACK() {
		return this._config['SendACK'];
	}

	get SocketEncodingPreference() {
		// select the socket encoding defined in the preferences
		switch (this._config['SocketEncoding']) {
			case "UTF-8":
				return "utf8";
			case "UTF-16LE":
				return "utf16le";
			case "ISO-8859-1":
				return "latin1";
			case "ASCII":
				return "ascii";
			default:
				return "utf8";
		}
	}

	get CustomSegmentSchema() {
		return this._config['CustomSegmentSchema'];
	}

	get TrustedCertificateAuthorities() {
		return this._config['TrustedCertificateAuthorities'];
	}

}

exports.ExtensionPreferences = ExtensionPreferences;<|endoftext|>


<commit_before>/* global define */
define([
    'underscore', 'backbone', 'oroui/js/messenger', 'orotranslation/js/translator',
    'oroworkflow/js/workflow-management/step/view/list',
    'oroworkflow/js/workflow-management/step/model',
    'oroworkflow/js/workflow-management/transition/model',
    'oroworkflow/js/workflow-management/step/view/edit',
    'oroworkflow/js/workflow-management/transition/view/edit',
    'oroworkflow/js/workflow-management/helper',
    'oronavigation/js/navigation',
    'oroui/js/app',
    'oroui/js/delete-confirmation',
    'oroentity/js/fields-loader'
],
function(_, Backbone, messanger, __,
     StepsListView,
     StepModel,
     TransitionModel,
     StepEditView,
     TransitionEditForm,
     Helper,
     Navigation,
     app,
     Confirmation
) {
    'use strict';

    var $ = Backbone.$;

    /**
     * @export  oroworkflow/js/workflow-management
     * @class   oro.WorkflowManagement
     * @extends Backbone.View
     */
    return Backbone.View.extend({
        events: {
            'click .add-step-btn': 'addNewStep',
            'click .add-transition-btn': 'addNewTransition'
        },

        options: {
            stepsEl: null,
            model: null,
            entities: [],
            backUrl: null
        },

        initialize: function() {
            this.saveAndClose = false;

            _.each(this.model.get('steps').models, this.setWorkflow, this);
            _.each(this.model.get('transitions').models, this.setWorkflow, this);
            this.listenTo(this.model.get('steps'), 'add', this.setWorkflow);
            this.listenTo(this.model.get('transitions'), 'add', this.setWorkflow);

            this.addStartingPoint();
            this.initStartStepSelector();

            this.stepListView = new StepsListView({
                el: this.$(this.options.stepsEl),
                collection: this.model.get('steps'),
                workflow: this.model
            });
            this.$entitySelectEl = this.$('[name$="[related_entity]"]');
            this.initEntityFieldsLoader();
            this.initForm();

            this.listenTo(this.model, 'requestAddTransition', this.addNewStepTransition);
            this.listenTo(this.model, 'requestEditStep', this.openManageStepForm);
            this.listenTo(this.model, 'requestCloneStep', this.cloneStep);
            this.listenTo(this.model, 'requestRemoveStep', this.removeStep);
            this.listenTo(this.model.get('steps'), 'destroy', this.onStepRemove);

            this.listenTo(this.model, 'requestRemoveTransition', this.removeTransition);
            this.listenTo(this.model, 'requestCloneTransition', this.cloneTransition);
            this.listenTo(this.model, 'requestEditTransition', this.openManageTransitionForm);

            this.listenTo(this.model, 'change:entity', this.resetWorkflow);
        },

        initForm: function() {
            this.$el.on('submit', _.bind(this.saveConfiguration, this));
            this.model.url = this.$el.attr('action');

            this.$('[type="submit"]').on('click', _.bind(function() {
                this.saveAndClose = true;
            }, this));
            this.$('[data-action="save_and_stay"]').on('click', _.bind(function() {
                this.saveAndClose = false;
            }, this));
        },

        setWorkflow: function(item) {
            item.setWorkflow(this.model);
        },

        initStartStepSelector: function() {
            var getSteps = _.bind(function(query) {
                var steps = [];
                _.each(this.model.get('steps').models, function(step) {
                    // starting point is not allowed to be a start step
                    var stepLabel = step.get('label');
                    if (!step.get('_is_start')
                        && (!query.term || query.term == stepLabel || _.indexOf(stepLabel, query.term) !== -1)
                    ) {
                        steps.push({
                            'id': step.get('name'),
                            'text': step.get('label')
                        });
                    }
                }, this);

                query.callback({results: steps});
            }, this);

            this.$startStepEl = this.$('[name="start_step"]');

            var select2Options = {
                'allowClear': true,
                'query': getSteps,
                'placeholder': __('Choose step...'),
                'initSelection' : _.bind(function (element, callback) {
                    var startStep = this.model.getStepByName(element.val());
                    callback({
                        id: startStep.get('name'),
                        text: startStep.get('label')
                    });
                }, this)
            };

            this.$startStepEl.select2(select2Options);
        },

        initEntityFieldsLoader: function() {
            var confirm = new Confirmation({
                title: __('Change Entity Confirmation'),
                okText: __('Yes, I Agree'),
                content: __('Are you sure you want to change entity?')
            });
            confirm.on('ok', _.bind(function() {
                this.model.set('entity', this.$entitySelectEl.val());
            }, this));
            confirm.on('cancel', _.bind(function() {
                this.$entitySelectEl.select2('val', this.model.get('entity'));
            }, this));

            this.$entitySelectEl.fieldsLoader({
                router: 'oro_workflow_api_rest_entity_get',
                routingParams: {"with-relations": 1, "with-entity-details": 1, "deep-level": 2},
                confirm: confirm,
                requireConfirm: _.bind(function () {
                     return this.model.get('steps').length > 1 &&
                         (this.model.get('transitions').length
                            + this.model.get('transition_definitions').length
                            + this.model.get('attributes').length) > 0;
                }, this)
            });
            this.$entitySelectEl.on('change', _.bind(function() {
                if (!this.model.get('entity')) {
                    this.model.set('entity', this.$entitySelectEl.val());
                }
            }, this));

            this.$entitySelectEl.on('fieldsloadercomplete', _.bind(function(e) {
                this.createPathMapping($(e.target).data('fields'));
            }, this));
        },

        addStartingPoint: function() {
            this.model.get('steps').add(this._createStartingPoint());
        },

        resetWorkflow: function() {
            //Need to manually destroy collection elements to trigger all appropriate events
            var resetCollection = function(collection) {
                if (collection.length) {
                    for (var i = collection.length -1; i > -1; i--) {
                        collection.at(i).destroy();
                    }
                }
            };

            this.model.set('start_step', null);
            resetCollection(this.model.get('attributes'));
            resetCollection(this.model.get('steps'));
            resetCollection(this.model.get('transition_definitions'));
            resetCollection(this.model.get('transitions'));

            this.addStartingPoint();
        },

        createPathMapping: function(fields) {
            var rootAttributeName = this.model.get('entity_attribute');
            var mapping = {};

            var addMapping = _.bind(function(field, parentPropertyPath, parentFieldId) {
                var propertyPath = parentPropertyPath + '.' + field.name;
                var fieldIdParts = [];

                if (parentFieldId) {
                    fieldIdParts.push(parentFieldId);
                }

                var fieldIdName = field.name;
                if (field.hasOwnProperty('related_entity_name')) {
                    fieldIdName += '+' + field.related_entity_name;
                }
                fieldIdParts.push(fieldIdName);

                if (!field.hasOwnProperty('related_entity_name')) {
                    mapping[propertyPath] = fieldIdParts.join('::');
                }

                if (field.hasOwnProperty('related_entity_fields')) {
                    _.each(field.related_entity_fields, function(relatedField) {
                        addMapping(relatedField, propertyPath, fieldIdParts.join('::'));
                    });
                }
            });

            _.each(fields, function(field) {
                addMapping(field, rootAttributeName, "");
            });

            this.model.setPropertyPathToFieldIdMapping(mapping);
        },

        saveConfiguration: function(e) {
            e.preventDefault();
            if (!this.$el.valid()) {
                return;
            }

            var formData = Helper.getFormData(this.$el);
            formData.steps_display_ordered = formData.hasOwnProperty('steps_display_ordered');

            if (!this.model.get('name')) {
                this.model.set('name', Helper.getNameByString(formData.label, 'workflow_'));
            }
            this.model.set('label', formData.label);
            this.model.set('steps_display_ordered', formData.steps_display_ordered);
            this.model.set('entity', formData.related_entity);
            this.model.set('start_step', formData.start_step);

            if (!this.validateConfiguration()) {
                return;
            }

            var navigation = Navigation.getInstance();
            navigation.showLoading();
            this.model.save(null, {
                'success': _.bind(function() {
                    navigation.hideLoading();
                    if (this.saveAndClose) {
                        navigation.setLocation(this.options.backUrl);
                    }

                    messanger.notificationFlashMessage('success', __('Workflow saved.'));
                }, this),
                'error': function(model, response) {
                    navigation.hideLoading();
                    if (app.debug && !_.isUndefined(console) && !_.isUndefined(response.responseJSON.error)) {
                        console.error(response.responseJSON.error);
                    }
                    messanger.notificationFlashMessage('error', __('Could not save workflow.'));
                }
            });
        },

        validateConfiguration: function() {
            // workflow label should be defined
            if (!this.model.get('label')) {
                messanger.notificationFlashMessage('error', __('Could not save workflow. Please set workflow name.'));
                return false;
            }

            // related entity should be defined
            if (!this.model.get('entity')) {
                messanger.notificationFlashMessage('error', __('Could not save workflow. Please set related entity.'));
                return false;
            }

            // at least one step and one transition must exist
            if (this.model.get('steps').length <= 1 || this.model.get('transitions').length == 0) {
                messanger.notificationFlashMessage(
                    'error',
                    __('Could not save workflow. Please add at least one step and one transition.')
                );
                return false;
            }

            // should be defined either start step or at least one start transition
            if (!this.model.get('start_step') && _.isEmpty(this._getStartingPoint().get('allowed_transitions'))) {
                messanger.notificationFlashMessage(
                    'error',
                    __('Could not save workflow. Please either set default step or add transitions to starting point.')
                );
                return false;
            }

            return true;
        },

        _createStartingPoint: function() {
            var startStepModel = new StepModel({
                'name': 'step:starting_point',
                'label': __('(Start)'),
                'order': -1,
                '_is_start': true
            });

            startStepModel
                .getAllowedTransitions(this.model)
                .reset(this.model.getStartTransitions());

            return startStepModel;
        },

        _getStartingPoint: function() {
            return this.model.getStepByName('step:starting_point');
        },

        renderSteps: function() {
            this.stepListView.render();
        },

        addNewStep: function() {
            var step = new StepModel();
            this.openManageStepForm(step);
        },

        addNewTransition: function() {
            this.addNewStepTransition(null);
        },

        cloneTransition: function(transition, step) {
            var clonedTransition = this.model.cloneTransition(transition, true);
            this.openManageTransitionForm(clonedTransition, step);
        },

        removeTransition: function(model) {
            this._removeHandler(model, __('Are you sure you want to delete this transition?'));
        },

        _removeHandler: function(model, message) {
            var confirm = new Confirmation({
                content: message
            });
            confirm.on('ok', function () {
                model.destroy();
            });
            confirm.open();
        },

        addNewStepTransition: function(step) {
            var transition = new TransitionModel();
            this.openManageTransitionForm(transition, step);
        },

        _showModalMessage: function(message, title, okText) {
            var confirm = new Confirmation({
                title: title || '',
                content: message,
                okText: okText || __('Ok'),
                allowCancel: false
            });
            confirm.open();
        },

        openManageTransitionForm: function(transition, step_from) {
            if (this.model.get('steps').length == 1) {
                this._showModalMessage(__('At least one step should be added to add transition.'), __('Warning'));
                return;
            }
            if (!this.$entitySelectEl.val()) {
                this._showModalMessage(__('Related entity must be selected to add transition.'), __('Warning'));
                return;
            }

            var transitionEditView = new TransitionEditForm({
                'model': transition,
                'workflow': this.model,
                'step_from': step_from,
                'entity_select_el': this.$entitySelectEl,
                'entities': this.options.entities,
                'workflowContainer': this.$el
            });
            transitionEditView.on('transitionAdd', this.addTransition, this);
            transitionEditView.render();
        },

        addTransition: function(transition, stepFrom) {
            if (!this.model.get('transitions').get(transition.cid)) {
                if (_.isString(stepFrom)) {
                    stepFrom = this.model.getStepByName(stepFrom);
                }
                transition.set('is_start', stepFrom.get('_is_start'));

                this.model
                    .get('transitions')
                    .add(transition);

                stepFrom
                    .getAllowedTransitions(this.model)
                    .add(transition);
            }
        },

        openManageStepForm: function(step) {
            if (!this.$entitySelectEl.val()) {
                this._showModalMessage(__('Related entity must be selected to add step.'), __('Warning'));
                return;
            }

            var stepEditView = new StepEditView({
                'model': step,
                'workflow': this.model,
                'workflowContainer': this.$el
            });
            stepEditView.on('stepAdd', this.addStep, this);
            stepEditView.render();
        },

        cloneStep: function(step) {
            var clonedStep = this.model.cloneStep(step, true);
            this.openManageStepForm(clonedStep);
        },

        addStep: function(step) {
            if (!this.model.get('steps').get(step.cid)) {
                this.model.get('steps').add(step);
            }
        },

        removeStep: function(model) {
            this._removeHandler(model, __('Are you sure you want to delete this step?'));
        },

        onStepRemove: function(step) {
            //Deselect start_step if it was removed
            if (this.$startStepEl.val() == step.get('name')) {
                this.$startStepEl.select2('val', '');
            }
        },

        render: function() {
            this.renderSteps();
            return this;
        }
    });
});
<commit_msg>BAP-3725: After session expires user is not redirected to login after ajax requests    - fix issues in workflow<commit_after>/* global define */
define([
    'underscore', 'backbone', 'oroui/js/messenger', 'orotranslation/js/translator',
    'oroworkflow/js/workflow-management/step/view/list',
    'oroworkflow/js/workflow-management/step/model',
    'oroworkflow/js/workflow-management/transition/model',
    'oroworkflow/js/workflow-management/step/view/edit',
    'oroworkflow/js/workflow-management/transition/view/edit',
    'oroworkflow/js/workflow-management/helper',
    'oronavigation/js/navigation',
    'oroui/js/app',
    'oroui/js/delete-confirmation',
    'oroentity/js/fields-loader'
],
function(_, Backbone, messanger, __,
     StepsListView,
     StepModel,
     TransitionModel,
     StepEditView,
     TransitionEditForm,
     Helper,
     Navigation,
     app,
     Confirmation
) {
    'use strict';

    var $ = Backbone.$;

    /**
     * @export  oroworkflow/js/workflow-management
     * @class   oro.WorkflowManagement
     * @extends Backbone.View
     */
    return Backbone.View.extend({
        events: {
            'click .add-step-btn': 'addNewStep',
            'click .add-transition-btn': 'addNewTransition'
        },

        options: {
            stepsEl: null,
            model: null,
            entities: [],
            backUrl: null
        },

        initialize: function() {
            this.saveAndClose = false;

            _.each(this.model.get('steps').models, this.setWorkflow, this);
            _.each(this.model.get('transitions').models, this.setWorkflow, this);
            this.listenTo(this.model.get('steps'), 'add', this.setWorkflow);
            this.listenTo(this.model.get('transitions'), 'add', this.setWorkflow);

            this.addStartingPoint();
            this.initStartStepSelector();

            this.stepListView = new StepsListView({
                el: this.$(this.options.stepsEl),
                collection: this.model.get('steps'),
                workflow: this.model
            });
            this.$entitySelectEl = this.$('[name$="[related_entity]"]');
            this.initEntityFieldsLoader();
            this.initForm();

            this.listenTo(this.model, 'requestAddTransition', this.addNewStepTransition);
            this.listenTo(this.model, 'requestEditStep', this.openManageStepForm);
            this.listenTo(this.model, 'requestCloneStep', this.cloneStep);
            this.listenTo(this.model, 'requestRemoveStep', this.removeStep);
            this.listenTo(this.model.get('steps'), 'destroy', this.onStepRemove);

            this.listenTo(this.model, 'requestRemoveTransition', this.removeTransition);
            this.listenTo(this.model, 'requestCloneTransition', this.cloneTransition);
            this.listenTo(this.model, 'requestEditTransition', this.openManageTransitionForm);

            this.listenTo(this.model, 'change:entity', this.resetWorkflow);
        },

        initForm: function() {
            this.$el.on('submit', _.bind(this.saveConfiguration, this));
            this.model.url = this.$el.attr('action');

            this.$('[type="submit"]').on('click', _.bind(function() {
                this.saveAndClose = true;
            }, this));
            this.$('[data-action="save_and_stay"]').on('click', _.bind(function() {
                this.saveAndClose = false;
            }, this));
        },

        setWorkflow: function(item) {
            item.setWorkflow(this.model);
        },

        initStartStepSelector: function() {
            var getSteps = _.bind(function(query) {
                var steps = [];
                _.each(this.model.get('steps').models, function(step) {
                    // starting point is not allowed to be a start step
                    var stepLabel = step.get('label');
                    if (!step.get('_is_start')
                        && (!query.term || query.term == stepLabel || _.indexOf(stepLabel, query.term) !== -1)
                    ) {
                        steps.push({
                            'id': step.get('name'),
                            'text': step.get('label')
                        });
                    }
                }, this);

                query.callback({results: steps});
            }, this);

            this.$startStepEl = this.$('[name="start_step"]');

            var select2Options = {
                'allowClear': true,
                'query': getSteps,
                'placeholder': __('Choose step...'),
                'initSelection' : _.bind(function (element, callback) {
                    var startStep = this.model.getStepByName(element.val());
                    callback({
                        id: startStep.get('name'),
                        text: startStep.get('label')
                    });
                }, this)
            };

            this.$startStepEl.select2(select2Options);
        },

        initEntityFieldsLoader: function() {
            var confirm = new Confirmation({
                title: __('Change Entity Confirmation'),
                okText: __('Yes, I Agree'),
                content: __('Are you sure you want to change entity?')
            });
            confirm.on('ok', _.bind(function() {
                this.model.set('entity', this.$entitySelectEl.val());
            }, this));
            confirm.on('cancel', _.bind(function() {
                this.$entitySelectEl.select2('val', this.model.get('entity'));
            }, this));

            this.$entitySelectEl.fieldsLoader({
                router: 'oro_workflow_api_rest_entity_get',
                routingParams: {"with-relations": 1, "with-entity-details": 1, "deep-level": 2},
                confirm: confirm,
                requireConfirm: _.bind(function () {
                     return this.model.get('steps').length > 1 &&
                         (this.model.get('transitions').length
                            + this.model.get('transition_definitions').length
                            + this.model.get('attributes').length) > 0;
                }, this)
            });
            this.$entitySelectEl.on('change', _.bind(function() {
                if (!this.model.get('entity')) {
                    this.model.set('entity', this.$entitySelectEl.val());
                }
            }, this));

            this.$entitySelectEl.on('fieldsloadercomplete', _.bind(function(e) {
                this.createPathMapping($(e.target).data('fields'));
            }, this));
        },

        addStartingPoint: function() {
            this.model.get('steps').add(this._createStartingPoint());
        },

        resetWorkflow: function() {
            //Need to manually destroy collection elements to trigger all appropriate events
            var resetCollection = function(collection) {
                if (collection.length) {
                    for (var i = collection.length -1; i > -1; i--) {
                        collection.at(i).destroy();
                    }
                }
            };

            this.model.set('start_step', null);
            resetCollection(this.model.get('attributes'));
            resetCollection(this.model.get('steps'));
            resetCollection(this.model.get('transition_definitions'));
            resetCollection(this.model.get('transitions'));

            this.addStartingPoint();
        },

        createPathMapping: function(fields) {
            var rootAttributeName = this.model.get('entity_attribute');
            var mapping = {};

            var addMapping = _.bind(function(field, parentPropertyPath, parentFieldId) {
                var propertyPath = parentPropertyPath + '.' + field.name;
                var fieldIdParts = [];

                if (parentFieldId) {
                    fieldIdParts.push(parentFieldId);
                }

                var fieldIdName = field.name;
                if (field.hasOwnProperty('related_entity_name')) {
                    fieldIdName += '+' + field.related_entity_name;
                }
                fieldIdParts.push(fieldIdName);

                if (!field.hasOwnProperty('related_entity_name')) {
                    mapping[propertyPath] = fieldIdParts.join('::');
                }

                if (field.hasOwnProperty('related_entity_fields')) {
                    _.each(field.related_entity_fields, function(relatedField) {
                        addMapping(relatedField, propertyPath, fieldIdParts.join('::'));
                    });
                }
            });

            _.each(fields, function(field) {
                addMapping(field, rootAttributeName, "");
            });

            this.model.setPropertyPathToFieldIdMapping(mapping);
        },

        saveConfiguration: function(e) {
            e.preventDefault();
            if (!this.$el.valid()) {
                return;
            }

            var formData = Helper.getFormData(this.$el);
            formData.steps_display_ordered = formData.hasOwnProperty('steps_display_ordered');

            if (!this.model.get('name')) {
                this.model.set('name', Helper.getNameByString(formData.label, 'workflow_'));
            }
            this.model.set('label', formData.label);
            this.model.set('steps_display_ordered', formData.steps_display_ordered);
            this.model.set('entity', formData.related_entity);
            this.model.set('start_step', formData.start_step);

            if (!this.validateConfiguration()) {
                return;
            }

            var navigation = Navigation.getInstance();
            navigation.showLoading();
            this.model.save(null, {
                'success': _.bind(function() {
                    navigation.hideLoading();
                    if (this.saveAndClose) {
                        navigation.setLocation(this.options.backUrl);
                    }

                    messanger.notificationFlashMessage('success', __('Workflow saved.'));
                }, this),
                'error': function(model, response) {
                    navigation.hideLoading();
                    var jsonResponse = response.responseJSON || {};

                    if (app.debug && !_.isUndefined(console) && !_.isUndefined(jsonResponse.error)) {
                        console.error(jsonResponse.error);
                    }
                    messanger.notificationFlashMessage('error', __('Could not save workflow.'));
                }
            });
        },

        validateConfiguration: function() {
            // workflow label should be defined
            if (!this.model.get('label')) {
                messanger.notificationFlashMessage('error', __('Could not save workflow. Please set workflow name.'));
                return false;
            }

            // related entity should be defined
            if (!this.model.get('entity')) {
                messanger.notificationFlashMessage('error', __('Could not save workflow. Please set related entity.'));
                return false;
            }

            // at least one step and one transition must exist
            if (this.model.get('steps').length <= 1 || this.model.get('transitions').length == 0) {
                messanger.notificationFlashMessage(
                    'error',
                    __('Could not save workflow. Please add at least one step and one transition.')
                );
                return false;
            }

            // should be defined either start step or at least one start transition
            if (!this.model.get('start_step') && _.isEmpty(this._getStartingPoint().get('allowed_transitions'))) {
                messanger.notificationFlashMessage(
                    'error',
                    __('Could not save workflow. Please either set default step or add transitions to starting point.')
                );
                return false;
            }

            return true;
        },

        _createStartingPoint: function() {
            var startStepModel = new StepModel({
                'name': 'step:starting_point',
                'label': __('(Start)'),
                'order': -1,
                '_is_start': true
            });

            startStepModel
                .getAllowedTransitions(this.model)
                .reset(this.model.getStartTransitions());

            return startStepModel;
        },

        _getStartingPoint: function() {
            return this.model.getStepByName('step:starting_point');
        },

        renderSteps: function() {
            this.stepListView.render();
        },

        addNewStep: function() {
            var step = new StepModel();
            this.openManageStepForm(step);
        },

        addNewTransition: function() {
            this.addNewStepTransition(null);
        },

        cloneTransition: function(transition, step) {
            var clonedTransition = this.model.cloneTransition(transition, true);
            this.openManageTransitionForm(clonedTransition, step);
        },

        removeTransition: function(model) {
            this._removeHandler(model, __('Are you sure you want to delete this transition?'));
        },

        _removeHandler: function(model, message) {
            var confirm = new Confirmation({
                content: message
            });
            confirm.on('ok', function () {
                model.destroy();
            });
            confirm.open();
        },

        addNewStepTransition: function(step) {
            var transition = new TransitionModel();
            this.openManageTransitionForm(transition, step);
        },

        _showModalMessage: function(message, title, okText) {
            var confirm = new Confirmation({
                title: title || '',
                content: message,
                okText: okText || __('Ok'),
                allowCancel: false
            });
            confirm.open();
        },

        openManageTransitionForm: function(transition, step_from) {
            if (this.model.get('steps').length == 1) {
                this._showModalMessage(__('At least one step should be added to add transition.'), __('Warning'));
                return;
            }
            if (!this.$entitySelectEl.val()) {
                this._showModalMessage(__('Related entity must be selected to add transition.'), __('Warning'));
                return;
            }

            var transitionEditView = new TransitionEditForm({
                'model': transition,
                'workflow': this.model,
                'step_from': step_from,
                'entity_select_el': this.$entitySelectEl,
                'entities': this.options.entities,
                'workflowContainer': this.$el
            });
            transitionEditView.on('transitionAdd', this.addTransition, this);
            transitionEditView.render();
        },

        addTransition: function(transition, stepFrom) {
            if (!this.model.get('transitions').get(transition.cid)) {
                if (_.isString(stepFrom)) {
                    stepFrom = this.model.getStepByName(stepFrom);
                }
                transition.set('is_start', stepFrom.get('_is_start'));

                this.model
                    .get('transitions')
                    .add(transition);

                stepFrom
                    .getAllowedTransitions(this.model)
                    .add(transition);
            }
        },

        openManageStepForm: function(step) {
            if (!this.$entitySelectEl.val()) {
                this._showModalMessage(__('Related entity must be selected to add step.'), __('Warning'));
                return;
            }

            var stepEditView = new StepEditView({
                'model': step,
                'workflow': this.model,
                'workflowContainer': this.$el
            });
            stepEditView.on('stepAdd', this.addStep, this);
            stepEditView.render();
        },

        cloneStep: function(step) {
            var clonedStep = this.model.cloneStep(step, true);
            this.openManageStepForm(clonedStep);
        },

        addStep: function(step) {
            if (!this.model.get('steps').get(step.cid)) {
                this.model.get('steps').add(step);
            }
        },

        removeStep: function(model) {
            this._removeHandler(model, __('Are you sure you want to delete this step?'));
        },

        onStepRemove: function(step) {
            //Deselect start_step if it was removed
            if (this.$startStepEl.val() == step.get('name')) {
                this.$startStepEl.select2('val', '');
            }
        },

        render: function() {
            this.renderSteps();
            return this;
        }
    });
});
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */

'use strict';

const endpoint = require('kronos-endpoint'),
	llm = require('loglevel-mixin'),
	stm = require('statetransition-mixin'),
	merge = require('merge-deep');

const actions = stm.prepareActions({
	start: {
		stopped: {
			target: 'running',
			during: 'starting',
			timeout: 5000
		}
	},
	stop: {
		running: {
			target: 'stopped',
			during: 'stopping',
			timeout: 5000
		},
		starting: {
			target: 'stopped',
			during: 'stopping',
			timeout: 5000
		}
	},
	remove: {
		stopped: {
			target: 'removed'
		}
	}
});

// Steps plain attributes without special handling
// may be extended by some properties like writable,...
const ATTRIBUTES = ['description'];

const BaseStep = {
	'name': 'kronos-step',
	'description': 'This step is the base class for step implementations',

	/**
	 * Creates the properties for a given object
	 * @param {Object} manager The kronos service manager
	 * @param {String} (mandatory) name The Name of the step
	 * @param {Object} (optional) stepDefinition The step definition
	 *
	 */
	prepareProperties(manager, name, stepConfiguration) {
		let endpoints = {};

		// TODO ther is no better way ?
		const type = stepConfiguration.type ? stepConfiguration.type : this.name;

		const props = {
			name: {
				value: name
			},
			type: {
				value: type
			},
			endpoints: {
				value: endpoints
			},
			manager: {
				value: manager
			},
			/**
			 * @return {Boolean} if step is in a running state
			 */
			isRunning: {
				get: function () {
					return this.state === 'running' || this.state === 'starting';
				}
			}
		};

		ATTRIBUTES.forEach(a => {
			if (stepConfiguration[a] !== undefined) {
				props[a] = {
					value: stepConfiguration[a]
				};
			}
		});

		return props;
	},

	/**
	 * Called to initialize step
	 * Please note 'this' is not jet present
	 */
	initialize(manager, name, stepConfiguration, props) {},

	/**
	 * Called when step instance properties are present.
	 *
	 */
	finalize(manager, stepConfiguration) {},

	/**
	 * This method could be overwritten by a derived object to setup default endpoints.
	 * This method may be overwritten by derived classes.
	 * This will be called by the initialize method
	 * @param {Object} stepConfiguration The default step configuration
	 * @api protected
	 */
	createPredefinedEndpoints(stepConfiguration) {
		if (this.endpoints.log) return;

		const logEndpoint = new endpoint.SendEndpointDefault('log', this);
		if (this.manager.services && this.manager.services.logger) {
			logEndpoint.connected = this.manager.services.logger.endpoints.log;
		}
		this.addEndpoint(logEndpoint);
	},

	/**
	 * This function mixes the endpoint definitions of a step.
	 * So that extensions comming from the flow will be mixed into
	 * the definition from the step (The definition from the prototype).
	 * Only the 'endpoints' part will be extended
	 *
	 * @param def The step definition from the flow or step itslef.
	 * @return definition The new extended step definition
	 */
	inheritEndpoints(def) {
		const prototype = Object.getPrototypeOf(this);
		if (prototype && prototype.endpoints) {
			if (def && def.endpoints) {
				// before we can merge endpoints of type string needs to be converted
				for (const endpointName in def.endpoints) {
					const endpointDefinition = def.endpoints[endpointName];
					if (typeof endpointDefinition === 'string') {
						def.endpoints[endpointName] = {
							'target': endpointDefinition
						};
					}
				}

				// need to mix the definition
				def.endpoints = merge({}, prototype.endpoints, def.endpoints);
			} else {
				if (!def) {
					def = {};
				}
				// just take the prototype definition
				def.endpoints = prototype.endpoints;
			}
		}
		return def;
	},

	/**
	 * Creates the endpoint objects defined as a combination from
	 * implementation and definition
	 * @param {Object} def The step configuration
	 * @api protected
	 */
	createEndpoints(def) {
		if (def && def.endpoints) {
			Object.keys(def.endpoints).forEach(name =>
				this.createEndpoint(name, def.endpoints[name]));
		}
	},

	createEndpoint(name, def) {
		let ep;
		let options = {};

		if (def.opposite) {
			options.createOpposite = true;
		}

		if (def.in) ep = new endpoint.ReceiveEndpoint(name, this, options);
		if (def.out) ep = new endpoint.SendEndpoint(name, this, options);

		this.addEndpoint(ep);

		if (def.interceptors) {
			ep.interceptors = def.interceptors.map(icDef => this.manager.createInterceptorInstanceFromConfig(icDef, ep));
		}
	},

	/**
	 * @param {Endpoint} ep
	 */
	addEndpoint(ep) {
		this.endpoints[ep.name] = ep;
	},

	/**
	 * removes a endpoint
	 * @param {string} name
	 */
	removeEndpoint(name) {
		delete this.endpoints[name];
	},

	/**
	 * Sends a 'stepStateChanged' event to the manager.
	 * arguments are:
	 *  step,oldState,newState
	 * @param {String} oldState
	 * @param {String} newState
	 */
	stateChanged(oldState, newState) {
		this.trace(level => ({
			message: 'transitioned',
			old: oldState,
			new: newState
		}));
		this.manager.emit('stepStateChanged', this, oldState, newState);
	},

	stateTransitionRejection(rejected, newState) {
		const p = stm.BaseMethods.stateTransitionRejection.call(this, rejected, newState);
		this.error(`${this.name} transition rejected ${rejected}`);
		return p;
	},

	/**
	 * Returns the string representation of this step
	 * @return {String} human readable name
	 */
	toString() {
		return this.name;
	},

	/**
	 * Deliver json representation
	 * @param {Object} options
	 *  with the following flags:
	 *    includeRuntimeInfo - include runtime informtion like state
	 *    includeName - name of the step
	 *    includeDefaults - include all properties also the inherited and not overwittern ones
	 * @return {Object) json representation
	 */
	toJSONWithOptions(options) {
		const json = {
			type: this.type,
			endpoints: {}
		};

		if (options.includeName) {
			json.name = this.name;
		}

		if (options.includeRuntimeInfo) {
			json.state = this.state;
			json.logLevel = this.logLevel;
		}

		for (const endpointName in this.endpoints) {
			const ep = this.endpoints[endpointName];
			if (ep.isDefault) {
				if (options.includeDefaults) {
					json.endpoints[endpointName] = ep.toJSON();
				}
			} else {
				json.endpoints[endpointName] = ep.toJSON();
			}
		}

		ATTRIBUTES.forEach(a => {
			json[a] = this[a];
		});

		return json;
	},

	toJSON() {
		return this.toJSONWithOptions({
			includeRuntimeInfo: false,
			includeDefaults: false,
			includeName: false
		});
	},

	/**
	 * Deliver an identifier suitable as target name.
	 * return {String} endpoint identifier
	 */
	endpointIdentifier(ep) {
		return `${this.name}${this.endpointParentSeparator}${ep.name}`;
	},

	/**
	 * @return {String} separator between service name and endpoint name
	 **/
	get endpointParentSeparator() {
		return '/';
	},

	/**
	 * @return {Step} newly created step
	 */
	createInstance(stepDefinition, manager) {
		if (!manager) {
			throw new Error(`No Manager given in 'createInstance' for step '${stepDefinition.name}'`);
		}

		const props = this.prepareProperties(manager, stepDefinition.name, stepDefinition);
		this.initialize(manager, stepDefinition.name, stepDefinition, props);
		const newInstance = Object.create(this, props);

		stm.defineStateTransitionProperties(newInstance, actions, 'stopped');

		// get the default loglevel for this step
		let logLevel = llm.defaultLogLevels[stepDefinition.logLevel] || llm.defaultLogLevels.info;

		// create the properties to store the loglevel
		llm.defineLogLevelProperties(newInstance, llm.defaultLogLevels, logLevel);

		// mix the endpoints from the prototype with the new definition
		stepDefinition = newInstance.inheritEndpoints(stepDefinition);

		newInstance.createEndpoints(stepDefinition);
		newInstance.createPredefinedEndpoints(stepDefinition);
		newInstance.finalize(stepDefinition);

		return newInstance;
	},

	log(level, arg) {
		const logevent = llm.makeLogEvent(level, arg, {
			'step-type': this.type,
			'step-name': this.name
		});

		if (typeof arg === 'object') {
			if (arg instanceof Error) {
				// The 'arg' is an Error Object
				const stack = arg.stack;

				if (stack.getLineNumber) {
					logevent.line = stack.getLineNumber();
				}
				if (stack.getFileName) {
					logevent._file_name = stack.getFileName();
				}
				logevent._error_name = arg.name;
				logevent.short_message = arg.message;
			} else {
				// The 'arg' is a normal object. Copy all the properties

				if (arg.short_message !== undefined) {
					logevent.short_message = arg.short_message;
					delete(arg.short_message);
				}
				if (arg.full_message !== undefined) {
					logevent.full_message = arg.full_message;
					delete(arg.full_message);
				}
			}
		}
		if (this.endpoints.log && this.endpoints.log.isConnected) {
			this.endpoints.log.receive(logevent);
		} else {
			console.log(logevent);
		}
	}
};

// define the logger methods
llm.defineLoggerMethods(BaseStep, llm.defaultLogLevels);

stm.defineActionMethods(BaseStep, actions, true);

module.exports.BaseStep = BaseStep;
<commit_msg>fix(state): allow stopping in failed state<commit_after>/* jslint node: true, esnext: true */

'use strict';

const endpoint = require('kronos-endpoint'),
	llm = require('loglevel-mixin'),
	stm = require('statetransition-mixin'),
	merge = require('merge-deep');

const actions = stm.prepareActions({
	start: {
		stopped: {
			target: 'running',
			during: 'starting',
			timeout: 5000
		}
	},
	stop: {
		running: {
			target: 'stopped',
			during: 'stopping',
			timeout: 5000
		},
		starting: {
			target: 'stopped',
			during: 'stopping',
			timeout: 5000
		},
		failed: {
			target: 'stopped',
			during: 'stopping',
			timeout: 1000
		}
	},
	remove: {
		stopped: {
			target: 'removed'
		}
	}
});

// Steps plain attributes without special handling
// may be extended by some properties like writable,...
const ATTRIBUTES = ['description'];

const BaseStep = {
	'name': 'kronos-step',
	'description': 'This step is the base class for step implementations',

	/**
	 * Creates the properties for a given object
	 * @param {Object} manager The kronos service manager
	 * @param {String} (mandatory) name The Name of the step
	 * @param {Object} (optional) stepDefinition The step definition
	 *
	 */
	prepareProperties(manager, name, stepConfiguration) {
		let endpoints = {};

		// TODO ther is no better way ?
		const type = stepConfiguration.type ? stepConfiguration.type : this.name;

		const props = {
			name: {
				value: name
			},
			type: {
				value: type
			},
			endpoints: {
				value: endpoints
			},
			manager: {
				value: manager
			},
			/**
			 * @return {Boolean} if step is in a running state
			 */
			isRunning: {
				get: function () {
					return this.state === 'running' || this.state === 'starting';
				}
			}
		};

		ATTRIBUTES.forEach(a => {
			if (stepConfiguration[a] !== undefined) {
				props[a] = {
					value: stepConfiguration[a]
				};
			}
		});

		return props;
	},

	/**
	 * Called to initialize step
	 * Please note 'this' is not jet present
	 */
	initialize(manager, name, stepConfiguration, props) {},

	/**
	 * Called when step instance properties are present.
	 *
	 */
	finalize(manager, stepConfiguration) {},

	/**
	 * This method could be overwritten by a derived object to setup default endpoints.
	 * This method may be overwritten by derived classes.
	 * This will be called by the initialize method
	 * @param {Object} stepConfiguration The default step configuration
	 * @api protected
	 */
	createPredefinedEndpoints(stepConfiguration) {
		if (this.endpoints.log) return;

		const logEndpoint = new endpoint.SendEndpointDefault('log', this);
		if (this.manager.services && this.manager.services.logger) {
			logEndpoint.connected = this.manager.services.logger.endpoints.log;
		}
		this.addEndpoint(logEndpoint);
	},

	/**
	 * This function mixes the endpoint definitions of a step.
	 * So that extensions comming from the flow will be mixed into
	 * the definition from the step (The definition from the prototype).
	 * Only the 'endpoints' part will be extended
	 *
	 * @param def The step definition from the flow or step itslef.
	 * @return definition The new extended step definition
	 */
	inheritEndpoints(def) {
		const prototype = Object.getPrototypeOf(this);
		if (prototype && prototype.endpoints) {
			if (def && def.endpoints) {
				// before we can merge endpoints of type string needs to be converted
				for (const endpointName in def.endpoints) {
					const endpointDefinition = def.endpoints[endpointName];
					if (typeof endpointDefinition === 'string') {
						def.endpoints[endpointName] = {
							'target': endpointDefinition
						};
					}
				}

				// need to mix the definition
				def.endpoints = merge({}, prototype.endpoints, def.endpoints);
			} else {
				if (!def) {
					def = {};
				}
				// just take the prototype definition
				def.endpoints = prototype.endpoints;
			}
		}
		return def;
	},

	/**
	 * Creates the endpoint objects defined as a combination from
	 * implementation and definition
	 * @param {Object} def The step configuration
	 * @api protected
	 */
	createEndpoints(def) {
		if (def && def.endpoints) {
			Object.keys(def.endpoints).forEach(name =>
				this.createEndpoint(name, def.endpoints[name]));
		}
	},

	createEndpoint(name, def) {
		let ep;
		let options = {};

		if (def.opposite) {
			options.createOpposite = true;
		}

		if (def.in) ep = new endpoint.ReceiveEndpoint(name, this, options);
		if (def.out) ep = new endpoint.SendEndpoint(name, this, options);

		this.addEndpoint(ep);

		if (def.interceptors) {
			ep.interceptors = def.interceptors.map(icDef => this.manager.createInterceptorInstanceFromConfig(icDef, ep));
		}
	},

	/**
	 * @param {Endpoint} ep
	 */
	addEndpoint(ep) {
		this.endpoints[ep.name] = ep;
	},

	/**
	 * removes a endpoint
	 * @param {string} name
	 */
	removeEndpoint(name) {
		delete this.endpoints[name];
	},

	/**
	 * Sends a 'stepStateChanged' event to the manager.
	 * arguments are:
	 *  step,oldState,newState
	 * @param {String} oldState
	 * @param {String} newState
	 */
	stateChanged(oldState, newState) {
		this.trace(level => ({
			message: 'transitioned',
			old: oldState,
			new: newState
		}));
		this.manager.emit('stepStateChanged', this, oldState, newState);
	},

	stateTransitionRejection(rejected, newState) {
		const p = stm.BaseMethods.stateTransitionRejection.call(this, rejected, newState);
		this.error(`${this.name} transition rejected ${rejected}`);
		return p;
	},

	/**
	 * Returns the string representation of this step
	 * @return {String} human readable name
	 */
	toString() {
		return this.name;
	},

	/**
	 * Deliver json representation
	 * @param {Object} options
	 *  with the following flags:
	 *    includeRuntimeInfo - include runtime informtion like state
	 *    includeName - name of the step
	 *    includeDefaults - include all properties also the inherited and not overwittern ones
	 * @return {Object) json representation
	 */
	toJSONWithOptions(options) {
		const json = {
			type: this.type,
			endpoints: {}
		};

		if (options.includeName) {
			json.name = this.name;
		}

		if (options.includeRuntimeInfo) {
			json.state = this.state;
			json.logLevel = this.logLevel;
		}

		for (const endpointName in this.endpoints) {
			const ep = this.endpoints[endpointName];
			if (ep.isDefault) {
				if (options.includeDefaults) {
					json.endpoints[endpointName] = ep.toJSON();
				}
			} else {
				json.endpoints[endpointName] = ep.toJSON();
			}
		}

		ATTRIBUTES.forEach(a => {
			json[a] = this[a];
		});

		return json;
	},

	toJSON() {
		return this.toJSONWithOptions({
			includeRuntimeInfo: false,
			includeDefaults: false,
			includeName: false
		});
	},

	/**
	 * Deliver an identifier suitable as target name.
	 * return {String} endpoint identifier
	 */
	endpointIdentifier(ep) {
		return `${this.name}${this.endpointParentSeparator}${ep.name}`;
	},

	/**
	 * @return {String} separator between service name and endpoint name
	 **/
	get endpointParentSeparator() {
		return '/';
	},

	/**
	 * @return {Step} newly created step
	 */
	createInstance(stepDefinition, manager) {
		if (!manager) {
			throw new Error(`No Manager given in 'createInstance' for step '${stepDefinition.name}'`);
		}

		const props = this.prepareProperties(manager, stepDefinition.name, stepDefinition);
		this.initialize(manager, stepDefinition.name, stepDefinition, props);
		const newInstance = Object.create(this, props);

		stm.defineStateTransitionProperties(newInstance, actions, 'stopped');

		// get the default loglevel for this step
		let logLevel = llm.defaultLogLevels[stepDefinition.logLevel] || llm.defaultLogLevels.info;

		// create the properties to store the loglevel
		llm.defineLogLevelProperties(newInstance, llm.defaultLogLevels, logLevel);

		// mix the endpoints from the prototype with the new definition
		stepDefinition = newInstance.inheritEndpoints(stepDefinition);

		newInstance.createEndpoints(stepDefinition);
		newInstance.createPredefinedEndpoints(stepDefinition);
		newInstance.finalize(stepDefinition);

		return newInstance;
	},

	log(level, arg) {
		const logevent = llm.makeLogEvent(level, arg, {
			'step-type': this.type,
			'step-name': this.name
		});

		if (typeof arg === 'object') {
			if (arg instanceof Error) {
				// The 'arg' is an Error Object
				const stack = arg.stack;

				if (stack.getLineNumber) {
					logevent.line = stack.getLineNumber();
				}
				if (stack.getFileName) {
					logevent._file_name = stack.getFileName();
				}
				logevent._error_name = arg.name;
				logevent.short_message = arg.message;
			} else {
				// The 'arg' is a normal object. Copy all the properties

				if (arg.short_message !== undefined) {
					logevent.short_message = arg.short_message;
					delete(arg.short_message);
				}
				if (arg.full_message !== undefined) {
					logevent.full_message = arg.full_message;
					delete(arg.full_message);
				}
			}
		}
		if (this.endpoints.log && this.endpoints.log.isConnected) {
			this.endpoints.log.receive(logevent);
		} else {
			console.log(logevent);
		}
	}
};

// define the logger methods
llm.defineLoggerMethods(BaseStep, llm.defaultLogLevels);

stm.defineActionMethods(BaseStep, actions, true);

module.exports.BaseStep = BaseStep;
<|endoftext|>


<commit_before>import React from 'react';
import { shallow } from 'enzyme';
import { Message } from 'retranslate';

import { FormattedMessage } from 'react-intl';
import FundTransferTable from './FundTransferTable';

describe('Fund transfer table', () => {
  let component;

  beforeEach(() => {
    component = shallow(<FundTransferTable />);
  });

  it('renders a header', () => {
    expect(
      component.contains(

            <FormattedMessage id="confirm.mandate.current.fund" />

            <FormattedMessage id="confirm.mandate.percentage" />

            <FormattedMessage id="confirm.mandate.future.fund" />

,
      ),
    ).toBe(true);
  });

  it('renders an exchange row for every selection', () => {
    const selections = [
      {
        percentage: 0.5,
        sourceFundIsin: 'source 1',
        targetFundIsin: 'target 1',
        sourceFundName: 'a',
        targetFundName: 'c',
      },
      {
        percentage: 1,
        sourceFundIsin: 'source 2',
        targetFundIsin: 'target 2',
        sourceFundName: 'b',
        targetFundName: 'd',
      },
    ];
    component.setProps({ selections });
    selections.forEach((selection) =>
      expect(
        component.contains(

{selection.sourceFundName}

{selection.percentage * 100}%

              {selection.targetFundName}

,
        ),
      ).toBe(true),
    );
  });
});
<commit_msg>Remove unused import<commit_after>import React from 'react';
import { shallow } from 'enzyme';

import { FormattedMessage } from 'react-intl';
import FundTransferTable from './FundTransferTable';

describe('Fund transfer table', () => {
  let component;

  beforeEach(() => {
    component = shallow(<FundTransferTable />);
  });

  it('renders a header', () => {
    expect(
      component.contains(

            <FormattedMessage id="confirm.mandate.current.fund" />

            <FormattedMessage id="confirm.mandate.percentage" />

            <FormattedMessage id="confirm.mandate.future.fund" />

,
      ),
    ).toBe(true);
  });

  it('renders an exchange row for every selection', () => {
    const selections = [
      {
        percentage: 0.5,
        sourceFundIsin: 'source 1',
        targetFundIsin: 'target 1',
        sourceFundName: 'a',
        targetFundName: 'c',
      },
      {
        percentage: 1,
        sourceFundIsin: 'source 2',
        targetFundIsin: 'target 2',
        sourceFundName: 'b',
        targetFundName: 'd',
      },
    ];
    component.setProps({ selections });
    selections.forEach((selection) =>
      expect(
        component.contains(

{selection.sourceFundName}

{selection.percentage * 100}%

              {selection.targetFundName}

,
        ),
      ).toBe(true),
    );
  });
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
	"use strict";

	const logging = require("./logging");
	const settings = require("./settings");
	logging.message("Opened browser action");

	settings.onloaded(function(){
		var actions = document.getElementById("actions");

		[
			{
				label: "settings",
				icon: browser.extension.getURL("icons/pageAction-showOptions.svg"),
				action: function(){
					if (browser.runtime && browser.runtime.openOptionsPage){
						browser.runtime.openOptionsPage();
					}
					else {
						window.open(browser.extension.getURL("options/options.html"), "_blank");
					}
				}
			},
			{
				label: "test",
				advanced: true,
				icon: browser.extension.getURL("icons/browserAction-test.svg"),
				action: function(){
					window.open("https://canvasblocker.kkapsner.de/test", "_blank");
				}
			},
			{
				label: "review",
				icon: browser.extension.getURL("icons/browserAction-review.svg"),
				action: function(){
					window.open("https://addons.mozilla.org/de/firefox/addon/canvasblocker/reviews/", "_blank");
				}
			},
			{
				label: "reportIssue",
				icon: browser.extension.getURL("icons/browserAction-reportIssue.svg"),
				action: function(){
					window.open("https://github.com/kkapsner/CanvasBlocker/issues", "_blank");
				}
			},
		].forEach(function(action){
			logging.verbose("Action", action);
			if (action.advanced && !settings.displayAdvancedSettings){
				logging.verbose("Hiding advanced action");
				return;
			}
			var actionButton = document.createElement("button");
			actionButton.className = "action";

			var icon = document.createElement("img");
			icon.src = action.icon;

			actionButton.appendChild(icon);

			actionButton.appendChild(
				document.createTextNode(
					browser.i18n.getMessage("browserAction_" + action.label) || action.label
				)
			);
			actionButton.addEventListener("click", action.action);
			actions.appendChild(actionButton);
		});
	});
}());<commit_msg>Use non language specific review url<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
	"use strict";

	const logging = require("./logging");
	const settings = require("./settings");
	logging.message("Opened browser action");

	settings.onloaded(function(){
		var actions = document.getElementById("actions");

		[
			{
				label: "settings",
				icon: browser.extension.getURL("icons/pageAction-showOptions.svg"),
				action: function(){
					if (browser.runtime && browser.runtime.openOptionsPage){
						browser.runtime.openOptionsPage();
					}
					else {
						window.open(browser.extension.getURL("options/options.html"), "_blank");
					}
				}
			},
			{
				label: "test",
				advanced: true,
				icon: browser.extension.getURL("icons/browserAction-test.svg"),
				action: function(){
					window.open("https://canvasblocker.kkapsner.de/test", "_blank");
				}
			},
			{
				label: "review",
				icon: browser.extension.getURL("icons/browserAction-review.svg"),
				action: function(){
					window.open("https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/", "_blank");
				}
			},
			{
				label: "reportIssue",
				icon: browser.extension.getURL("icons/browserAction-reportIssue.svg"),
				action: function(){
					window.open("https://github.com/kkapsner/CanvasBlocker/issues", "_blank");
				}
			},
		].forEach(function(action){
			logging.verbose("Action", action);
			if (action.advanced && !settings.displayAdvancedSettings){
				logging.verbose("Hiding advanced action");
				return;
			}
			var actionButton = document.createElement("button");
			actionButton.className = "action";

			var icon = document.createElement("img");
			icon.src = action.icon;

			actionButton.appendChild(icon);

			actionButton.appendChild(
				document.createTextNode(
					browser.i18n.getMessage("browserAction_" + action.label) || action.label
				)
			);
			actionButton.addEventListener("click", action.action);
			actions.appendChild(actionButton);
		});
	});
}());<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import OnClickOutside from 'shared/components/OnClickOutside'
import classNames from 'classnames'
import _ from 'lodash'

const labelText = ({localSelectedItems, isOpen, label}) => {
  if (label) {
    return label
  } else if (localSelectedItems.length) {
    return localSelectedItems.map((s) => s).join(', ')
  }

  // TODO: be smarter about the text displayed here
  if (isOpen) {
    return '0 Selected'
  }
  return 'None'
}

class MultiSelectDropdown extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      localSelectedItems: this.props.selectedItems,
    }

    this.onSelect = ::this.onSelect
    this.onApplyFunctions = ::this.onApplyFunctions
  }

  componentWillReceiveProps(nextProps) {
    if (!_.isEqual(this.state.localSelectedItems, nextProps.selectedItems)) {
      this.setState({
        localSelectedItems: nextProps.selectedItems,
      })
    }
  }

  handleClickOutside() {
    this.setState({isOpen: false})
  }

  toggleMenu(e) {
    e.stopPropagation()
    this.setState({isOpen: !this.state.isOpen})
  }

  onSelect(item, e) {
    e.stopPropagation()

    const {localSelectedItems} = this.state

    let nextItems
    if (this.isSelected(item)) {
      nextItems = localSelectedItems.filter((i) => i !== item)
    } else {
      nextItems = localSelectedItems.concat(item)
    }

    this.setState({localSelectedItems: nextItems})
  }

  isSelected(item) {
    return this.state.localSelectedItems.indexOf(item) > -1
  }

  onApplyFunctions(e) {
    e.stopPropagation()

    this.setState({isOpen: false})
    this.props.onApply(this.state.localSelectedItems)
  }

  render() {
    const {localSelectedItems, isOpen} = this.state
    const {label} = this.props

    return (

            {
              labelText({localSelectedItems, isOpen, label})
            }

        {this.renderMenu()}

    )
  }

  renderMenu() {
    const {items} = this.props

    return (

Apply

          {items.map((listItem, i) => {
            return (

                {listItem}

            )
          })}

    )
  }
}

const {
  arrayOf,
  func,
  string,
} = PropTypes

MultiSelectDropdown.propTypes = {
  onApply: func.isRequired,
  items: arrayOf(string.isRequired).isRequired,
  selectedItems: arrayOf(string.isRequired).isRequired,
  label: string,
}

export default OnClickOutside(MultiSelectDropdown)
<commit_msg>Fix function application bug<commit_after>import React, {Component, PropTypes} from 'react'
import OnClickOutside from 'shared/components/OnClickOutside'
import classNames from 'classnames'
import _ from 'lodash'

const labelText = ({localSelectedItems, isOpen, label}) => {
  if (label) {
    return label
  } else if (localSelectedItems.length) {
    return localSelectedItems.map((s) => s).join(', ')
  }

  // TODO: be smarter about the text displayed here
  if (isOpen) {
    return '0 Selected'
  }
  return 'None'
}

class MultiSelectDropdown extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      localSelectedItems: this.props.selectedItems,
    }

    this.onSelect = ::this.onSelect
    this.onApplyFunctions = ::this.onApplyFunctions
  }

  handleClickOutside() {
    this.setState({isOpen: false})
  }

  toggleMenu(e) {
    e.stopPropagation()
    this.setState({isOpen: !this.state.isOpen})
  }

  onSelect(item, e) {
    e.stopPropagation()

    const {localSelectedItems} = this.state

    let nextItems
    if (this.isSelected(item)) {
      nextItems = localSelectedItems.filter((i) => i !== item)
    } else {
      nextItems = localSelectedItems.concat(item)
    }

    this.setState({localSelectedItems: nextItems})
  }

  isSelected(item) {
    return this.state.localSelectedItems.indexOf(item) > -1
  }

  onApplyFunctions(e) {
    e.stopPropagation()

    this.setState({isOpen: false})
    this.props.onApply(this.state.localSelectedItems)
  }

  render() {
    const {localSelectedItems, isOpen} = this.state
    const {label} = this.props

    return (

            {
              labelText({localSelectedItems, isOpen, label})
            }

        {this.renderMenu()}

    )
  }

  renderMenu() {
    const {items} = this.props

    return (

Apply

          {items.map((listItem, i) => {
            return (

                {listItem}

            )
          })}

    )
  }
}

const {
  arrayOf,
  func,
  string,
} = PropTypes

MultiSelectDropdown.propTypes = {
  onApply: func.isRequired,
  items: arrayOf(string.isRequired).isRequired,
  selectedItems: arrayOf(string.isRequired).isRequired,
  label: string,
}

export default OnClickOutside(MultiSelectDropdown)
<|endoftext|>