<commit_before>'use strict';

var
	transferableMessage = self.webkitPostMessage || self.postMessage,

	// enum
	MESSAGE_TYPES = {
		WORLDREPORT: 0,
		COLLISIONREPORT: 1
	},

	// temp variables
	_object,
	_vector,
	_transform,

	// functions
	public_functions = {},
	reportWorld,
	reportCollisions,

	// world variables
	fixedTimeStep, // used when calling stepSimulation
	last_simulation_time, // store in *seconds*
	world,

	// private cache
	_now,
	_objects = [],
	_objects_ammo = {},

	// object reporting
	REPORT_CHUNKSIZE, // report array is increased in increments of this chunk size

	WORLDREPORT_ITEMSIZE = 14, // how many float values each reported item needs
	worldreport,

	COLLISIONREPORT_ITEMSIZE = 2, // one float for each object id
	collisionreport;

public_functions.init = function( params ) {
	importScripts( params.ammo );
	_transform = new Ammo.btTransform;

	REPORT_CHUNKSIZE = params.reportsize || 50;
	worldreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
	worldreport[0] = MESSAGE_TYPES.WORLDREPORT;

	collisionreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of collisions to report + chunk size * # of values per object
	collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT;

	var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration,
		dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ),
		solver = new Ammo.btSequentialImpulseConstraintSolver,
		broadphase;

	if ( !params.broadphase ) params.broadphase = { type: 'dynamic' };
	switch ( params.broadphase.type ) {
		case 'sweepprune':
			broadphase = new Ammo.btAxisSweep3(
				new Ammo.btVector3( params.broadphase.aabbmin.x, params.broadphase.aabbmin.y, params.broadphase.aabbmax.z ),
				new Ammo.btVector3( params.broadphase.aabbmax.x, params.broadphase.aabbmax.y, params.broadphase.aabbmax.z )
			);
			break;

		case 'dynamic':
		default:
			broadphase = new Ammo.btDbvtBroadphase;
	}

	world = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration );

	fixedTimeStep = params.fixedTimeStep || 1 / 60;
};

public_functions.setGravity = function( description ) {
	world.setGravity(new Ammo.btVector3( description.x, description.y, description.z ));
};

public_functions.addObject = function( description ) {
	var localInertia, shape, motionState, rbInfo, body;

	_transform.setIdentity();

	localInertia = new Ammo.btVector3(0, 0, 0); // #TODO: localIntertia is the local inertia tensor, what does it do and should it be a parameter?

	switch ( description.type ) {
		case 'plane':
			shape = new Ammo.btStaticPlaneShape( new Ammo.btVector3( description.normal.x, description.normal.y, description.normal.z ) );
			break;

		case 'box':
			shape = new Ammo.btBoxShape(new Ammo.btVector3( description.width / 2, description.height / 2, description.depth / 2 ));
			break;

		case 'sphere':
			shape = new Ammo.btSphereShape( description.radius );
			break;

		case 'cylinder':
			shape = new Ammo.btCylinderShape(new Ammo.btVector3( description.width / 2, description.height / 2, description.depth / 2 ));
			break;

		case 'cone':
			shape = new Ammo.btConeShape( description.radius, description.height );
			break;

		case 'triangle':
			var i, triangle, triangle_mesh = new Ammo.btTriangleMesh;
			for ( i = 0; i < description.triangles.length; i++ ) {
				triangle = description.triangles[i];
				triangle_mesh.addTriangle(
					new Ammo.btVector3( triangle[0][0], triangle[0][1], triangle[0][2] ),
					new Ammo.btVector3( triangle[1][0], triangle[1][1], triangle[1][2] ),
					new Ammo.btVector3( triangle[2][0], triangle[2][1], triangle[2][2] ),
					true
				);
			}
			shape = new Ammo.btConvexTriangleMeshShape(
				triangle_mesh,
				true
			);
			break;

		case 'convex':
			var i, point, shape = new Ammo.btConvexHullShape;
			for ( i = 0; i < description.points.length; i++ ) {
				point = description.points[i];
				shape.addPoint( new Ammo.btVector3( point.x, point.y, point.z ) );
			}
			break;

		case 'heightfield':
			var ptr = Ammo.allocate( description.heightfield.length*4, "float", Ammo.ALLOC_NORMAL );
			for ( var f = 0; f < description.heightfield.length; f++ ) {
				//Ammo.setValue(ptr+(f<<2), description.heightfield[f][1], 'float');
				Ammo.setValue(ptr+(f<<2), Math.random() * 5, 'float');
			}

			shape = new Ammo.btHeightfieldTerrainShape( description.datapoints_x, description.datapoints_y, ptr, 1, -20, 20, 1, 0, false );
			//shape.setUseDiamondSubdivision( true );

			var localScaling = new Ammo.btVector3( description.width / (description.datapoints_x - 1), 1, description.length / (description.datapoints_y - 1) );
			shape.setLocalScaling(localScaling);

			break;

		default:
			// Not recognized
			return;
			break;
	}

	shape.calculateLocalInertia( description.mass, localInertia );

	motionState = new Ammo.btDefaultMotionState( _transform ); // #TODO: btDefaultMotionState supports center of mass offset as second argument - implement
	rbInfo = new Ammo.btRigidBodyConstructionInfo( description.mass, motionState, shape, localInertia );

	if ( typeof description.friction !== 'undefined' ) rbInfo.set_m_friction( description.friction );
	if ( typeof description.restitution !== 'undefined' ) rbInfo.set_m_restitution( description.restitution );

	body = new Ammo.btRigidBody( rbInfo );

	if ( typeof description.collision_flags !== 'undefined' ) {
		body.setCollisionFlags( description.collision_flags );
	}

	world.addRigidBody( body );

	body.id = description.id;
	_objects[ body.id ] = body;
	_objects_ammo[body.a] = body.id;
};

public_functions.removeObject = function( details ) {
	world.removeRigidBody( _objects[details.id] );
	delete _objects[details.id];
};

public_functions.updateTransform = function( details ) {
	_object = _objects[details.id];
	_object.getMotionState().getWorldTransform( _transform );

	if ( details.pos ) {
		_transform.setOrigin(new Ammo.btVector3( details.pos.x, details.pos.y, details.pos.z ));
	}

	if ( details.quat ) {
		_transform.setRotation(new Ammo.btQuaternion( details.quat.x, details.quat.y, details.quat.z, details.quat.w ));
	}

	_object.setWorldTransform( _transform );
	_object.activate();
};

public_functions.updateMass = function( details ) {
	// #TODO: changing a static object into dynamic is buggy
	_object = _objects[details.id];
	_object.setMassProps( details.mass, new Ammo.btVector3(0, 0, 0) );

	// Per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=&f=9&t=3663#p13816
	world.removeRigidBody( _object );
	world.addRigidBody( _object );
	_object.activate();
};

public_functions.applyCentralImpulse = function ( details ) {
	_objects[details.id].applyCentralImpulse(new Ammo.btVector3( details.x, details.y, details.z ));
	_objects[details.id].activate();
};

public_functions.applyImpulse = function ( details ) {
	_objects[details.id].applyImpulse(
		new Ammo.btVector3( details.impulse_x, details.impulse_y, details.impulse_z ),
		new Ammo.btVector3( details.x, details.y, details.z )
	);
	_objects[details.id].activate();
};

public_functions.setAngularVelocity = function ( details ) {
	_objects[details.id].setAngularVelocity(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
	_objects[details.id].activate();
};

public_functions.setLinearVelocity = function ( details ) {
	_objects[details.id].setLinearVelocity(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
	_objects[details.id].activate();
};

public_functions.setAngularFactor = function ( details ) {
	_objects[details.id].setAngularFactor(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
};

public_functions.setLinearFactor = function ( details ) {
	_objects[details.id].setLinearFactor(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
};

public_functions.setCcdMotionThreshold = function ( details ) {
	_objects[details.id].setCcdMotionThreshold( details.threshold );
};

public_functions.setCcdSweptSphereRadius = function ( details ) {
	_objects[details.id].setCcdSweptSphereRadius( details.radius );
};

public_functions.simulate = function( params ) {
	if ( world ) {
		params = params || {};
		_now = new Date().getTime() / 1000; // store in *seconds*

		if ( !params.timeStep ) {
			if ( last_simulation_time ) {
				params.timeStep = _now - last_simulation_time; // time since last simulation
			} else {
				params.timeStep = fixedTimeStep; // handle first frame
			}
		}

		params.maxSubSteps = params.maxSubSteps || Math.ceil( params.timeStep / fixedTimeStep ); // If maxSubSteps is not defined, keep the simulation fully up to date

		world.stepSimulation( params.timeStep, params.maxSubSteps, fixedTimeStep );
		reportWorld();
		reportCollisions();

		last_simulation_time = _now;
	}
};

reportWorld = function() {
	var i, object,
		transform = new Ammo.btTransform(), origin, rotation,
		offset = 0;

	if ( worldreport.length < 2 + _objects.length * WORLDREPORT_ITEMSIZE ) {
		worldreport = new Float32Array(worldreport.length + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
		worldreport[0] = MESSAGE_TYPES.WORLDREPORT;
	}

	worldreport[1] = _objects.length; // record how many objects we're reporting on

	for ( i = 0; i < worldreport[1]; i++ ) {
		object = _objects[i];

		// #TODO: we can't use center of mass transform when center of mass can change,
		//        but getMotionState().getWorldTransform() screws up on objects that have been moved
		//object.getMotionState().getWorldTransform( transform );
		transform = object.getCenterOfMassTransform();

		origin = transform.getOrigin();
		rotation = transform.getRotation();

		// add values to report
		offset = 2 + i * WORLDREPORT_ITEMSIZE;

		worldreport[ offset ] = object.id;

		worldreport[ offset + 1 ] = origin.x();
		worldreport[ offset + 2 ] = origin.y();
		worldreport[ offset + 3 ] = origin.z();

		worldreport[ offset + 4 ] = rotation.x();
		worldreport[ offset + 5 ] = rotation.y();
		worldreport[ offset + 6 ] = rotation.z();
		worldreport[ offset + 7 ] = rotation.w();

		_vector = object.getLinearVelocity();
		worldreport[ offset + 8 ] = _vector.x();
		worldreport[ offset + 9 ] = _vector.y();
		worldreport[ offset + 10 ] = _vector.z();

		_vector = object.getAngularVelocity();
		worldreport[ offset + 11 ] = _vector.x();
		worldreport[ offset + 12 ] = _vector.y();
		worldreport[ offset + 13 ] = _vector.z()
	}

	transferableMessage( worldreport, [worldreport.buffer] );
};

reportCollisions = function() {
	var i, offset,
		dp = world.getDispatcher(),
		num = dp.getNumManifolds(),
		manifold, num_contacts, j, pt,
		_collided = false;

	if ( collisionreport.length < 2 + num * COLLISIONREPORT_ITEMSIZE ) {
		collisionreport = new Float32Array(collisionreport.length + REPORT_CHUNKSIZE * COLLISIONREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
		collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT;
	}

	collisionreport[1] = 0; // how many collisions we're reporting on

	for ( i = 0; i < num; i++ ) {
		manifold = dp.getManifoldByIndexInternal( i );

		num_contacts = manifold.getNumContacts();
		if ( num_contacts == 0 ) continue;

		for ( j = 0; j < num_contacts; j++ ) {
			pt = manifold.getContactPoint( j );
			//if ( pt.getDistance() < 0 ) {
				offset = 2 + (collisionreport[1]++) * COLLISIONREPORT_ITEMSIZE;
				collisionreport[ offset ] = _objects_ammo[ manifold.getBody0() ];
				collisionreport[ offset + 1 ] = _objects_ammo[ manifold.getBody1() ];
				break;
			//}
		}
	}

	transferableMessage( collisionreport, [collisionreport.buffer] );
};

self.onmessage = function( event ) {

	if ( event.data instanceof Float32Array ) {
		// transferable object

		switch ( event.data[0] ) {
			case MESSAGE_TYPES.WORLDREPORT:
				worldreport = event.data;
				break;

			case MESSAGE_TYPES.COLLISIONREPORT:
				collisionreport = event.data;
				break;
		}

		return;
	}

	if ( event.data.cmd && public_functions[event.data.cmd] ) {
		if ( event.data.params.id !== undefined && _objects[event.data.params.id] === undefined && event.data.cmd !== 'addObject' ) return;
		public_functions[event.data.cmd]( event.data.params );
	}

};<commit_msg>Support for object removal - fixes #9<commit_after>'use strict';

var
	transferableMessage = self.webkitPostMessage || self.postMessage,

	// enum
	MESSAGE_TYPES = {
		WORLDREPORT: 0,
		COLLISIONREPORT: 1
	},

	// temp variables
	_object,
	_vector,
	_transform,

	// functions
	public_functions = {},
	reportWorld,
	reportCollisions,

	// world variables
	fixedTimeStep, // used when calling stepSimulation
	last_simulation_time, // store in *seconds*
	world,

	// private cache
	_now,
	_objects = {},
	_objects_ammo = {},
	_num_objects = 0,

	// object reporting
	REPORT_CHUNKSIZE, // report array is increased in increments of this chunk size

	WORLDREPORT_ITEMSIZE = 14, // how many float values each reported item needs
	worldreport,

	COLLISIONREPORT_ITEMSIZE = 2, // one float for each object id
	collisionreport;

public_functions.init = function( params ) {
	importScripts( params.ammo );
	_transform = new Ammo.btTransform;

	REPORT_CHUNKSIZE = params.reportsize || 50;
	worldreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
	worldreport[0] = MESSAGE_TYPES.WORLDREPORT;

	collisionreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of collisions to report + chunk size * # of values per object
	collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT;

	var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration,
		dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ),
		solver = new Ammo.btSequentialImpulseConstraintSolver,
		broadphase;

	if ( !params.broadphase ) params.broadphase = { type: 'dynamic' };
	switch ( params.broadphase.type ) {
		case 'sweepprune':
			broadphase = new Ammo.btAxisSweep3(
				new Ammo.btVector3( params.broadphase.aabbmin.x, params.broadphase.aabbmin.y, params.broadphase.aabbmax.z ),
				new Ammo.btVector3( params.broadphase.aabbmax.x, params.broadphase.aabbmax.y, params.broadphase.aabbmax.z )
			);
			break;

		case 'dynamic':
		default:
			broadphase = new Ammo.btDbvtBroadphase;
	}

	world = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration );

	fixedTimeStep = params.fixedTimeStep || 1 / 60;
};

public_functions.setGravity = function( description ) {
	world.setGravity(new Ammo.btVector3( description.x, description.y, description.z ));
};

public_functions.addObject = function( description ) {
	var localInertia, shape, motionState, rbInfo, body;

	_transform.setIdentity();

	localInertia = new Ammo.btVector3(0, 0, 0); // #TODO: localIntertia is the local inertia tensor, what does it do and should it be a parameter?

	switch ( description.type ) {
		case 'plane':
			shape = new Ammo.btStaticPlaneShape( new Ammo.btVector3( description.normal.x, description.normal.y, description.normal.z ) );
			break;

		case 'box':
			shape = new Ammo.btBoxShape(new Ammo.btVector3( description.width / 2, description.height / 2, description.depth / 2 ));
			break;

		case 'sphere':
			shape = new Ammo.btSphereShape( description.radius );
			break;

		case 'cylinder':
			shape = new Ammo.btCylinderShape(new Ammo.btVector3( description.width / 2, description.height / 2, description.depth / 2 ));
			break;

		case 'cone':
			shape = new Ammo.btConeShape( description.radius, description.height );
			break;

		case 'triangle':
			var i, triangle, triangle_mesh = new Ammo.btTriangleMesh;
			for ( i = 0; i < description.triangles.length; i++ ) {
				triangle = description.triangles[i];
				triangle_mesh.addTriangle(
					new Ammo.btVector3( triangle[0][0], triangle[0][1], triangle[0][2] ),
					new Ammo.btVector3( triangle[1][0], triangle[1][1], triangle[1][2] ),
					new Ammo.btVector3( triangle[2][0], triangle[2][1], triangle[2][2] ),
					true
				);
			}
			shape = new Ammo.btConvexTriangleMeshShape(
				triangle_mesh,
				true
			);
			break;

		case 'convex':
			var i, point, shape = new Ammo.btConvexHullShape;
			for ( i = 0; i < description.points.length; i++ ) {
				point = description.points[i];
				shape.addPoint( new Ammo.btVector3( point.x, point.y, point.z ) );
			}
			break;

		case 'heightfield':
			var ptr = Ammo.allocate( description.heightfield.length*4, "float", Ammo.ALLOC_NORMAL );
			for ( var f = 0; f < description.heightfield.length; f++ ) {
				//Ammo.setValue(ptr+(f<<2), description.heightfield[f][1], 'float');
				Ammo.setValue(ptr+(f<<2), Math.random() * 5, 'float');
			}

			shape = new Ammo.btHeightfieldTerrainShape( description.datapoints_x, description.datapoints_y, ptr, 1, -20, 20, 1, 0, false );
			//shape.setUseDiamondSubdivision( true );

			var localScaling = new Ammo.btVector3( description.width / (description.datapoints_x - 1), 1, description.length / (description.datapoints_y - 1) );
			shape.setLocalScaling(localScaling);

			break;

		default:
			// Not recognized
			return;
			break;
	}

	shape.calculateLocalInertia( description.mass, localInertia );

	motionState = new Ammo.btDefaultMotionState( _transform ); // #TODO: btDefaultMotionState supports center of mass offset as second argument - implement
	rbInfo = new Ammo.btRigidBodyConstructionInfo( description.mass, motionState, shape, localInertia );

	if ( typeof description.friction !== 'undefined' ) rbInfo.set_m_friction( description.friction );
	if ( typeof description.restitution !== 'undefined' ) rbInfo.set_m_restitution( description.restitution );

	body = new Ammo.btRigidBody( rbInfo );

	if ( typeof description.collision_flags !== 'undefined' ) {
		body.setCollisionFlags( description.collision_flags );
	}

	world.addRigidBody( body );

	body.id = description.id;
	_objects[ body.id ] = body;
	_objects_ammo[body.a] = body.id;
	_num_objects++;
};

public_functions.removeObject = function( details ) {
	world.removeRigidBody( _objects[details.id] );
	delete _objects[details.id];
	_num_objects--;
};

public_functions.updateTransform = function( details ) {
	_object = _objects[details.id];
	_object.getMotionState().getWorldTransform( _transform );

	if ( details.pos ) {
		_transform.setOrigin(new Ammo.btVector3( details.pos.x, details.pos.y, details.pos.z ));
	}

	if ( details.quat ) {
		_transform.setRotation(new Ammo.btQuaternion( details.quat.x, details.quat.y, details.quat.z, details.quat.w ));
	}

	_object.setWorldTransform( _transform );
	_object.activate();
};

public_functions.updateMass = function( details ) {
	// #TODO: changing a static object into dynamic is buggy
	_object = _objects[details.id];

	// Per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=&f=9&t=3663#p13816
	world.removeRigidBody( _object );
	_object.setMassProps( details.mass, new Ammo.btVector3(0, 0, 0) );
	world.addRigidBody( _object );
	_object.activate();
};

public_functions.applyCentralImpulse = function ( details ) {
	_objects[details.id].applyCentralImpulse(new Ammo.btVector3( details.x, details.y, details.z ));
	_objects[details.id].activate();
};

public_functions.applyImpulse = function ( details ) {
	_objects[details.id].applyImpulse(
		new Ammo.btVector3( details.impulse_x, details.impulse_y, details.impulse_z ),
		new Ammo.btVector3( details.x, details.y, details.z )
	);
	_objects[details.id].activate();
};

public_functions.setAngularVelocity = function ( details ) {
	_objects[details.id].setAngularVelocity(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
	_objects[details.id].activate();
};

public_functions.setLinearVelocity = function ( details ) {
	_objects[details.id].setLinearVelocity(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
	_objects[details.id].activate();
};

public_functions.setAngularFactor = function ( details ) {
	_objects[details.id].setAngularFactor(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
};

public_functions.setLinearFactor = function ( details ) {
	_objects[details.id].setLinearFactor(
		new Ammo.btVector3( details.x, details.y, details.z )
	);
};

public_functions.setCcdMotionThreshold = function ( details ) {
	_objects[details.id].setCcdMotionThreshold( details.threshold );
};

public_functions.setCcdSweptSphereRadius = function ( details ) {
	_objects[details.id].setCcdSweptSphereRadius( details.radius );
};

public_functions.simulate = function( params ) {
	if ( world ) {
		params = params || {};
		_now = new Date().getTime() / 1000; // store in *seconds*

		if ( !params.timeStep ) {
			if ( last_simulation_time ) {
				params.timeStep = _now - last_simulation_time; // time since last simulation
			} else {
				params.timeStep = fixedTimeStep; // handle first frame
			}
		}

		params.maxSubSteps = params.maxSubSteps || Math.ceil( params.timeStep / fixedTimeStep ); // If maxSubSteps is not defined, keep the simulation fully up to date

		world.stepSimulation( params.timeStep, params.maxSubSteps, fixedTimeStep );
		reportWorld();
		reportCollisions();

		last_simulation_time = _now;
	}
};

reportWorld = function() {
	var index, object,
		transform = new Ammo.btTransform(), origin, rotation,
		offset = 0,
		i = 0;

	if ( worldreport.length < 2 + _num_objects * WORLDREPORT_ITEMSIZE ) {
		worldreport = new Float32Array(worldreport.length + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
		worldreport[0] = MESSAGE_TYPES.WORLDREPORT;
	}

	worldreport[1] = _num_objects; // record how many objects we're reporting on

	//for ( i = 0; i < worldreport[1]; i++ ) {
	for ( index in _objects ) {
		if ( _objects.hasOwnProperty( index ) ) {
			object = _objects[index];

			// #TODO: we can't use center of mass transform when center of mass can change,
			//        but getMotionState().getWorldTransform() screws up on objects that have been moved
			//object.getMotionState().getWorldTransform( transform );
			transform = object.getCenterOfMassTransform();

			origin = transform.getOrigin();
			rotation = transform.getRotation();

			// add values to report
			offset = 2 + (i++) * WORLDREPORT_ITEMSIZE;

			worldreport[ offset ] = object.id;

			worldreport[ offset + 1 ] = origin.x();
			worldreport[ offset + 2 ] = origin.y();
			worldreport[ offset + 3 ] = origin.z();

			worldreport[ offset + 4 ] = rotation.x();
			worldreport[ offset + 5 ] = rotation.y();
			worldreport[ offset + 6 ] = rotation.z();
			worldreport[ offset + 7 ] = rotation.w();

			_vector = object.getLinearVelocity();
			worldreport[ offset + 8 ] = _vector.x();
			worldreport[ offset + 9 ] = _vector.y();
			worldreport[ offset + 10 ] = _vector.z();

			_vector = object.getAngularVelocity();
			worldreport[ offset + 11 ] = _vector.x();
			worldreport[ offset + 12 ] = _vector.y();
			worldreport[ offset + 13 ] = _vector.z()
		}
	}

	transferableMessage( worldreport, [worldreport.buffer] );
};

reportCollisions = function() {
	var i, offset,
		dp = world.getDispatcher(),
		num = dp.getNumManifolds(),
		manifold, num_contacts, j, pt,
		_collided = false;

	if ( collisionreport.length < 2 + num * COLLISIONREPORT_ITEMSIZE ) {
		collisionreport = new Float32Array(collisionreport.length + REPORT_CHUNKSIZE * COLLISIONREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
		collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT;
	}

	collisionreport[1] = 0; // how many collisions we're reporting on

	for ( i = 0; i < num; i++ ) {
		manifold = dp.getManifoldByIndexInternal( i );

		num_contacts = manifold.getNumContacts();
		if ( num_contacts == 0 ) continue;

		for ( j = 0; j < num_contacts; j++ ) {
			pt = manifold.getContactPoint( j );
			//if ( pt.getDistance() < 0 ) {
				offset = 2 + (collisionreport[1]++) * COLLISIONREPORT_ITEMSIZE;
				collisionreport[ offset ] = _objects_ammo[ manifold.getBody0() ];
				collisionreport[ offset + 1 ] = _objects_ammo[ manifold.getBody1() ];
				break;
			//}
		}
	}

	transferableMessage( collisionreport, [collisionreport.buffer] );
};

self.onmessage = function( event ) {

	if ( event.data instanceof Float32Array ) {
		// transferable object

		switch ( event.data[0] ) {
			case MESSAGE_TYPES.WORLDREPORT:
				worldreport = event.data;
				break;

			case MESSAGE_TYPES.COLLISIONREPORT:
				collisionreport = event.data;
				break;
		}

		return;
	}

	if ( event.data.cmd && public_functions[event.data.cmd] ) {
		if ( event.data.params.id !== undefined && _objects[event.data.params.id] === undefined && event.data.cmd !== 'addObject' ) return;
		public_functions[event.data.cmd]( event.data.params );
	}

};<|endoftext|>


<commit_before>'use strict';

//
//	PointingEvent
//

// Description:
// ポインティングイベントを表す。

/// 新しいインスタンスを初期化する。
function PointingEvent(sender, e)
{
	this.m_sender = sender;
	let bounds = sender.getBoundingDrawAreaRect();
	this.m_point = jsPoint(
		e.clientX - bounds.x,
		e.clientY - bounds.y
	);
	this.m_spkey = g_keyStateManager.getSpKeyState();
}

//
//	PictureCanvas
//

// ■ PictureCanvas
// canvasを重ねた描画領域を表す。
// 領域内にてマウスまたはタッチパネルイベントを受け付け、
// 登録された描画ツールに座標と対象レイヤーの情報を配送する。
// (描画のためのcanvas要素の操作は描画ツールに委譲する。)
// キャンバス全クリアとレイヤー合成の機能を備える。

/// 新しいインスタンスを初期化する。
function PictureCanvas()
{
	// DOMオブジェクト取得
	this.m_view_port = document.getElementById("viewport");
	this.m_layers = [
		document.getElementById("canvas_0"),
		document.getElementById("canvas_1"),
		document.getElementById("canvas_2")
	];
	this.m_joint_canvas = document.getElementById("joint_canvas");

	// 描画担当ツール
	this.m_drawer = null;

	// 描画担当ツールに引き渡す情報
	this.m_nTargetLayerNo = this.m_layers.length - 1;		// 描画対象レイヤー

	// ポインタデバイスのドラッグ状態
	this.m_bDragging = false;
	this.m_lastEvent = null;

	// イベントハンドラ登録
	register_pointer_event_handler(this.m_view_port, this);

	// レイヤーのサイズ調整
	this.fitCanvas();

	// レイヤーのオフセット取得
	// fitCanvas()呼出し後である必要がある。
	this.m_layer_left = parseInt(this.m_layers[0].style.left);
	this.m_layer_top = parseInt(this.m_layers[0].style.top);
}

/// イベントリスナ。
PictureCanvas.prototype.handleEvent = function(e)
{
	// console.log("Event: " + e.type);
	// console.dir(e);

	// 描画ツールに引き渡す情報を構成
	let mod_e = new PointingEvent(this, e);
	this.m_lastEvent = Object.assign({}, mod_e);		// 値コピー
	// console.dir(this.m_lastEvent);

	// イベント別処理
	switch (e.type) {
	case 'mousedown':
	case 'touchstart':
		// mouseupやtouchendを確実に補足するための登録
		g_pointManager.notifyPointStart(this, e);

		// 描画開始を通知
		this.m_bDragging = true;
		if (this.m_drawer) {
			this.m_drawer.OnDrawStart(mod_e);
		}
		break;
	case 'mouseup':
	case 'touchend':
		// 描画終了を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawEnd(mod_e);
		}
		this.m_bDragging = false;
		break;
	case 'mousemove':
	case 'touchmove':
		// ポインタの移動を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawing(mod_e);
		}
		break;
	default:
		break;
	}
}

/// 要素の絶対座標を返す。
PictureCanvas.prototype.getBoundingDrawAreaRect = function()
{
	let bounds = this.m_layers[0].getBoundingClientRect();
	bounds.x -= (this.m_layer_left + 1);	// +1はborder幅。本当は決め打ちではなく、取得した方が良い。
	bounds.y -= (this.m_layer_top + 1);
	return bounds;
}

/// 描画ツールを設定する。
PictureCanvas.prototype.setDrawer = function(drawer)
{
	assert(!this.m_bDragging);
	let prev = this.m_drawer;
	this.m_drawer = drawer;
	return prev;
}

/// カレントレイヤーを変更する。
PictureCanvas.prototype.changeLayer = function(layerNo)
{
	assert(1 <= layerNo && layerNo < this.m_layers.length);
	m_nTargetLayerNo = layerIdx;
}

/// カレントレイヤーを取得する。
PictureCanvas.prototype.getLayer = function()
{
	return this.m_layers[this.m_nTargetLayerNo];
}

/// キャンバスを全クリアする。
PictureCanvas.prototype.eraseCanvas = function()
{
	erase_canvas(this.m_layers);
	// {	/*UTEST*/	// アンチエリアシング無し描画テスト
	// 	let ctx = this.m_layers[0].getContext('2d');
	// 	// ctx.strokeStyle = "#000000";
	// 	ctx.fillStyle = "#000000";
	// 	ctx.beginPath();
	// 	draw_line_1px(0.5, 0.5, 200.5, 200.5, ctx, 1);
	// 	ctx.stroke();
	// 	ctx.closePath();
	// }
}

/// 全レイヤーを合成する。
PictureCanvas.prototype.getJointImage = function(dstCanvas)
{
	get_joint_image(this.m_layers, dstCanvas);
}

/// View portをキャンバスにfitさせる。
PictureCanvas.prototype.fitCanvas = function()
{
	const width_margin = 100;
	const height_margin = 50;
	const layer_margin_horz = 5;
	const layer_margin_vert = 5;
	const layer_client_width_min = 400;
	const layer_client_height_min = 400;

	// レイヤーのオフセット設定
	// オフセットの値はCSSで指定してあるが、なぜかプログラムから
	// 1回は明示的に設定せねばプログラムで値を取得できない。FireFoxにて確認。
	for (let i = 0; i < this.m_layers.length; ++i) {
		this.m_layers[0].style.left = layer_margin_horz + "px";
		this.m_layers[0].style.top = layer_margin_vert + "px";
	}

	// View portの寸法取得
	let vport_client_width = this.m_view_port.clientWidth;
	let vport_client_height = this.m_view_port.clientHeight;
	let vport_outer_width = this.m_view_port.offsetWidth;
	let vport_outer_height = this.m_view_port.offsetHeight;
	// {	/*UTEST*/		// スクロールバーの幅の算出
	// 	// 次の関係が成り立つ。
	// 	//   vport_client_width < vport_outer_width == vport_bounds.width
	// 	// vport_client_widthがスクロールバーを含まない領域幅のようだ。
	// 	let vport_bounds = this.m_view_port.getBoundingClientRect();
	// 	console.log("vport_client_width=" + vport_client_width
	// 	 	+ ", vport_outer_width=" + vport_outer_width
	// 		+ ", vport_bounds.width=" + vport_bounds.width
	// 	);
	// 	console.log("scroll bar width(?)=" + (vport_outer_width - vport_client_width));
	// 	console.log("layer client width=" + this.m_layers[0].clientWidth);
	// }

	// レイヤーの寸法取得
	let layer_outer_width = this.m_layers[0].offsetWidth;
	let layer_outer_height = this.m_layers[0].offsetHeight;
	{
		let layer_client_width = this.m_layers[0].clientWidth;
		let layer_client_height = this.m_layers[0].clientHeight;
		if (layer_client_width < layer_client_width_min) {
			layer_outer_width += (layer_client_width_min - layer_client_width);
		}
		if (layer_client_height < layer_client_height_min) {
			layer_outer_height += (layer_client_height_min - layer_client_height);
		}
	}

	// レイヤーのオフセット取得
	let layer_left = parseInt(this.m_layers[0].style.left);
	let layer_top = parseInt(this.m_layers[0].style.top);
	// console.log("layer_left=" + layer_left + ", layer_top=" + layer_top);

	// View portの必要なサイズを取得
	let delta_w = layer_left + layer_outer_width - vport_client_width;
	let delta_h = layer_top + layer_outer_height - vport_client_height;
	let vport_outer_width_min = vport_outer_width + delta_w;
	let vport_outer_height_min = vport_outer_height + delta_h;

	// ウィンドウの表示領域サイズで制限をかける
	// console.log("window.innerWidth=" + window.innerWidth);
	// console.log("window.innerHeight=" + window.innerHeight);
	if (vport_outer_width_min > window.innerWidth - width_margin) {
		vport_outer_width_min = window.innerWidth - width_margin;
	}
	if (vport_outer_height_min > window.innerHeight - height_margin) {
		vport_outer_height_min = window.innerHeight - height_margin;
	}

	// View portのサイズ設定
	this.m_view_port.style.width = vport_outer_width_min + "px";
	this.m_view_port.style.height = vport_outer_height_min + "px";
}
<commit_msg>PictureCanvas::getBoundingDrawAreaRect()の実装の誤りを修正した。<commit_after>'use strict';

//
//	PointingEvent
//

// Description:
// ポインティングイベントを表す。

/// 新しいインスタンスを初期化する。
function PointingEvent(sender, e)
{
	this.m_sender = sender;
	let bounds = sender.getBoundingDrawAreaRect();
	this.m_point = jsPoint(
		e.clientX - bounds.x,
		e.clientY - bounds.y
	);
	this.m_spkey = g_keyStateManager.getSpKeyState();
}

//
//	PictureCanvas
//

// ■ PictureCanvas
// canvasを重ねた描画領域を表す。
// 領域内にてマウスまたはタッチパネルイベントを受け付け、
// 登録された描画ツールに座標と対象レイヤーの情報を配送する。
// (描画のためのcanvas要素の操作は描画ツールに委譲する。)
// キャンバス全クリアとレイヤー合成の機能を備える。

/// 新しいインスタンスを初期化する。
function PictureCanvas()
{
	// DOMオブジェクト取得
	this.m_view_port = document.getElementById("viewport");
	this.m_layers = [
		document.getElementById("canvas_0"),
		document.getElementById("canvas_1"),
		document.getElementById("canvas_2")
	];
	this.m_joint_canvas = document.getElementById("joint_canvas");

	// 描画担当ツール
	this.m_drawer = null;

	// 描画担当ツールに引き渡す情報
	this.m_nTargetLayerNo = this.m_layers.length - 1;		// 描画対象レイヤー

	// ポインタデバイスのドラッグ状態
	this.m_bDragging = false;
	this.m_lastEvent = null;

	// イベントハンドラ登録
	register_pointer_event_handler(this.m_view_port, this);

	// レイヤーのサイズ調整
	this.fitCanvas();

	// レイヤーのオフセット取得
	// fitCanvas()呼出し後である必要がある。
	this.m_layer_left = parseInt(this.m_layers[0].style.left);
	this.m_layer_top = parseInt(this.m_layers[0].style.top);
}

/// イベントリスナ。
PictureCanvas.prototype.handleEvent = function(e)
{
	// console.log("Event: " + e.type);
	// console.dir(e);

	// 描画ツールに引き渡す情報を構成
	let mod_e = new PointingEvent(this, e);
	this.m_lastEvent = Object.assign({}, mod_e);		// 値コピー
	// console.dir(this.m_lastEvent);

	// イベント別処理
	switch (e.type) {
	case 'mousedown':
	case 'touchstart':
		// mouseupやtouchendを確実に補足するための登録
		g_pointManager.notifyPointStart(this, e);

		// 描画開始を通知
		this.m_bDragging = true;
		if (this.m_drawer) {
			this.m_drawer.OnDrawStart(mod_e);
		}
		break;
	case 'mouseup':
	case 'touchend':
		// 描画終了を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawEnd(mod_e);
		}
		this.m_bDragging = false;
		break;
	case 'mousemove':
	case 'touchmove':
		// ポインタの移動を通知
		if (this.m_bDragging && this.m_drawer) {
			this.m_drawer.OnDrawing(mod_e);
		}
		break;
	default:
		break;
	}
}

/// 要素の絶対座標を返す。
PictureCanvas.prototype.getBoundingDrawAreaRect = function()
{
	let bounds = this.m_layers[0].getBoundingClientRect();
	return bounds;
}

/// 描画ツールを設定する。
PictureCanvas.prototype.setDrawer = function(drawer)
{
	assert(!this.m_bDragging);
	let prev = this.m_drawer;
	this.m_drawer = drawer;
	return prev;
}

/// カレントレイヤーを変更する。
PictureCanvas.prototype.changeLayer = function(layerNo)
{
	assert(1 <= layerNo && layerNo < this.m_layers.length);
	m_nTargetLayerNo = layerIdx;
}

/// カレントレイヤーを取得する。
PictureCanvas.prototype.getLayer = function()
{
	return this.m_layers[this.m_nTargetLayerNo];
}

/// キャンバスを全クリアする。
PictureCanvas.prototype.eraseCanvas = function()
{
	erase_canvas(this.m_layers);
	// {	/*UTEST*/	// アンチエリアシング無し描画テスト
	// 	let ctx = this.m_layers[0].getContext('2d');
	// 	// ctx.strokeStyle = "#000000";
	// 	ctx.fillStyle = "#000000";
	// 	ctx.beginPath();
	// 	draw_line_1px(0.5, 0.5, 200.5, 200.5, ctx, 1);
	// 	ctx.stroke();
	// 	ctx.closePath();
	// }
}

/// 全レイヤーを合成する。
PictureCanvas.prototype.getJointImage = function(dstCanvas)
{
	get_joint_image(this.m_layers, dstCanvas);
}

/// View portをキャンバスにfitさせる。
PictureCanvas.prototype.fitCanvas = function()
{
	const width_margin = 100;
	const height_margin = 50;
	const layer_margin_horz = 5;
	const layer_margin_vert = 5;
	const layer_client_width_min = 400;
	const layer_client_height_min = 400;

	// レイヤーのオフセット設定
	// オフセットの値はCSSで指定してあるが、なぜかプログラムから
	// 1回は明示的に設定せねばプログラムで値を取得できない。FireFoxにて確認。
	for (let i = 0; i < this.m_layers.length; ++i) {
		this.m_layers[0].style.left = layer_margin_horz + "px";
		this.m_layers[0].style.top = layer_margin_vert + "px";
	}

	// View portの寸法取得
	let vport_client_width = this.m_view_port.clientWidth;
	let vport_client_height = this.m_view_port.clientHeight;
	let vport_outer_width = this.m_view_port.offsetWidth;
	let vport_outer_height = this.m_view_port.offsetHeight;
	// {	/*UTEST*/		// スクロールバーの幅の算出
	// 	// 次の関係が成り立つ。
	// 	//   vport_client_width < vport_outer_width == vport_bounds.width
	// 	// vport_client_widthがスクロールバーを含まない領域幅のようだ。
	// 	let vport_bounds = this.m_view_port.getBoundingClientRect();
	// 	console.log("vport_client_width=" + vport_client_width
	// 	 	+ ", vport_outer_width=" + vport_outer_width
	// 		+ ", vport_bounds.width=" + vport_bounds.width
	// 	);
	// 	console.log("scroll bar width(?)=" + (vport_outer_width - vport_client_width));
	// 	console.log("layer client width=" + this.m_layers[0].clientWidth);
	// }

	// レイヤーの寸法取得
	let layer_outer_width = this.m_layers[0].offsetWidth;
	let layer_outer_height = this.m_layers[0].offsetHeight;
	{
		let layer_client_width = this.m_layers[0].clientWidth;
		let layer_client_height = this.m_layers[0].clientHeight;
		if (layer_client_width < layer_client_width_min) {
			layer_outer_width += (layer_client_width_min - layer_client_width);
		}
		if (layer_client_height < layer_client_height_min) {
			layer_outer_height += (layer_client_height_min - layer_client_height);
		}
	}

	// レイヤーのオフセット取得
	let layer_left = parseInt(this.m_layers[0].style.left);
	let layer_top = parseInt(this.m_layers[0].style.top);
	// console.log("layer_left=" + layer_left + ", layer_top=" + layer_top);

	// View portの必要なサイズを取得
	let delta_w = layer_left + layer_outer_width - vport_client_width;
	let delta_h = layer_top + layer_outer_height - vport_client_height;
	let vport_outer_width_min = vport_outer_width + delta_w;
	let vport_outer_height_min = vport_outer_height + delta_h;

	// ウィンドウの表示領域サイズで制限をかける
	// console.log("window.innerWidth=" + window.innerWidth);
	// console.log("window.innerHeight=" + window.innerHeight);
	if (vport_outer_width_min > window.innerWidth - width_margin) {
		vport_outer_width_min = window.innerWidth - width_margin;
	}
	if (vport_outer_height_min > window.innerHeight - height_margin) {
		vport_outer_height_min = window.innerHeight - height_margin;
	}

	// View portのサイズ設定
	this.m_view_port.style.width = vport_outer_width_min + "px";
	this.m_view_port.style.height = vport_outer_height_min + "px";
}
<|endoftext|>


<commit_before>import { expect } from 'chai'
import { beforeEach, describe, it } from 'mocha'
import Ember from 'ember'
const { A, Controller, on, run } = Ember
import FrostListMixin from 'ember-frost-list/mixins/frost-list-mixin'
import FrostListSelectionMixin from 'ember-frost-list/mixins/frost-list-selection-mixin'
import FrostListExpansionMixin from 'ember-frost-list/mixins/frost-list-expansion-mixin'
import FrostListSortingMixin from 'ember-frost-list/mixins/frost-list-sorting-mixin'

describe('Unit: FrostListMixin', function () {
  const testItems = [
    {
      id: '1'
    }
  ]
  let subject

  beforeEach(function () {
    let testObject = Controller.extend(FrostListMixin)
    subject = testObject.create({
      listConfig: {
        items: 'model'
      }
    })

    run(() => subject.set('model', testItems))
  })

  it('successfully mixed', function () {
    expect(
      subject
    ).to.be.ok
  })

  describe('expected Mixins', function () {
    it('has FrostListSelectionMixin Mixin', function () {
      expect(
        FrostListSelectionMixin.detect(subject)
      ).to.eql(true)
    })

    it('has FrostListExpansionMixin Mixin', function () {
      expect(
        FrostListExpansionMixin.detect(subject)
      ).to.eql(true)
    })

    it('has FrostListSortingMixin Mixin', function () {
      expect(
        FrostListSortingMixin.detect(subject)
      ).to.eql(true)
    })
  })

  it('sets dependent keys correctly', function () {
    const listMixinConfigDependentKeys = [
      'activeSorting',
      'sortableProperties',
      'statefulListItems.[]'
    ]

    expect(
      subject.listMixinConfig._dependentKeys
    ).to.eql(listMixinConfigDependentKeys)
  })

  describe('"listMixinConfig" computed property', function () {
    let listMixinConfig

    beforeEach(function () {
      let list = A()
      list.addObject({
        id: '1',
        isExpanded: false
      })

      const mixinTestObject = Controller.extend(FrostListMixin)
      const mixin = mixinTestObject.create({
        listConfig: {
          items: 'model',
          sorting: {
            active: [],
            properties: []
          }
        },
        initListMixin: on('init', function () {
          this.set('_selectItem', '_selectItem')
          this.set('_collapseItems', '_collapseItems')
          this.set('_expandItems', '_expandItems')
          this.set('_collapseItem', '_collapseItem')
          this.set('_expandItem', '_expandItem')
          this.set('_sortItems', '_sortItems')
          this.set('_loadNext', '_loadNext')
          this.set('_loadPrevious', '_loadPrevious')
        })
      })

      run(() => {
        mixin.set('model', list)
        mixin.set('listConfig.component', 'my-list-item')
        mixin.set('activeSorting', [])
        mixin.set('properties', [])
      })

      listMixinConfig = mixin.get('listMixinConfig')
    })

    it('has "items" property', function () {
      expect(
        listMixinConfig
      ).to.have.property('items')
    })

    it('has component" property', function () {
      expect(
        listMixinConfig
      ).to.have.property('component', 'my-list-item')
    })

    it('has "expansion" property with correct structure', function () {
      expect(
        listMixinConfig
      ).to.have.property('expansion')
        .that.deep.equals({
          onCollapseAll: '_collapseItems',
          onExpandAll: '_expandItems'
        })
    })

    it('has "selection" propery with correct structure', function () {
      expect(
        listMixinConfig
        ).to.have.property('selection')
          .that.deep.equals({
            onSelect: '_selectItem'
          })
    })

    it('has "sorting" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"sorting" propery exists and has correct structure'
        ).to.have.property('sorting')
          .that.deep.equals({
            activeSorting: [],
            properties: [],
            onSort: '_sortItems'
          })
    })

    it('has "infiniteScroll" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"infiniteScroll" propery exists and has correct structure'
        ).to.have.property('infiniteScroll')
          .that.deep.equals({
            loadNext: '_loadNext',
            loadPrevious: '_loadPrevious'
          })
    })
  })
})
<commit_msg>updated mixin tests<commit_after>import { expect } from 'chai'
import { beforeEach, describe, it } from 'mocha'
import Ember from 'ember'
const { A, Controller, on, run } = Ember
import FrostListMixin from 'ember-frost-list/mixins/frost-list-mixin'
import FrostListSelectionMixin from 'ember-frost-list/mixins/frost-list-selection-mixin'
import FrostListExpansionMixin from 'ember-frost-list/mixins/frost-list-expansion-mixin'
import FrostListSortingMixin from 'ember-frost-list/mixins/frost-list-sorting-mixin'

describe('Unit: FrostListMixin', function () {
  const testItems = [
    {
      id: '1'
    }
  ]
  let subject

  beforeEach(function () {
    let testObject = Controller.extend(FrostListMixin)
    subject = testObject.create({
      listConfig: {
        items: 'model'
      }
    })

    run(() => subject.set('model', testItems))
  })

  it('successfully mixed', function () {
    expect(
      subject
    ).to.be.ok
  })

  describe('expected Mixins', function () {
    it('has FrostListSelectionMixin Mixin', function () {
      expect(
        FrostListSelectionMixin.detect(subject)
      ).to.eql(true)
    })

    it('has FrostListExpansionMixin Mixin', function () {
      expect(
        FrostListExpansionMixin.detect(subject)
      ).to.eql(true)
    })

    it('has FrostListSortingMixin Mixin', function () {
      expect(
        FrostListSortingMixin.detect(subject)
      ).to.eql(true)
    })
  })

  it('sets dependent keys correctly', function () {
    const listMixinConfigDependentKeys = [
      'activeSorting',
      'sortableProperties',
      'statefulListItems.[]'
    ]

    expect(
      subject.listMixinConfig._dependentKeys
    ).to.eql(listMixinConfigDependentKeys)
  })

  describe('"listMixinConfig" computed property', function () {
    let listMixinConfig

    beforeEach(function () {
      let list = A()
      list.addObject({
        id: '1',
        isExpanded: false
      })

      const mixinTestObject = Controller.extend(FrostListMixin)
      const mixin = mixinTestObject.create({
        listConfig: {
          items: 'model',
          sorting: {
            active: [],
            properties: []
          }
        },
        initListMixin: on('init', function () {
          this.set('_selectItem', '_selectItem')
          this.set('_collapseItems', '_collapseItems')
          this.set('_expandItems', '_expandItems')
          this.set('_collapseItem', '_collapseItem')
          this.set('_expandItem', '_expandItem')
          this.set('_sortItems', '_sortItems')
          this.set('_loadNext', '_loadNext')
          this.set('_loadPrevious', '_loadPrevious')
        })
      })

      run(() => {
        mixin.setProperties(
          {
            'model': list,
            'listConfig.component': 'my-list-item',
            'activeSorting': [],
            'properties': []
          }
        )
      })

      listMixinConfig = mixin.get('listMixinConfig')
    })

    it('has "items" property', function () {
      expect(
        listMixinConfig
      ).to.have.property('items')
    })

    it('has component" property', function () {
      expect(
        listMixinConfig
      ).to.have.property('component', 'my-list-item')
    })

    it('has "expansion" property with correct structure', function () {
      expect(
        listMixinConfig
      ).to.have.property('expansion')
        .that.deep.equals({
          onCollapseAll: '_collapseItems',
          onExpandAll: '_expandItems'
        })
    })

    it('has "selection" propery with correct structure', function () {
      expect(
        listMixinConfig
        ).to.have.property('selection')
          .that.deep.equals({
            onSelect: '_selectItem'
          })
    })

    it('has "sorting" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"sorting" propery exists and has correct structure'
        ).to.have.property('sorting')
          .that.deep.equals({
            activeSorting: [],
            properties: [],
            onSort: '_sortItems'
          })
    })

    it('has "infiniteScroll" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"infiniteScroll" propery exists and has correct structure'
        ).to.have.property('infiniteScroll')
          .that.deep.equals({
            loadNext: '_loadNext',
            loadPrevious: '_loadPrevious'
          })
    })
  })
})
<|endoftext|>


<commit_before>import {expect} from 'chai'
import {
  beforeEach,
  describe,
  it
} from 'mocha'
import Ember from 'ember'
const {
  A,
  Controller,
  on,
  run
} = Ember
import FrostListMixin from 'ember-frost-list/mixins/frost-list-mixin'
import FrostListSelectionMixin from 'ember-frost-list/mixins/frost-list-selection-mixin'
import FrostListExpansionMixin from 'ember-frost-list/mixins/frost-list-expansion-mixin'
import FrostListSortingMixin from 'ember-frost-list/mixins/frost-list-sorting-mixin'

describe('Unit: FrostListMixin', function () {
  const testItems = [
    {
      id: '1'
    }
  ]
  let subject

  beforeEach(function () {
    let testObject = Controller.extend(FrostListMixin)
    subject = testObject.create({
      listConfig: {
        items: 'model'
      }
    })

    run(() => subject.set('model', testItems))
  })

  it('successfully mixed', function () {
    expect(
      subject
    ).to.be.ok
  })

  it('has the expected Mixins', function () {
    expect(
      FrostListSelectionMixin.detect(subject),
      'FrostListSelectionMixin Mixin is present'
    ).to.eql(true)

    expect(
      FrostListExpansionMixin.detect(subject),
      'FrostListExpansionMixin Mixin is present'
    ).to.eql(true)

    expect(
      FrostListSortingMixin.detect(subject),
      'FrostListSortingMixin Mixin is present'
    ).to.eql(true)
  })

  it('sets dependent keys correctly', function () {
    const listMixinConfigDependentKeys = [
      'activeSorting',
      'sortableProperties',
      'statefulListItems.[]'
    ]

    expect(
      subject.listMixinConfig._dependentKeys,
      'Dependent keys are correct for listMixinConfig()'
    ).to.eql(listMixinConfigDependentKeys)
  })

  it('"listMixinConfig" computed property returns correct object structure', function () {
    let list = A()
    list.addObject({
      id: '1',
      isExpanded: false
    })

    const mixinTestObject = Controller.extend(FrostListMixin)
    const mixin = mixinTestObject.create({
      listConfig: {
        items: 'model',
        sorting: {
          active: [],
          properties: []
        }
      },
      initListMixin: on('init', function () {
        this.set('_selectItem', '_selectItem')
        this.set('_collapseItems', '_collapseItems')
        this.set('_expandItems', '_expandItems')
        this.set('_collapseItem', '_collapseItem')
        this.set('_expandItem', '_expandItem')
        this.set('_sortItems', '_sortItems')
        this.set('_loadNext', '_loadNext')
        this.set('_loadPrevious', '_loadPrevious')
      })
    })

    run(() => {
      mixin.set('model', list)
      mixin.set('listConfig.component', 'my-list-item')
      mixin.set('activeSorting', [])
      mixin.set('properties', [])
    })

    const listMixinConfig = mixin.get('listMixinConfig')

    expect(
      listMixinConfig,
      '"items" property exists'
    ).to.have.property('items')

    expect(
      listMixinConfig,
      '"component" property exists'
    ).to.have.property('component', 'my-list-item')

    expect(
      listMixinConfig,
      '"expansion" property exists and has correct structure'
    ).to.have.property('expansion')
      .that.deep.equals({
        onCollapseAll: '_collapseItems',
        onExpandAll: '_expandItems'
      })

    expect(
    listMixinConfig,
    '"selection" propery exists and has correct structure'
    ).to.have.property('selection')
      .that.deep.equals({
        onSelect: '_selectItem'
      })

    expect(
    listMixinConfig,
    '"sorting" propery exists and has correct structure'
    ).to.have.property('sorting')
      .that.deep.equals({
        activeSorting: [],
        properties: [],
        onSort: '_sortItems'
      })

    expect(
    listMixinConfig,
    '"infiniteScroll" propery exists and has correct structure'
    ).to.have.property('infiniteScroll')
      .that.deep.equals({
        loadNext: '_loadNext',
        loadPrevious: '_loadPrevious'
      })
  })
})
<commit_msg>updated listMixinConfig CP test in frost-list-mixin unit test<commit_after>import {expect} from 'chai'
import {
  beforeEach,
  describe,
  it
} from 'mocha'
import Ember from 'ember'
const {
  A,
  Controller,
  on,
  run
} = Ember
import FrostListMixin from 'ember-frost-list/mixins/frost-list-mixin'
import FrostListSelectionMixin from 'ember-frost-list/mixins/frost-list-selection-mixin'
import FrostListExpansionMixin from 'ember-frost-list/mixins/frost-list-expansion-mixin'
import FrostListSortingMixin from 'ember-frost-list/mixins/frost-list-sorting-mixin'

describe('Unit: FrostListMixin', function () {
  const testItems = [
    {
      id: '1'
    }
  ]
  let subject

  beforeEach(function () {
    let testObject = Controller.extend(FrostListMixin)
    subject = testObject.create({
      listConfig: {
        items: 'model'
      }
    })

    run(() => subject.set('model', testItems))
  })

  it('successfully mixed', function () {
    expect(
      subject
    ).to.be.ok
  })

  it('has the expected Mixins', function () {
    expect(
      FrostListSelectionMixin.detect(subject),
      'FrostListSelectionMixin Mixin is present'
    ).to.eql(true)

    expect(
      FrostListExpansionMixin.detect(subject),
      'FrostListExpansionMixin Mixin is present'
    ).to.eql(true)

    expect(
      FrostListSortingMixin.detect(subject),
      'FrostListSortingMixin Mixin is present'
    ).to.eql(true)
  })

  it('sets dependent keys correctly', function () {
    const listMixinConfigDependentKeys = [
      'activeSorting',
      'sortableProperties',
      'statefulListItems.[]'
    ]

    expect(
      subject.listMixinConfig._dependentKeys,
      'Dependent keys are correct for listMixinConfig()'
    ).to.eql(listMixinConfigDependentKeys)
  })

  describe('"listMixinConfig" computed property', function () {
    let listMixinConfig

    beforeEach(function () {
      let list = A()
      list.addObject({
        id: '1',
        isExpanded: false
      })

      const mixinTestObject = Controller.extend(FrostListMixin)
      const mixin = mixinTestObject.create({
        listConfig: {
          items: 'model',
          sorting: {
            active: [],
            properties: []
          }
        },
        initListMixin: on('init', function () {
          this.set('_selectItem', '_selectItem')
          this.set('_collapseItems', '_collapseItems')
          this.set('_expandItems', '_expandItems')
          this.set('_collapseItem', '_collapseItem')
          this.set('_expandItem', '_expandItem')
          this.set('_sortItems', '_sortItems')
          this.set('_loadNext', '_loadNext')
          this.set('_loadPrevious', '_loadPrevious')
        })
      })

      run(() => {
        mixin.set('model', list)
        mixin.set('listConfig.component', 'my-list-item')
        mixin.set('activeSorting', [])
        mixin.set('properties', [])
      })

      listMixinConfig = mixin.get('listMixinConfig')
    })

    it('has "items" property', function () {
      expect(
        listMixinConfig,
        '"items" property exists'
      ).to.have.property('items')
    })

    it('has component" property', function () {
      expect(
        listMixinConfig,
        '"component" property exists'
      ).to.have.property('component', 'my-list-item')
    })

    it('has "expansion" property with correct structure', function () {
      expect(
        listMixinConfig,
        '"expansion" property exists and has correct structure'
      ).to.have.property('expansion')
        .that.deep.equals({
          onCollapseAll: '_collapseItems',
          onExpandAll: '_expandItems'
        })
    })

    it('has "selection" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"selection" propery exists and has correct structure'
        ).to.have.property('selection')
          .that.deep.equals({
            onSelect: '_selectItem'
          })
    })

    it('has "sorting" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"sorting" propery exists and has correct structure'
        ).to.have.property('sorting')
          .that.deep.equals({
            activeSorting: [],
            properties: [],
            onSort: '_sortItems'
          })
    })

    it('has "infiniteScroll" propery with correct structure', function () {
      expect(
        listMixinConfig,
        '"infiniteScroll" propery exists and has correct structure'
        ).to.have.property('infiniteScroll')
          .that.deep.equals({
            loadNext: '_loadNext',
            loadPrevious: '_loadPrevious'
          })
    })
  })
})
<|endoftext|>


<commit_before>/* global hexo */

// add _blank to external links

var cheerio = require('cheerio')
var URL = require('url')

hexo.extend.filter.register('after_render:html', source => {
  var $ = cheerio.load(source, { decodeEntities: false })
  $('a').each((index, elem) => {
    var href = $(elem).attr('href')
    if (href && isExternal(href)) {
      $(elem).attr({ target: '_blank' })
    }
  })
  return $.html()
})

function isExternal (url) {
  var myhost = URL.parse(hexo.config.url).hostname
  var h = url.split('?')[0]
  return !/^\/\w/.test(h) || h.indexOf(myhost) !== -1
}
<commit_msg>fixed external link<commit_after>/* global hexo */

// add _blank to external links

var cheerio = require('cheerio')
var URL = require('url')

hexo.extend.filter.register('after_render:html', source => {
  var $ = cheerio.load(source, { decodeEntities: false })
  $('a').each((index, elem) => {
    var href = $(elem).attr('href')
    if (href && isExternal(href)) {
      $(elem).attr({ target: '_blank' })
    }
  })
  return $.html()
})

function isExternal (url) {
  var myhost = URL.parse(hexo.config.url).hostname
  var h = url.split('?')[0]
  return !/^((\/(\w|$))|#)/.test(h) || h.indexOf(myhost) !== -1
}
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import AutoRefresh from 'shared/components/AutoRefresh'
import LineGraph from 'shared/components/LineGraph'
import SingleStat from 'shared/components/SingleStat'
import NameableGraph from 'shared/components/NameableGraph'
import ReactGridLayout, {WidthProvider} from 'react-grid-layout'

import timeRanges from 'hson!../data/timeRanges.hson'
import buildInfluxQLQuery from 'utils/influxql'

const GridLayout = WidthProvider(ReactGridLayout)

const RefreshingLineGraph = AutoRefresh(LineGraph)
const RefreshingSingleStat = AutoRefresh(SingleStat)

const {
  arrayOf,
  bool,
  func,
  number,
  shape,
  string,
} = PropTypes

export const LayoutRenderer = React.createClass({
  propTypes: {
    autoRefresh: number.isRequired,
    timeRange: shape({
      lower: string.isRequired,
    }).isRequired,
    cells: arrayOf(
      shape({
        queries: arrayOf(
          shape({
            label: string,
            text: string,
            query: string,
          }).isRequired
        ).isRequired,
        x: number.isRequired,
        y: number.isRequired,
        w: number.isRequired,
        h: number.isRequired,
        i: string.isRequired,
        name: string.isRequired,
        type: string.isRequired,
      }).isRequired
    ),
    host: string,
    source: string,
    onPositionChange: func,
    onEditCell: func,
    onRenameCell: func,
    onUpdateCell: func,
    onDeleteCell: func,
    onSummonOverlayTechnologies: func,
    shouldNotBeEditable: bool,
  },

  buildQueryForOldQuerySchema(q) {
    const {timeRange: {lower}, host} = this.props
    const {defaultGroupBy} = timeRanges.find((range) => range.lower === lower)
    const {wheres, groupbys} = q

    let text = q.text

    text += ` where time > ${lower}`

    if (host) {
      text += ` and \"host\" = '${host}'`
    }

    if (wheres && wheres.length > 0) {
      text += ` and ${wheres.join(' and ')}`
    }

    if (groupbys) {
      if (groupbys.find((g) => g.includes("time"))) {
        text += ` group by ${groupbys.join(',')}`
      } else if (groupbys.length > 0) {
        text += ` group by time(${defaultGroupBy}),${groupbys.join(',')}`
      } else {
        text += ` group by time(${defaultGroupBy})`
      }
    } else {
      text += ` group by time(${defaultGroupBy})`
    }

    return text
  },

  generateVisualizations() {
    const {autoRefresh, timeRange, source, cells, onEditCell, onRenameCell, onUpdateCell, onDeleteCell, onSummonOverlayTechnologies, shouldNotBeEditable} = this.props

    return cells.map((cell) => {
      const qs = cell.queries.map((query) => {
        // TODO: Canned dashboards (and possibly Kubernetes dashboard) use an old query schema,
        // which does not have enough information for the new `buildInfluxQLQuery` function
        // to operate on. We will use `buildQueryForOldQuerySchema` until we conform
        // on a stable query representation.
        let queryText
        if (query.queryConfig) {
          const {queryConfig: {rawText}} = query
          queryText = rawText || buildInfluxQLQuery(timeRange, query.queryConfig)
        } else {
          queryText = this.buildQueryForOldQuerySchema(query)
        }

        return Object.assign({}, query, {
          host: source,
          text: queryText,
        })
      })

      if (cell.type === 'single-stat') {
        return (

            <NameableGraph
              onEditCell={onEditCell}
              onRenameCell={onRenameCell}
              onUpdateCell={onUpdateCell}
              onDeleteCell={onDeleteCell}
              onSummonOverlayTechnologies={onSummonOverlayTechnologies}
              shouldNotBeEditable={shouldNotBeEditable}
              cell={cell}
            >
              <RefreshingSingleStat queries={[qs[0]]} autoRefresh={autoRefresh} />
            </NameableGraph>

        )
      }

      const displayOptions = {
        stepPlot: cell.type === 'line-stepplot',
        stackedGraph: cell.type === 'line-stacked',
      }

      return (

          <NameableGraph
            onEditCell={onEditCell}
            onRenameCell={onRenameCell}
            onUpdateCell={onUpdateCell}
            onDeleteCell={onDeleteCell}
            onSummonOverlayTechnologies={onSummonOverlayTechnologies}
            shouldNotBeEditable={shouldNotBeEditable}
            cell={cell}
          >
            <RefreshingLineGraph
              queries={qs}
              autoRefresh={autoRefresh}
              showSingleStat={cell.type === 'line-plus-single-stat'}
              displayOptions={displayOptions}
            />
          </NameableGraph>

      )
    })
  },

  handleLayoutChange(layout) {
    this.triggerWindowResize()

    if (!this.props.onPositionChange) {
      return
    }

    const newCells = this.props.cells.map((cell) => {
      const l = layout.find((ly) => ly.i === cell.i)
      const newLayout = {x: l.x, y: l.y, h: l.h, w: l.w}
      return {...cell, ...newLayout}
    })

    this.props.onPositionChange(newCells)
  },

  render() {
    const layoutMargin = 4
    const isDashboard = !!this.props.onPositionChange

    return (
      <GridLayout
        layout={this.props.cells}
        cols={12}
        rowHeight={83.5}
        margin={[layoutMargin, layoutMargin]}
        containerPadding={[0, 0]}
        useCSSTransforms={false}
        onResize={this.triggerWindowResize}
        onLayoutChange={this.handleLayoutChange}
        draggableHandle={'.dash-graph--name'}
        isDraggable={isDashboard}
        isResizable={isDashboard}
      >
        {this.generateVisualizations()}
      </GridLayout>
    )
  },

  triggerWindowResize() {
    // Hack to get dygraphs to fit properly during and after resize (dispatchEvent is a global method on window).
    const evt = document.createEvent('CustomEvent')  // MUST be 'CustomEvent'
    evt.initCustomEvent('resize', false, false, null)
    dispatchEvent(evt)
  },
})

export default LayoutRenderer
<commit_msg>Factor refreshing graph into its own render function<commit_after>import React, {PropTypes} from 'react'
import AutoRefresh from 'shared/components/AutoRefresh'
import LineGraph from 'shared/components/LineGraph'
import SingleStat from 'shared/components/SingleStat'
import NameableGraph from 'shared/components/NameableGraph'
import ReactGridLayout, {WidthProvider} from 'react-grid-layout'

import timeRanges from 'hson!../data/timeRanges.hson'
import buildInfluxQLQuery from 'utils/influxql'

const GridLayout = WidthProvider(ReactGridLayout)

const RefreshingLineGraph = AutoRefresh(LineGraph)
const RefreshingSingleStat = AutoRefresh(SingleStat)

const {
  arrayOf,
  bool,
  func,
  number,
  shape,
  string,
} = PropTypes

export const LayoutRenderer = React.createClass({
  propTypes: {
    autoRefresh: number.isRequired,
    timeRange: shape({
      lower: string.isRequired,
    }).isRequired,
    cells: arrayOf(
      shape({
        queries: arrayOf(
          shape({
            label: string,
            text: string,
            query: string,
          }).isRequired
        ).isRequired,
        x: number.isRequired,
        y: number.isRequired,
        w: number.isRequired,
        h: number.isRequired,
        i: string.isRequired,
        name: string.isRequired,
        type: string.isRequired,
      }).isRequired
    ),
    host: string,
    source: string,
    onPositionChange: func,
    onEditCell: func,
    onRenameCell: func,
    onUpdateCell: func,
    onDeleteCell: func,
    onSummonOverlayTechnologies: func,
    shouldNotBeEditable: bool,
  },

  buildQueryForOldQuerySchema(q) {
    const {timeRange: {lower}, host} = this.props
    const {defaultGroupBy} = timeRanges.find((range) => range.lower === lower)
    const {wheres, groupbys} = q

    let text = q.text

    text += ` where time > ${lower}`

    if (host) {
      text += ` and \"host\" = '${host}'`
    }

    if (wheres && wheres.length > 0) {
      text += ` and ${wheres.join(' and ')}`
    }

    if (groupbys) {
      if (groupbys.find((g) => g.includes("time"))) {
        text += ` group by ${groupbys.join(',')}`
      } else if (groupbys.length > 0) {
        text += ` group by time(${defaultGroupBy}),${groupbys.join(',')}`
      } else {
        text += ` group by time(${defaultGroupBy})`
      }
    } else {
      text += ` group by time(${defaultGroupBy})`
    }

    return text
  },

  renderRefreshingGraph(type, queries, autoRefresh) {
    if (type === 'single-stat') {
      return <RefreshingSingleStat queries={[queries[0]]} autoRefresh={autoRefresh} />
    }

    const displayOptions = {
      stepPlot: type === 'line-stepplot',
      stackedGraph: type === 'line-stacked',
    }

    return (
      <RefreshingLineGraph
        queries={queries}
        autoRefresh={autoRefresh}
        showSingleStat={type === 'line-plus-single-stat'}
        displayOptions={displayOptions}
      />
    )
  },

  generateVisualizations() {
    const {autoRefresh, timeRange, source, cells, onEditCell, onRenameCell, onUpdateCell, onDeleteCell, onSummonOverlayTechnologies, shouldNotBeEditable} = this.props

    return cells.map((cell) => {
      const queries = cell.queries.map((query) => {
        // TODO: Canned dashboards (and possibly Kubernetes dashboard) use an old query schema,
        // which does not have enough information for the new `buildInfluxQLQuery` function
        // to operate on. We will use `buildQueryForOldQuerySchema` until we conform
        // on a stable query representation.
        let queryText
        if (query.queryConfig) {
          const {queryConfig: {rawText}} = query
          queryText = rawText || buildInfluxQLQuery(timeRange, query.queryConfig)
        } else {
          queryText = this.buildQueryForOldQuerySchema(query)
        }

        return Object.assign({}, query, {
          host: source,
          text: queryText,
        })
      })

      return (

          <NameableGraph
            onEditCell={onEditCell}
            onRenameCell={onRenameCell}
            onUpdateCell={onUpdateCell}
            onDeleteCell={onDeleteCell}
            onSummonOverlayTechnologies={onSummonOverlayTechnologies}
            shouldNotBeEditable={shouldNotBeEditable}
            cell={cell}
          >
            {this.renderRefreshingGraph(cell.type, queries, autoRefresh)}
          </NameableGraph>

      )
    })
  },

  handleLayoutChange(layout) {
    this.triggerWindowResize()

    if (!this.props.onPositionChange) {
      return
    }

    const newCells = this.props.cells.map((cell) => {
      const l = layout.find((ly) => ly.i === cell.i)
      const newLayout = {x: l.x, y: l.y, h: l.h, w: l.w}
      return {...cell, ...newLayout}
    })

    this.props.onPositionChange(newCells)
  },

  render() {
    const layoutMargin = 4
    const isDashboard = !!this.props.onPositionChange

    return (
      <GridLayout
        layout={this.props.cells}
        cols={12}
        rowHeight={83.5}
        margin={[layoutMargin, layoutMargin]}
        containerPadding={[0, 0]}
        useCSSTransforms={false}
        onResize={this.triggerWindowResize}
        onLayoutChange={this.handleLayoutChange}
        draggableHandle={'.dash-graph--name'}
        isDraggable={isDashboard}
        isResizable={isDashboard}
      >
        {this.generateVisualizations()}
      </GridLayout>
    )
  },

  triggerWindowResize() {
    // Hack to get dygraphs to fit properly during and after resize (dispatchEvent is a global method on window).
    const evt = document.createEvent('CustomEvent')  // MUST be 'CustomEvent'
    evt.initCustomEvent('resize', false, false, null)
    dispatchEvent(evt)
  },
})

export default LayoutRenderer
<|endoftext|>


<commit_before>sap.ui.define([
   'sap/ui/core/mvc/Controller',
   'sap/ui/model/json/JSONModel',
   'sap/ui/commons/CheckBox',
   'sap/ui/commons/Menu',
   'sap/ui/commons/MenuItem',
   'sap/ui/core/Item',
   'sap/ui/table/Column',
   'sap/m/Input',
   'sap/m/Button',
   "sap/m/FormattedText",
   "sap/ui/core/ResizeHandler",
   "sap/ui/layout/VerticalLayout",
   "sap/ui/layout/HorizontalLayout",
   "sap/m/MessageBox"
], function (Controller, JSONModel, CheckBox, Menu, MenuItem, coreItem, Column,
             mInput, mButton, FormattedText, ResizeHandler, VerticalLayout, HorizontalLayout, MessageBox) {

   "use strict";

   return Controller.extend("rootui5.eve7.controller.EveTable", {

      onInit : function() {
         var data = this.getView().getViewData();
         // console.log("VIEW DATA", data);

         var id = this.getView().getId();
         console.log("eve.GL.onInit id = ", id );

         this._load_scripts = true;
         this._render_html = false;

         this.mgr = data.mgr;
         this.eveViewerId = data.eveViewerId;
         this.kind = data.kind;

         var rh = this.mgr.handle.GetUserArgs("TableRowHeight");
         if (rh && (rh>0))
            this.getView().byId("table").setRowHeight(rh);

         this.bindTableColumns = true;
         var element = this.mgr.GetElement(this.eveViewerId);
         // loop over scene and add dependency
         for (var k=0;k<element.childs.length;++k) {
            var scene = element.childs[k];
            this.mgr.RegisterSceneReceiver(scene.fSceneId, this);
            this.onSceneCreate();
         }
      },
      sortTable: function(e) {
	   var col = e.mParameters.column;
	   var colId = col.getId();

	   var col = e.mParameters.column;
	   var bDescending = (e.mParameters.sortOrder ==  sap.ui.core.SortOrder.Descending);
	   var sv = bDescending;

	   var oSorter0 = new sap.ui.model.Sorter({
	       path: "Filtered",
	       descending: true
	   });

	   var oSorter1 = new sap.ui.model.Sorter({
	       path: col.mProperties.sortProperty,
	       descending: sv
	   });

          if ( col.mProperties.sortProperty === "Name")
          {
             let off = this.collection.fName.length;
             oSorter1.fnCompare = function(value1In, value2In) {
               let value1 = value1In.substring(off);
               let value2 = value2In.substring(off);
	       value2 = parseInt(value2);
	       value1 = parseInt(value1);
	       if (value1 < value2) return -1;
	       if (value1 == value2) return 0;
	       if (value1 > value2) return 1;
	     };
          }
          else {
	   oSorter1.fnCompare = function(value1, value2) {
	       value2 = parseFloat(value2);
	       value1 = parseFloat(value1);
	       if (value1 < value2) return -1;
	       if (value1 == value2) return 0;
	       if (value1 > value2) return 1;
	   };
          }
          var oTable = this.getView().byId("table");
	  var oItemsBinding = oTable.getBinding("rows");
          // Do one-level sort on Filtered entry
          if ( col.mProperties.sortProperty === "Filtered")
               oItemsBinding.sort([oSorter1]);
          else
	   oItemsBinding.sort([oSorter0, oSorter1]);

          // show indicators in column header
           col.setSorted(true);
           if (sv)
               col.setSortOrder(sap.ui.table.SortOrder.Descending);
           else
               col.setSortOrder(sap.ui.table.SortOrder.Ascending);

	   e.preventDefault();
      },

      locateEveTable: function()
      {
         this.eveTable = 0;
         var element = this.mgr.GetElement(this.eveViewerId);
         var sceneInfo = element.childs[0];
         var scene = this.mgr.GetElement(sceneInfo.fSceneId);
         // console.log(">>>table scene", scene);
         if (scene.childs[0]._typename == "ROOT::Experimental::REveTableViewInfo") {
            // presume table view manger is first child of table scene
            this.viewInfo = scene.childs[0];
         }

         // console.log("table viewinfo", this.viewInfo  );
         this.collection = this.mgr.GetElement(this.viewInfo.fDisplayedCollection);
         // loop over products
         for (var i = 1; i < scene.childs.length; ++i) {
            var product = scene.childs[i];
            if (product.childs && product.childs.length && product.childs[0].fCollectionId == this.viewInfo.fDisplayedCollection) {
               // console.log("table found  ",product.childs[0] );
               this.eveTable =  product.childs[0];
               break;
            }
         }

      },
      getCellText : function(value, filtered) {
	 return "" + value + ""
      },
      buildTableBody: function()
      {
         var oTable = this.getView().byId("table");

         // row definition
         var rowData = this.eveTable.body;

         // parse to float -- AMT in future data should be streamed as floats
         for (var r=0; r < rowData.length; r++) {
            var xr = rowData[r];
            for (var xri = 0; xri < xr.length; xri++) {
               var nv = parseFloat(xr[i]);
               if (nv != NaN) {
                  rowData[r][ri] = nv;
               }
            }
         }

         for (var i = 0; i < this.collection.childs.length; i++)
         {
            rowData[i].Name =  this.collection.childs[i].fName;
            rowData[i].Filtered =  this.collection.childs[i].fFiltered === true ? 0 : 1;
         }

         if (this.bindTableColumns) {
            // column definition
            var columnData = [];

            columnData.push({columnName:"Name"});
            columnData.push({columnName:"Filtered"});

            var eveColumns = this.eveTable.childs;
            for (var i = 0; i < eveColumns.length; i++)
            {
               var cname = eveColumns[i].fName;
               columnData.push({columnName : cname});
            }

            // table model
            var oModel = new JSONModel();
            oModel.setData({
               rows: rowData,
               columns: columnData
            });
            oTable.setModel(oModel);

	     var pthis = this;
             oTable.bindAggregation("columns", "/columns", function(sId, oContext) {
                 return new sap.ui.table.Column(sId, {
	             label: "{columnName}",
	             sortProperty: "{columnName}",
		     template: new FormattedText({
		         htmlText: {
			     parts: [
			         {path: oContext.getProperty("columnName")},
			         {path: "Filtered"}
			     ],
			     formatter: pthis.getCellText
		         }
		     }),
                     showFilterMenuEntry: true,
                     width: "100px"
               });
            });

	     // bind the Table items to the data collection
	     var oBinding = oTable.bindRows({
		 path : "/rows",
		 sorter: [
		     new sap.ui.model.Sorter({
			 path: 'Filtered',
			 descending: true
		     })
		 ]

	     });
            this.bindTableColumns = false;
         }
         else
         {
            var model = oTable.getModel();
            var data = model.getData();
            model.setData({"rows":rowData, "columns":data.columns});
         }
      },
      buildTableHeader: function()
      {
         var oModel = new JSONModel();
         var collection = this.mgr.GetElement(this.eveTable.fCollectionId);
         var clist = this.mgr.GetElement(collection.fMotherId);
         // console.log("collection list ", clist);

	 var mData = {
	    "itemx": [
	    ]};

         for (var i = 0; i < clist.childs.length; i++)
         {
            mData.itemx.push({"text" :clist.childs[i].fName, "key": clist.childs[i].fName, "collectionEveId":clist.childs[i].fElementId });
         }
         oModel.setData(mData);
         this.getView().setModel(oModel, "collections");

         var combo = this.getView().byId("ccombo");
         combo.setSelectedKey(collection.fName);
         combo.data("controller", this);
      },

      onLoadScripts: function() {
         this._load_scripts = true;
         this.checkScenes();
      },

      // function called from GuiPanelController
      onExit : function() {
         if (this.mgr) this.mgr.Unregister(this);
      },

      onSceneCreate: function(element, id) {
         console.log("EveTable onSceneChanged", id);
         this.locateEveTable();
         this.buildTableHeader();
         this.buildTableBody(true);
      },

      UpdateMgr : function(mgr) {
         var elem = mgr.map[this.eveViewerId];
         var scene = mgr.map[ elem.fMotherId];
         this.mgr = mgr;
      },

      onAfterRendering: function() {
         this._render_html = true;
         this.checkScenes();
      },

      checkScenes: function() {
      },

      toggleTableEdit: function() {
         var header = sap.ui.getCore().byId("EveViewer21--header");
         if (!this.editor) {
            this.editor = new VerticalLayout("tableEdit", {"width":"100%"});

            header.addContent(this.editor);

            // expression row
            {
               var collection = this.mgr.GetElement(this.eveTable.fCollectionId);
               var exprIn = new sap.m.Input("inputExp", {
    		  placeholder: "Start expression with \"i.\" to access object",
    		  showValueHelp: true,
    		  showTableSuggestionValueHelp: false,
    		  width: "100%",
    		  maxSuggestionWidth: "500px",
    		  showSuggestion: true,
    		  valueHelpRequest: function (oEvent) {
                     MessageBox.alert("Write any valid expression.\n Using \"i.\" convetion to access an object in collection. Below is an example:\ni.GetPdgCode() + 2");
    		  },
    		  suggestionItemSelected: function(oEvent){
    		     var oItem = oEvent.getParameter("selectedRow");
    		     console.log("sap.m.Input id with suggestion: selected item text is ------ " , oItem.getCells());
                     // fill in title if empty
                     var it = sap.ui.getCore().byId("titleEx");
                     if ((it.getValue() && it.getValue().length) == false) {
                        var v =  oItem.getCells()[0].getText().substring(2);
                        var t = v.split("(");
                        it.setValue(t[0]);
                     }
                     //fill in precision if empty
                     var ip = sap.ui.getCore().byId("precisionEx");
                     if ((ip.getValue() && ip.getValue().length) == false) {
                        var p = oItem.getCells()[1].getText();
                        if (p.startsWith("Int") || p.startsWith("int"))
                           ip.setValue("0");
                        else
                           ip.setValue("3");
                     }
    		  },
    		  suggestionColumns : [
    		     new sap.m.Column({
    			styleClass : "f",
    			hAlign : "Begin",
    			header : new sap.m.Label({
                           text : "Funcname"
    			})
    		     }),
    		     new sap.m.Column({
    			hAlign : "Center",
    			styleClass : "r",
    			popinDisplay : "Inline",
    			header : new sap.m.Label({
    			   text : "Return"
    			}),
    			minScreenWidth : "Tablet",
    			demandPopin : true
    		     }),

    		     new sap.m.Column({
    			hAlign : "End",
    			styleClass : "c",
    			width : "30%",
    			popinDisplay : "Inline",
    			header : new sap.m.Label({
                           text : "Class"
    			}),
    			minScreenWidth : "400px",
    			demandPopin : true
    		     })
    		  ]
    	       }).addStyleClass("inputRight");

               exprIn.setModel(oModel);

    	       var oTableItemTemplate = new sap.m.ColumnListItem({
    		  type : "Active",
    		  vAlign : "Middle",
    		  cells : [
    		     new sap.m.Label({
    			text : "{f}"
    		     }),
                     new sap.m.Label({
    			text: "{r}"
    		     }),
                     new sap.m.Label({
    			text : "{c}"
    		     })
    		  ]
    	       });
    	       var oModel = new sap.ui.model.json.JSONModel();
               var oSuggestionData = collection.fPublicFunctions;
               console.log("suggested data ",oSuggestionData );
    	       oModel.setData(oSuggestionData);
    	       exprIn.setModel(oModel);
    	       exprIn.bindAggregation("suggestionRows", "/", oTableItemTemplate);

               this.editor.addContent(exprIn);
            }

            // title & prec
            {
               var hl = new HorizontalLayout( {"width":"100%"});
               var titleIn = new mInput("titleEx", {placeholder:"Title", tooltip:"column title"});
             titleIn.setWidth("100%");
               hl.addContent(titleIn);

               var precIn = new mInput("precisionEx", {placeholder:"Precision", type: sap.m.InputType.Number, constraints: {minimum:"0", maximum:"9"}});
               precIn.setWidth("100%");
               hl.addContent(precIn);

               this.editor.addContent(hl);
            }

            //  button actions
            {
               var ll = new HorizontalLayout();
               var addBut = new mButton("AddCol", {text:"Add", press: this.addColumn});
               addBut.data("controller", this);
               ll.addContent(addBut);
               this.editor.visible = true;
               this.editor.addContent(ll);
            }
            return;
         }

         if (this.editor.visible) {
            var x = header.getContent().pop();
            header.removeContent(x);
            this.editor.visible = false;
         }
         else {
            header.addContent(this.editor);
            this.editor.visible = true;
         }
      },

      addColumn: function(event) {
         // console.log("add column s", event.getSource(), this);
         // console.log("add column p", this.data("controller"));
         var pthis = this.data("controller");
         var ws = pthis.editor.getContent();

         var expr = ws[0].getProperty("value");
         if (!expr) {
            alert("need a new column expression");
         }
         var hl =  ws[1].getContent();
         var title = hl[0].getProperty("value");
         if (!title) {
            title = expr;
         }

         pthis.mgr.SendMIR("AddNewColumnToCurrentCollection( \"" + expr + "\", \"" + title + "\" )",
                           pthis.viewInfo.fElementId, pthis.viewInfo._typename);

         // reset values
         sap.ui.getCore().byId("titleEx").setValue("");
         sap.ui.getCore().byId("precisionEx").setValue("");
      },

      collectionChanged: function(oEvent) {
         // console.log("collectionChanged ", oEvent.getSource());
         var model = oEvent.oSource.getSelectedItem().getBindingContext("collections");
         var path = model.getPath();
         var entry = model.getProperty(path);
         var coll = entry.collectionEveId;
         var mng =  this.viewInfo;

         this.mgr.SendMIR("SetDisplayedCollection(" + coll + ")", mng.fElementId, mng._typename);
      },

      sceneElementChange : function(el)
      {
         // console.log("table sceneElementChange", el);
         if (el._typename == "ROOT::Experimental::REveTableViewInfo") {
            this.bindTableColumns = true;
         }
      },

      endChanges : function(oEvent) {
         // console.log("table controller endChanges ",this.eveTable );
         this.locateEveTable();
         this.buildTableBody();
      },

      elementRemoved: function(elId) {
         var el = this.mgr.GetElement(elId);
         // console.log("EveTable element removed ", el);
      }
   });
});
<commit_msg>transition to REveDataItems as non-reveelement object<commit_after>sap.ui.define([
   'sap/ui/core/mvc/Controller',
   'sap/ui/model/json/JSONModel',
   'sap/ui/commons/CheckBox',
   'sap/ui/commons/Menu',
   'sap/ui/commons/MenuItem',
   'sap/ui/core/Item',
   'sap/ui/table/Column',
   'sap/m/Input',
   'sap/m/Button',
   "sap/m/FormattedText",
   "sap/ui/core/ResizeHandler",
   "sap/ui/layout/VerticalLayout",
   "sap/ui/layout/HorizontalLayout",
   "sap/m/MessageBox"
], function (Controller, JSONModel, CheckBox, Menu, MenuItem, coreItem, Column,
             mInput, mButton, FormattedText, ResizeHandler, VerticalLayout, HorizontalLayout, MessageBox) {

   "use strict";

   return Controller.extend("rootui5.eve7.controller.EveTable", {

      onInit : function() {
         var data = this.getView().getViewData();
         // console.log("VIEW DATA", data);

         var id = this.getView().getId();
         console.log("eve.GL.onInit id = ", id );

         this._load_scripts = true;
         this._render_html = false;

         this.mgr = data.mgr;
         this.eveViewerId = data.eveViewerId;
         this.kind = data.kind;

         var rh = this.mgr.handle.GetUserArgs("TableRowHeight");
         if (rh && (rh>0))
            this.getView().byId("table").setRowHeight(rh);

         this.bindTableColumns = true;
         var element = this.mgr.GetElement(this.eveViewerId);
         // loop over scene and add dependency
         for (var k=0;k<element.childs.length;++k) {
            var scene = element.childs[k];
            this.mgr.RegisterSceneReceiver(scene.fSceneId, this);
            this.onSceneCreate();
         }
      },
      sortTable: function(e) {
	   var col = e.mParameters.column;
	   var colId = col.getId();

	   var col = e.mParameters.column;
	   var bDescending = (e.mParameters.sortOrder ==  sap.ui.core.SortOrder.Descending);
	   var sv = bDescending;

	   var oSorter0 = new sap.ui.model.Sorter({
	       path: "Filtered",
	       descending: true
	   });

	   var oSorter1 = new sap.ui.model.Sorter({
	       path: col.mProperties.sortProperty,
	       descending: sv
	   });

          if ( col.mProperties.sortProperty === "Name")
          {
             let off = this.collection.fName.length;
             oSorter1.fnCompare = function(value1In, value2In) {
               let value1 = value1In.substring(off);
               let value2 = value2In.substring(off);
	       value2 = parseInt(value2);
	       value1 = parseInt(value1);
	       if (value1 < value2) return -1;
	       if (value1 == value2) return 0;
	       if (value1 > value2) return 1;
	     };
          }
          else {
	   oSorter1.fnCompare = function(value1, value2) {
	       value2 = parseFloat(value2);
	       value1 = parseFloat(value1);
	       if (value1 < value2) return -1;
	       if (value1 == value2) return 0;
	       if (value1 > value2) return 1;
	   };
          }
          var oTable = this.getView().byId("table");
	  var oItemsBinding = oTable.getBinding("rows");
          // Do one-level sort on Filtered entry
          if ( col.mProperties.sortProperty === "Filtered")
               oItemsBinding.sort([oSorter1]);
          else
	   oItemsBinding.sort([oSorter0, oSorter1]);

          // show indicators in column header
           col.setSorted(true);
           if (sv)
               col.setSortOrder(sap.ui.table.SortOrder.Descending);
           else
               col.setSortOrder(sap.ui.table.SortOrder.Ascending);

	   e.preventDefault();
      },

      locateEveTable: function()
      {
         this.eveTable = 0;
         var element = this.mgr.GetElement(this.eveViewerId);
         var sceneInfo = element.childs[0];
         var scene = this.mgr.GetElement(sceneInfo.fSceneId);
         // console.log(">>>table scene", scene);
         if (scene.childs[0]._typename == "ROOT::Experimental::REveTableViewInfo") {
            // presume table view manger is first child of table scene
            this.viewInfo = scene.childs[0];
         }

         // console.log("table viewinfo", this.viewInfo  );
         this.collection = this.mgr.GetElement(this.viewInfo.fDisplayedCollection);
         // loop over products
         for (var i = 1; i < scene.childs.length; ++i) {
            var product = scene.childs[i];
            if (product.childs && product.childs.length && product.childs[0].fCollectionId == this.viewInfo.fDisplayedCollection) {
               // console.log("table found  ",product.childs[0] );
               this.eveTable =  product.childs[0];
               break;
            }
         }

      },
      getCellText : function(value, filtered) {
	 return "" + value + ""
      },
      buildTableBody: function()
      {
         var oTable = this.getView().byId("table");

         // row definition
         var rowData = this.eveTable.body;

         // parse to float -- AMT in future data should be streamed as floats
         for (var r=0; r < rowData.length; r++) {
            var xr = rowData[r];
            for (var xri = 0; xri < xr.length; xri++) {
               var nv = parseFloat(xr[i]);
               if (nv != NaN) {
                  rowData[r][ri] = nv;
               }
            }
         }

         for (var i = 0; i < this.collection.items.length; i++)
         {
            rowData[i].Name = this.collection.fName + " " + i;
            rowData[i].Filtered =  this.collection.items[i].fFiltered === true ? 0 : 1;
         }

         if (this.bindTableColumns) {
            // column definition
            var columnData = [];

            columnData.push({columnName:"Name"});
            columnData.push({columnName:"Filtered"});

            var eveColumns = this.eveTable.childs;
            for (var i = 0; i < eveColumns.length; i++)
            {
               var cname = eveColumns[i].fName;
               columnData.push({columnName : cname});
            }

            // table model
            var oModel = new JSONModel();
            oModel.setData({
               rows: rowData,
               columns: columnData
            });
            oTable.setModel(oModel);

	     var pthis = this;
             oTable.bindAggregation("columns", "/columns", function(sId, oContext) {
                 return new sap.ui.table.Column(sId, {
	             label: "{columnName}",
	             sortProperty: "{columnName}",
		     template: new FormattedText({
		         htmlText: {
			     parts: [
			         {path: oContext.getProperty("columnName")},
			         {path: "Filtered"}
			     ],
			     formatter: pthis.getCellText
		         }
		     }),
                     showFilterMenuEntry: true,
                     width: "100px"
               });
            });

	     // bind the Table items to the data collection
	     var oBinding = oTable.bindRows({
		 path : "/rows",
		 sorter: [
		     new sap.ui.model.Sorter({
			 path: 'Filtered',
			 descending: true
		     })
		 ]

	     });
            this.bindTableColumns = false;
         }
         else
         {
            var model = oTable.getModel();
            var data = model.getData();
            model.setData({"rows":rowData, "columns":data.columns});
         }
      },
      buildTableHeader: function()
      {
         var oModel = new JSONModel();
         var collection = this.mgr.GetElement(this.eveTable.fCollectionId);
         var clist = this.mgr.GetElement(collection.fMotherId);
         // console.log("collection list ", clist);

	 var mData = {
	    "itemx": [
	    ]};

         for (var i = 0; i < clist.childs.length; i++)
         {
            mData.itemx.push({"text" :clist.childs[i].fName, "key": clist.childs[i].fName, "collectionEveId":clist.childs[i].fElementId });
         }
         oModel.setData(mData);
         this.getView().setModel(oModel, "collections");

         var combo = this.getView().byId("ccombo");
         combo.setSelectedKey(collection.fName);
         combo.data("controller", this);
      },

      onLoadScripts: function() {
         this._load_scripts = true;
         this.checkScenes();
      },

      // function called from GuiPanelController
      onExit : function() {
         if (this.mgr) this.mgr.Unregister(this);
      },

      onSceneCreate: function(element, id) {
         console.log("EveTable onSceneChanged", id);
         this.locateEveTable();
         this.buildTableHeader();
         this.buildTableBody(true);
      },

      UpdateMgr : function(mgr) {
         var elem = mgr.map[this.eveViewerId];
         var scene = mgr.map[ elem.fMotherId];
         this.mgr = mgr;
      },

      onAfterRendering: function() {
         this._render_html = true;
         this.checkScenes();
      },

      checkScenes: function() {
      },

      toggleTableEdit: function() {
         var header = sap.ui.getCore().byId("EveViewer21--header");
         if (!this.editor) {
            this.editor = new VerticalLayout("tableEdit", {"width":"100%"});

            header.addContent(this.editor);

            // expression row
            {
               var collection = this.mgr.GetElement(this.eveTable.fCollectionId);
               var exprIn = new sap.m.Input("inputExp", {
    		  placeholder: "Start expression with \"i.\" to access object",
    		  showValueHelp: true,
    		  showTableSuggestionValueHelp: false,
    		  width: "100%",
    		  maxSuggestionWidth: "500px",
    		  showSuggestion: true,
    		  valueHelpRequest: function (oEvent) {
                     MessageBox.alert("Write any valid expression.\n Using \"i.\" convetion to access an object in collection. Below is an example:\ni.GetPdgCode() + 2");
    		  },
    		  suggestionItemSelected: function(oEvent){
    		     var oItem = oEvent.getParameter("selectedRow");
    		     console.log("sap.m.Input id with suggestion: selected item text is ------ " , oItem.getCells());
                     // fill in title if empty
                     var it = sap.ui.getCore().byId("titleEx");
                     if ((it.getValue() && it.getValue().length) == false) {
                        var v =  oItem.getCells()[0].getText().substring(2);
                        var t = v.split("(");
                        it.setValue(t[0]);
                     }
                     //fill in precision if empty
                     var ip = sap.ui.getCore().byId("precisionEx");
                     if ((ip.getValue() && ip.getValue().length) == false) {
                        var p = oItem.getCells()[1].getText();
                        if (p.startsWith("Int") || p.startsWith("int"))
                           ip.setValue("0");
                        else
                           ip.setValue("3");
                     }
    		  },
    		  suggestionColumns : [
    		     new sap.m.Column({
    			styleClass : "f",
    			hAlign : "Begin",
    			header : new sap.m.Label({
                           text : "Funcname"
    			})
    		     }),
    		     new sap.m.Column({
    			hAlign : "Center",
    			styleClass : "r",
    			popinDisplay : "Inline",
    			header : new sap.m.Label({
    			   text : "Return"
    			}),
    			minScreenWidth : "Tablet",
    			demandPopin : true
    		     }),

    		     new sap.m.Column({
    			hAlign : "End",
    			styleClass : "c",
    			width : "30%",
    			popinDisplay : "Inline",
    			header : new sap.m.Label({
                           text : "Class"
    			}),
    			minScreenWidth : "400px",
    			demandPopin : true
    		     })
    		  ]
    	       }).addStyleClass("inputRight");

               exprIn.setModel(oModel);

    	       var oTableItemTemplate = new sap.m.ColumnListItem({
    		  type : "Active",
    		  vAlign : "Middle",
    		  cells : [
    		     new sap.m.Label({
    			text : "{f}"
    		     }),
                     new sap.m.Label({
    			text: "{r}"
    		     }),
                     new sap.m.Label({
    			text : "{c}"
    		     })
    		  ]
    	       });
    	       var oModel = new sap.ui.model.json.JSONModel();
               var oSuggestionData = collection.fPublicFunctions;
               console.log("suggested data ",oSuggestionData );
    	       oModel.setData(oSuggestionData);
    	       exprIn.setModel(oModel);
    	       exprIn.bindAggregation("suggestionRows", "/", oTableItemTemplate);

               this.editor.addContent(exprIn);
            }

            // title & prec
            {
               var hl = new HorizontalLayout( {"width":"100%"});
               var titleIn = new mInput("titleEx", {placeholder:"Title", tooltip:"column title"});
             titleIn.setWidth("100%");
               hl.addContent(titleIn);

               var precIn = new mInput("precisionEx", {placeholder:"Precision", type: sap.m.InputType.Number, constraints: {minimum:"0", maximum:"9"}});
               precIn.setWidth("100%");
               hl.addContent(precIn);

               this.editor.addContent(hl);
            }

            //  button actions
            {
               var ll = new HorizontalLayout();
               var addBut = new mButton("AddCol", {text:"Add", press: this.addColumn});
               addBut.data("controller", this);
               ll.addContent(addBut);
               this.editor.visible = true;
               this.editor.addContent(ll);
            }
            return;
         }

         if (this.editor.visible) {
            var x = header.getContent().pop();
            header.removeContent(x);
            this.editor.visible = false;
         }
         else {
            header.addContent(this.editor);
            this.editor.visible = true;
         }
      },

      addColumn: function(event) {
         // console.log("add column s", event.getSource(), this);
         // console.log("add column p", this.data("controller"));
         var pthis = this.data("controller");
         var ws = pthis.editor.getContent();

         var expr = ws[0].getProperty("value");
         if (!expr) {
            alert("need a new column expression");
         }
         var hl =  ws[1].getContent();
         var title = hl[0].getProperty("value");
         if (!title) {
            title = expr;
         }

         pthis.mgr.SendMIR("AddNewColumnToCurrentCollection( \"" + expr + "\", \"" + title + "\" )",
                           pthis.viewInfo.fElementId, pthis.viewInfo._typename);

         // reset values
         sap.ui.getCore().byId("titleEx").setValue("");
         sap.ui.getCore().byId("precisionEx").setValue("");
      },

      collectionChanged: function(oEvent) {
         // console.log("collectionChanged ", oEvent.getSource());
         var model = oEvent.oSource.getSelectedItem().getBindingContext("collections");
         var path = model.getPath();
         var entry = model.getProperty(path);
         var coll = entry.collectionEveId;
         var mng =  this.viewInfo;

         this.mgr.SendMIR("SetDisplayedCollection(" + coll + ")", mng.fElementId, mng._typename);
      },

      sceneElementChange : function(el)
      {
         // console.log("table sceneElementChange", el);
         if (el._typename == "ROOT::Experimental::REveTableViewInfo") {
            this.bindTableColumns = true;
         }
      },

      endChanges : function(oEvent) {
         // console.log("table controller endChanges ",this.eveTable );
         this.locateEveTable();
         this.buildTableBody();
      },

      elementRemoved: function(elId) {
         var el = this.mgr.GetElement(elId);
         // console.log("EveTable element removed ", el);
      }
   });
});
<|endoftext|>