<commit_before>// global variables and functions
// create a variable to hold database related stuff
var stdb = {}; stdb.db = null;

// function to open the database
stdb.open = function() {
	var size = 1024 * 1024 * 5; // 5MB
	stdb.db = openDatabase( 'STDB', '1.0', 'See Turtles Database', size );
};

// success and error functions for database queries
stdb.onSuccess = function() {
	// figure out what to do here...
	alert( "Success!" );
};

stdb.onError = function( tx, e ) {
	// figure out what to do here...
	console.log( e.message );
};

// create schema for our database
stdb.createSchema = function() {
	// start a transaction
	stdb.db.transaction( function( tx ) {
		// create the nests table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS nests ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, discovery_date TEXT, predicted_delivery TEXT, actual_delivery TEXT, nest_lat REAL, nest_lon REAL )", [] );
		// create the observations table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS observations ( id INTEGER PRIMARY KEY AUTOINCREMENT, nest_id INTEGER, observed_on TEXT, temperature REAL, condition TEXT )", [] );
		// create the species table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS species ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, description TEXT )", [] );
		// create the turtles table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS turtles ( id INTEGER, dob TEXT, gender TEXT DEFAULT F, species_id INTEGER, birth_lat REAL, birth_lon REAL, deceased INTEGER DEFAULT 0 )", [] );
		// create the sitings table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS sitings ( id INTEGER PRIMARY KEY AUTOINCREMENT, turtle_id INTEGER, condition TEXT, is_injured INTEGER DEFAULT 0, sited_on TEXT, lat REAL, lon REAL )", [] );
	}, function( e ) { console.log( e ); } );
};

// create functions for adding NEW data to tables
stdb.addNest = function( name, discovery, predicted, actual, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO nests ( name, discovery_date, predicted_delivery, actual_delivery, nest_lat, nest_lon ) VALUES ( ?, ?, ?, ?, ?, ? )',
			[ name, discovery, predicted, actual, lat, lon ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};

stdb.addObservation = function( nest_id, osbserved, temp, cond ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO observations ( nest_id, observed_on, temperature, condition ) VALUES ( ?, ?, ?, ? )',
			[ nest_id, osbserved, temp, cond ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.addSpecies = function( name, description ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO species ( name, description ) VALUES ( ?, ? )',
			[ name, description ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.addTurtles = function( id, dob, gender, species_id, birth_lat, birth_lon, deceased ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO species ( id, dob, gender, species_id, birth_lat, birth_lon, deceased ) VALUES ( ?, ?, ?, ?, ?, ? )',
			[ id, dob, gender, species_id, birth_lat, birth_lon, deceased ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.addSitings = function( turtle_id, condition, is_injured, sited_on, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO species ( turtle_id, condition, is_injured, sited_on, lat, lon ) VALUES ( ?, ?, ?, ?, ?, ? )',
			[ turtle_id, condition, is_injured, sited_on, lat, lon ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
// create functions for getting data from the tables
stdb.getNests = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM nests', [], successHandler, stdb.onError );
	});
};
stdb.getNests = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM nests WHERE id = ?', [ id ], successHandler, stdb.onError );
	});
};
stdb.getObservation = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM observations', [], successHandler, stdb.onError );
	});
};
stdb.getObservation = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM observations WHERE id = ?', [ id ], successHandler, stdb.onError );
	});
};
stdb.getSpecies = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM species', [], successHandler, stdb.onError );
	});
};
stdb.getSpecies = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM species WHERE id = ', [ id ], successHandler, stdb.onError );
	});
};
stdb.getTurtles = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM turtles', [], successHandler, stdb.onError );
	});
};
stdb.getTurtles = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM turtles WHERE id = ', [ id ], successHandler, stdb.onError );
	});
};
stdb.getSitings = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM sitings', [], successHandler, stdb.onError );
	});
};
stdb.getSitings = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM sitings WHERE id = ', [ id ], successHandler, stdb.onError );
	});
};
// create functions for updating data
stdb.updateNest = function( id,  name, discovery, predicted, actual, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET name = ?, discovery_date = ?, predicted_delivery = ?, actual_delivery = ?, nest_lat = ?, nest_lon = ? WHERE id = ?',
			[ name, discovery, predicted, actual, lat, lon, id ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateObservation = function( nest_id, osbserved, temp, cond ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET nest_id = ?, observed_on = ?, temperature = ?, condition = ? WHERE id = ?',
			[ nest_id, osbserved, temp, cond ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateSpecies = function( name, description ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET name = ?, description = ? WHERE id = ?',
			[ name, description ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateTurtles = function( id, dob, gender, species_id, birth_lat, birth_lon, deceased ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET id = ?, dob = ?, gender = ?, species_id = ?, birth_lat = ?, birth_lon = ?, deceased = ? WHERE id = ?',
			[ id, dob, species_id, birth_lat, birth_lon, deceased ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateSitings = function( turtle_id, condition, is_injured, sited_on, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET turtle_id = ?, condition = ?, is_injured = ?, sited_on = ?, lat = ?, lon = ? WHERE id = ?',
			[ turtle_id, condition, is_injured, sited_on, lat, lon ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
// create functions for deleting data
stdb.deleteNest = function( id ) {
	stdb.db.transaction( function( tx ) {
		//first get rid of the nest
		tx.executeSql( 'DELETE FROM nests WHERE id = ?',
			[ id ],
			null,
			stdb.onError
		);
		// next get rid of any associated observations
		tx.executeSql( 'DELETE FROM observations WHERE nest_id = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
		// next get rid of any associated species
		tx.executeSql( 'DELETE FROM species WHERE name = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
		// next get rid of any associated turtles
		tx.executeSql( 'DELETE FROM turtles WHERE id = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
		tx.executeSql( 'DELETE FROM sitings WHERE turtle_id = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
	});
};
// initialize the app
$( document ).on( 'mobileinit', function() {

	// initialize our database
	stdb.open();
	stdb.createSchema();

});

// code to run after the page has loaded
$( document ).ready( function() {
	var displayNests;

	displayNests = function( tx, rs ) {
		var nestlist;
		if ( rs.rows.length > 0 ) {
			nestlist  = '
';
			for ( i = 0; i < rs.rows.length; i++ ) {
				nestlist += '
'
				nestlist += rs.rows.item(i).name;
				nestlist += '';
				nestlist += 'Edit';
				nestlist += 'Edit';
				nestlist += 'Edit;
				nestlist += '';
				nestlist += '
';

			}
			nestlist += '
';
			$( '#nests div[data-role="content"]' ).prepend( nestlist ).trigger( 'create' );
		} else {
			$( '#nests div[data-role="content"]' ).prepend( '
No nests have been added to the database yet. Click the button below to add one.
' );
		}
	};

	// handle form submissions
	$( '#plot_submit' ).on( 'click', function( e ) {
		// don't actually try to follow the link
		e.preventDefault();

		// get the data from the fields
		var name = $( '#plot #name' ).val(),
			lat  = $( '#plot #lat'  ).val(),
			lon  = $( '#plot #lon'  ).val(),
			disc = $( '#plot #disc' ).val(),
			pred = $( '#plot #pred' ).val(),
			act  = $( '#plot #act'  ).val();

		// try to add it to the database
		stdb.addNest( name, lat, lon, disc, pred, act );

		// go back to the list of nests page
		$.mobile.changePage( '#nests' );
	});

	// handle page transitions
	$( '#nests' ).on( 'pagebeforeshow', function( e ) { stdb.getNests( displayNests ); });
});
<commit_msg>added buttons to nest list<commit_after>// global variables and functions
// create a variable to hold database related stuff
var stdb = {}; stdb.db = null;

// function to open the database
stdb.open = function() {
	var size = 1024 * 1024 * 5; // 5MB
	stdb.db = openDatabase( 'STDB', '1.0', 'See Turtles Database', size );
};

// success and error functions for database queries
stdb.onSuccess = function() {
	// figure out what to do here...
	alert( "Success!" );
};

stdb.onError = function( tx, e ) {
	// figure out what to do here...
	console.log( e.message );
};

// create schema for our database
stdb.createSchema = function() {
	// start a transaction
	stdb.db.transaction( function( tx ) {
		// create the nests table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS nests ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, discovery_date TEXT, predicted_delivery TEXT, actual_delivery TEXT, nest_lat REAL, nest_lon REAL )", [] );
		// create the observations table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS observations ( id INTEGER PRIMARY KEY AUTOINCREMENT, nest_id INTEGER, observed_on TEXT, temperature REAL, condition TEXT )", [] );
		// create the species table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS species ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, description TEXT )", [] );
		// create the turtles table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS turtles ( id INTEGER, dob TEXT, gender TEXT DEFAULT F, species_id INTEGER, birth_lat REAL, birth_lon REAL, deceased INTEGER DEFAULT 0 )", [] );
		// create the sitings table
		tx.executeSql( "CREATE TABLE IF NOT EXISTS sitings ( id INTEGER PRIMARY KEY AUTOINCREMENT, turtle_id INTEGER, condition TEXT, is_injured INTEGER DEFAULT 0, sited_on TEXT, lat REAL, lon REAL )", [] );
	}, function( e ) { console.log( e ); } );
};

// create functions for adding NEW data to tables
stdb.addNest = function( name, discovery, predicted, actual, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO nests ( name, discovery_date, predicted_delivery, actual_delivery, nest_lat, nest_lon ) VALUES ( ?, ?, ?, ?, ?, ? )',
			[ name, discovery, predicted, actual, lat, lon ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};

stdb.addObservation = function( nest_id, osbserved, temp, cond ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO observations ( nest_id, observed_on, temperature, condition ) VALUES ( ?, ?, ?, ? )',
			[ nest_id, osbserved, temp, cond ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.addSpecies = function( name, description ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO species ( name, description ) VALUES ( ?, ? )',
			[ name, description ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.addTurtles = function( id, dob, gender, species_id, birth_lat, birth_lon, deceased ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO species ( id, dob, gender, species_id, birth_lat, birth_lon, deceased ) VALUES ( ?, ?, ?, ?, ?, ? )',
			[ id, dob, gender, species_id, birth_lat, birth_lon, deceased ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.addSitings = function( turtle_id, condition, is_injured, sited_on, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'INSERT INTO species ( turtle_id, condition, is_injured, sited_on, lat, lon ) VALUES ( ?, ?, ?, ?, ?, ? )',
			[ turtle_id, condition, is_injured, sited_on, lat, lon ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
// create functions for getting data from the tables
stdb.getNests = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM nests', [], successHandler, stdb.onError );
	});
};
<<<<<<>>>>>> 1f3e74f5c0128491d4faab7b79377b811be6b3d4
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM nests WHERE id = ?', [ id ], successHandler, stdb.onError );
	});
};
<<<<<<< HEAD
stdb.getObservation = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM observations', [], successHandler, stdb.onError );
	});
};
stdb.getObservation = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM observations WHERE id = ?', [ id ], successHandler, stdb.onError );
	});
};
stdb.getSpecies = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM species', [], successHandler, stdb.onError );
	});
};
stdb.getSpecies = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM species WHERE id = ', [ id ], successHandler, stdb.onError );
	});
};
stdb.getTurtles = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM turtles', [], successHandler, stdb.onError );
	});
};
stdb.getTurtles = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM turtles WHERE id = ', [ id ], successHandler, stdb.onError );
	});
};
stdb.getSitings = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM sitings', [], successHandler, stdb.onError );
	});
};
stdb.getSitings = function( successHandler ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'SELECT * FROM sitings WHERE id = ', [ id ], successHandler, stdb.onError );
	});
};
=======

>>>>>>> 1f3e74f5c0128491d4faab7b79377b811be6b3d4
// create functions for updating data
stdb.updateNest = function( id,  name, discovery, predicted, actual, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET name = ?, discovery_date = ?, predicted_delivery = ?, actual_delivery = ?, nest_lat = ?, nest_lon = ? WHERE id = ?',
			[ name, discovery, predicted, actual, lat, lon, id ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateObservation = function( nest_id, osbserved, temp, cond ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET nest_id = ?, observed_on = ?, temperature = ?, condition = ? WHERE id = ?',
			[ nest_id, osbserved, temp, cond ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateSpecies = function( name, description ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET name = ?, description = ? WHERE id = ?',
			[ name, description ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateTurtles = function( id, dob, gender, species_id, birth_lat, birth_lon, deceased ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET id = ?, dob = ?, gender = ?, species_id = ?, birth_lat = ?, birth_lon = ?, deceased = ? WHERE id = ?',
			[ id, dob, species_id, birth_lat, birth_lon, deceased ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
stdb.updateSitings = function( turtle_id, condition, is_injured, sited_on, lat, lon ) {
	stdb.db.transaction( function( tx ) {
		tx.executeSql( 'UPDATE nests SET turtle_id = ?, condition = ?, is_injured = ?, sited_on = ?, lat = ?, lon = ? WHERE id = ?',
			[ turtle_id, condition, is_injured, sited_on, lat, lon ],
			stdb.onSuccess,
			stdb.onError
		);
	});
};
// create functions for deleting data
stdb.deleteNest = function( id ) {
	stdb.db.transaction( function( tx ) {
<<<<<<>>>>>> 1f3e74f5c0128491d4faab7b79377b811be6b3d4
		tx.executeSql( 'DELETE FROM nests WHERE id = ?',
			[ id ],
			null,
			stdb.onError
		);
		// next get rid of any associated observations
<<<<<<< HEAD
		tx.executeSql( 'DELETE FROM observations WHERE nest_id = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
		// next get rid of any associated species
		tx.executeSql( 'DELETE FROM species WHERE name = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
		// next get rid of any associated turtles
		tx.executeSql( 'DELETE FROM turtles WHERE id = ?')
			[ id ]
			stdb.onSuccess
			stdb.onError
		);
		tx.executeSql( 'DELETE FROM sitings WHERE turtle_id = ?')
			[ id ]
			stdb.onSuccess
=======
		tx.executeSql( 'DELETE FROM observations WHERE nest_id = ?',
			[ id ],
			stdb.onSuccess,
>>>>>>> 1f3e74f5c0128491d4faab7b79377b811be6b3d4
			stdb.onError
		);
	});
};
// initialize the app
$( document ).on( 'mobileinit', function() {

	// initialize our database
	stdb.open();
	stdb.createSchema();

});

// code to run after the page has loaded
$( document ).ready( function() {
	var displayNests;

	displayNests = function( tx, rs ) {
		var nestlist;
		if ( rs.rows.length > 0 ) {
			nestlist  = '
';
			for ( i = 0; i < rs.rows.length; i++ ) {
				nestlist += '
'
				nestlist += rs.rows.item(i).name;
				nestlist += '';
				nestlist += 'Edit';
				nestlist += 'Edit';
				nestlist += 'Edit;
				nestlist += '';
				nestlist += '
';

			}
			nestlist += '
';
			$( '#nests div[data-role="content"]' ).prepend( nestlist ).trigger( 'create' );
		} else {
			$( '#nests div[data-role="content"]' ).prepend( '
No nests have been added to the database yet. Click the button below to add one.
' );
		}
	};

	// handle form submissions
	$( '#plot_submit' ).on( 'click', function( e ) {
		// don't actually try to follow the link
		e.preventDefault();

		// get the data from the fields
		var name = $( '#plot #name' ).val(),
			lat  = $( '#plot #lat'  ).val(),
			lon  = $( '#plot #lon'  ).val(),
			disc = $( '#plot #disc' ).val(),
			pred = $( '#plot #pred' ).val(),
			act  = $( '#plot #act'  ).val();

		// try to add it to the database
		stdb.addNest( name, lat, lon, disc, pred, act );

		// go back to the list of nests page
		$.mobile.changePage( '#nests' );
	});

	// handle page transitions
	$( '#nests' ).on( 'pagebeforeshow', function( e ) { stdb.getNests( displayNests ); });
});
<|endoftext|>


<commit_before>
import { GltfView } from 'gltf-viewer-source';

import { UIModel } from './logic/uimodel.js';
import { app } from './ui/ui.js';
import { Observable, Subject, from, merge } from 'rxjs';
import { mergeMap, filter, map, multicast } from 'rxjs/operators';
import { gltfModelPathProvider, fillEnvironmentWithPaths } from './model_path_provider.js';

async function main()
{
    const canvas = document.getElementById("canvas");
    const context = canvas.getContext("webgl2", { alpha: false, antialias: true });
    const ui = document.getElementById("app");
    const view = new GltfView(context);
    const resourceLoader = view.createResourceLoader();
    const state = view.createState();
    state.renderingParameters.useDirectionalLightsWithDisabledIBL = true;

    const pathProvider = new gltfModelPathProvider('assets/models/2.0/model-index.json');
    await pathProvider.initialize();
    const environmentPaths = fillEnvironmentWithPaths({
        "footprint_court": "Footprint Court",
        "pisa": "Pisa",
        "doge2": "Doge's palace",
        "ennis": "Dining room",
        "field": "Field",
        "helipad": "Helipad Goldenhour",
        "papermill": "Papermill Ruins",
        "neutral": "Studio Neutral",
        "Cannon_Exterior": "Cannon Exterior",
        "Colorful_Studio": "Colorful Studio",
        "Wide_Street" : "Wide Street",
    }, "assets/environments/");

    const uiModel = new UIModel(app, pathProvider, environmentPaths);

    // whenever a new model is selected, load it and when complete pass the loaded gltf
    // into a stream back into the UI
    const gltfLoadedSubject = new Subject();
    const gltfLoadedMulticast = uiModel.model.pipe(
        mergeMap( (model) =>
        {
        	uiModel.goToLoadingState();

            // Workaround for errors in ktx lib after loading an asset with ktx2 files for the second time:
            resourceLoader.initKtxLib();

            return from(resourceLoader.loadGltf(model.mainFile, model.additionalFiles).then( (gltf) => {
                state.gltf = gltf;
                const defaultScene = state.gltf.scene;
                state.sceneIndex = defaultScene === undefined ? 0 : defaultScene;
                state.cameraIndex = undefined;
                if (state.gltf.scenes.length != 0)
                {
                    if(state.sceneIndex > state.gltf.scenes.length - 1)
                    {
                        state.sceneIndex = 0;
                    }
                    const scene = state.gltf.scenes[state.sceneIndex];
                    scene.applyTransformHierarchy(state.gltf);
                    state.userCamera.aspectRatio = canvas.width / canvas.height;
                    state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);

                    // Try to start as many animations as possible without generating conficts.
                    state.animationIndices = [];
                    for (let i = 0; i < gltf.animations.length; i++)
                    {
                        if (!gltf.nonDisjointAnimations(state.animationIndices).includes(i))
                        {
                            state.animationIndices.push(i);
                        }
                    }
                    state.animationTimer.start();
                }

                uiModel.exitLoadingState();

                return state;
            })
            );
        }),
        // transform gltf loaded observable to multicast observable to avoid multiple execution with multiple subscriptions
        multicast(gltfLoadedSubject)
    );

    uiModel.disabledAnimations(uiModel.activeAnimations.pipe(map(animationIndices => {
        // Disable all animations which are not disjoint to the current selection of animations.
        return state.gltf.nonDisjointAnimations(animationIndices);
    })));

    const sceneChangedSubject = new Subject();
    const sceneChangedObservable = uiModel.scene.pipe(map( newSceneIndex => {
        state.sceneIndex = newSceneIndex;
        state.cameraIndex = undefined;
        const scene = state.gltf.scenes[state.sceneIndex];
        if (scene !== undefined)
        {
            scene.applyTransformHierarchy(state.gltf);
            state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);
        }
    }),
    multicast(sceneChangedSubject)
    );

    const statisticsUpdateObservableTemp = merge(
        gltfLoadedMulticast,
        sceneChangedObservable
    );

    const statisticsUpdateObservable = statisticsUpdateObservableTemp.pipe(
        map( (_) => view.gatherStatistics(state) )
    );

    const cameraExportChangedObservable = uiModel.cameraValuesExport.pipe( map(_ => {
        let camera = state.userCamera;
        if(state.cameraIndex !== undefined)
        {
            camera = state.gltf.cameras[state.cameraIndex];
        }
        const cameraDesc = camera.getDescription(state.gltf);
        return cameraDesc;
    }));

    const downloadDataURL = (filename, dataURL) => {
        var element = document.createElement('a');
        element.setAttribute('href', dataURL);
        element.setAttribute('download', filename);

        element.style.display = 'none';
        document.body.appendChild(element);

        element.click();

        document.body.removeChild(element);
    };

    cameraExportChangedObservable.subscribe( cameraDesc => {
        const gltf = JSON.stringify(cameraDesc, undefined, 4);
        const dataURL = 'data:text/plain;charset=utf-8,' +  encodeURIComponent(gltf);
        downloadDataURL("camera.gltf", dataURL);
    });

    uiModel.captureCanvas.subscribe( () => {
        view.renderFrame(state, canvas.width, canvas.height);
        const dataURL = canvas.toDataURL();
        downloadDataURL("capture.png", dataURL);
    });

    uiModel.scene.pipe(filter(scene => scene === -1)).subscribe( () => {
        state.sceneIndex = undefined;
    });
    uiModel.scene.pipe(filter(scene => scene !== -1)).subscribe( scene => {
        state.sceneIndex = scene;
    });

    uiModel.camera.pipe(filter(camera => camera === -1)).subscribe( () => {
        state.cameraIndex = undefined;
    });
    uiModel.camera.pipe(filter(camera => camera !== -1)).subscribe( camera => {
        state.cameraIndex = camera;
    });

    uiModel.variant.subscribe( variant => {
        state.variant = variant;
    });

    uiModel.tonemap.subscribe( tonemap => {
        state.renderingParameters.toneMap = tonemap;
    });

    uiModel.debugchannel.subscribe( debugchannel => {
        state.renderingParameters.debugOutput = debugchannel;
    });

    uiModel.skinningEnabled.subscribe( skinningEnabled => {
        state.renderingParameters.skinning = skinningEnabled;
    });

    uiModel.exposurecompensation.subscribe( exposurecompensation => {
        state.renderingParameters.exposure = Math.pow(2, exposurecompensation);
    });

    uiModel.morphingEnabled.subscribe( morphingEnabled => {
        state.renderingParameters.morphing = morphingEnabled;
    });

    uiModel.clearcoatEnabled.subscribe( clearcoatEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_clearcoat = clearcoatEnabled;
    });
    uiModel.sheenEnabled.subscribe( sheenEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_sheen = sheenEnabled;
    });
    uiModel.transmissionEnabled.subscribe( transmissionEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_transmission = transmissionEnabled;
    });
    uiModel.volumeEnabled.subscribe( volumeEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_volume = volumeEnabled;
    });
    uiModel.iorEnabled.subscribe( iorEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_ior = iorEnabled;
    });
    uiModel.specularEnabled.subscribe( specularEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_specular = specularEnabled;
    });

    uiModel.iblEnabled.subscribe( iblEnabled => {
        state.renderingParameters.useIBL = iblEnabled;
    });

    uiModel.renderEnvEnabled.subscribe( renderEnvEnabled => {
        state.renderingParameters.renderEnvironmentMap = renderEnvEnabled;
    });
    uiModel.blurEnvEnabled.subscribe( blurEnvEnabled => {
        state.renderingParameters.blurEnvironmentMap = blurEnvEnabled;
    });

    uiModel.punctualLightsEnabled.subscribe( punctualLightsEnabled => {
        state.renderingParameters.usePunctual = punctualLightsEnabled;
    });

    uiModel.environmentRotation.subscribe( environmentRotation => {
        switch (environmentRotation)
        {
        case "+Z":
            state.renderingParameters.environmentRotation = 90.0;
            break;
        case "-X":
            state.renderingParameters.environmentRotation = 180.0;
            break;
        case "-Z":
            state.renderingParameters.environmentRotation = 270.0;
            break;
        case "+X":
            state.renderingParameters.environmentRotation = 0.0;
            break;
        }
    });

    uiModel.clearColor.subscribe( clearColor => {
        state.renderingParameters.clearColor = clearColor;
    });

    uiModel.animationPlay.subscribe( animationPlay => {
        if(animationPlay)
        {
            state.animationTimer.unpause();
        }
        else
        {
            state.animationTimer.pause();
        }
    });

    uiModel.activeAnimations.subscribe( animations => {
        state.animationIndices = animations;
    });

    uiModel.hdr.subscribe( hdrFile => {
        resourceLoader.loadEnvironment(hdrFile).then( (environment) => {
            state.environment = environment;
        });
    });

    uiModel.attachGltfLoaded(gltfLoadedMulticast);
    uiModel.updateStatistics(statisticsUpdateObservable);
    const sceneChangedStateObservable = uiModel.scene.pipe(map( newSceneIndex => state));
    uiModel.attachCameraChangeObservable(sceneChangedStateObservable);
    gltfLoadedMulticast.connect();

    uiModel.orbit.subscribe( orbit => {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.orbit(orbit.deltaPhi, orbit.deltaTheta);
        }
    });

    uiModel.pan.subscribe( pan => {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.pan(pan.deltaX, -pan.deltaY);
        }
    });

    uiModel.zoom.subscribe( zoom => {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.zoomBy(zoom.deltaZoom);
        }
    });

    // configure the animation loop
    const update = () =>
    {
        const devicePixelRatio = window.devicePixelRatio || 1;

        // set the size of the drawingBuffer based on the size it's displayed.
        canvas.width = Math.floor(canvas.clientWidth * devicePixelRatio);
        canvas.height = Math.floor(canvas.clientHeight * devicePixelRatio);

        view.renderFrame(state, canvas.width, canvas.height);
        window.requestAnimationFrame(update);
    };

    // After this start executing animation loop.
    window.requestAnimationFrame(update);
}

export { main };
<commit_msg>Web app: Skip frames when no user input and nothing has changed<commit_after>
import { GltfView } from 'gltf-viewer-source';

import { UIModel } from './logic/uimodel.js';
import { app } from './ui/ui.js';
import { Observable, Subject, from, merge } from 'rxjs';
import { mergeMap, filter, map, multicast } from 'rxjs/operators';
import { gltfModelPathProvider, fillEnvironmentWithPaths } from './model_path_provider.js';

async function main()
{
    const canvas = document.getElementById("canvas");
    const context = canvas.getContext("webgl2", { alpha: false, antialias: true });
    const ui = document.getElementById("app");
    const view = new GltfView(context);
    const resourceLoader = view.createResourceLoader();
    const state = view.createState();
    state.renderingParameters.useDirectionalLightsWithDisabledIBL = true;

    const pathProvider = new gltfModelPathProvider('assets/models/2.0/model-index.json');
    await pathProvider.initialize();
    const environmentPaths = fillEnvironmentWithPaths({
        "footprint_court": "Footprint Court",
        "pisa": "Pisa",
        "doge2": "Doge's palace",
        "ennis": "Dining room",
        "field": "Field",
        "helipad": "Helipad Goldenhour",
        "papermill": "Papermill Ruins",
        "neutral": "Studio Neutral",
        "Cannon_Exterior": "Cannon Exterior",
        "Colorful_Studio": "Colorful Studio",
        "Wide_Street" : "Wide Street",
    }, "assets/environments/");

    const uiModel = new UIModel(app, pathProvider, environmentPaths);

    // whenever a new model is selected, load it and when complete pass the loaded gltf
    // into a stream back into the UI
    const gltfLoadedSubject = new Subject();
    const gltfLoadedMulticast = uiModel.model.pipe(
        mergeMap( (model) =>
        {
        	uiModel.goToLoadingState();

            // Workaround for errors in ktx lib after loading an asset with ktx2 files for the second time:
            resourceLoader.initKtxLib();

            return from(resourceLoader.loadGltf(model.mainFile, model.additionalFiles).then( (gltf) => {
                state.gltf = gltf;
                const defaultScene = state.gltf.scene;
                state.sceneIndex = defaultScene === undefined ? 0 : defaultScene;
                state.cameraIndex = undefined;
                if (state.gltf.scenes.length != 0)
                {
                    if(state.sceneIndex > state.gltf.scenes.length - 1)
                    {
                        state.sceneIndex = 0;
                    }
                    const scene = state.gltf.scenes[state.sceneIndex];
                    scene.applyTransformHierarchy(state.gltf);
                    state.userCamera.aspectRatio = canvas.width / canvas.height;
                    state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);

                    // Try to start as many animations as possible without generating conficts.
                    state.animationIndices = [];
                    for (let i = 0; i < gltf.animations.length; i++)
                    {
                        if (!gltf.nonDisjointAnimations(state.animationIndices).includes(i))
                        {
                            state.animationIndices.push(i);
                        }
                    }
                    state.animationTimer.start();
                }

                uiModel.exitLoadingState();

                return state;
            })
            );
        }),
        // transform gltf loaded observable to multicast observable to avoid multiple execution with multiple subscriptions
        multicast(gltfLoadedSubject)
    );

    uiModel.disabledAnimations(uiModel.activeAnimations.pipe(map(animationIndices => {
        // Disable all animations which are not disjoint to the current selection of animations.
        return state.gltf.nonDisjointAnimations(animationIndices);
    })));

    const sceneChangedSubject = new Subject();
    const sceneChangedObservable = uiModel.scene.pipe(map( newSceneIndex => {
        state.sceneIndex = newSceneIndex;
        state.cameraIndex = undefined;
        const scene = state.gltf.scenes[state.sceneIndex];
        if (scene !== undefined)
        {
            scene.applyTransformHierarchy(state.gltf);
            state.userCamera.fitViewToScene(state.gltf, state.sceneIndex);
        }
    }),
    multicast(sceneChangedSubject)
    );

    const statisticsUpdateObservableTemp = merge(
        gltfLoadedMulticast,
        sceneChangedObservable
    );

    const statisticsUpdateObservable = statisticsUpdateObservableTemp.pipe(
        map( (_) => view.gatherStatistics(state) )
    );

    const cameraExportChangedObservable = uiModel.cameraValuesExport.pipe( map(_ => {
        let camera = state.userCamera;
        if(state.cameraIndex !== undefined)
        {
            camera = state.gltf.cameras[state.cameraIndex];
        }
        const cameraDesc = camera.getDescription(state.gltf);
        return cameraDesc;
    }));

    const downloadDataURL = (filename, dataURL) => {
        var element = document.createElement('a');
        element.setAttribute('href', dataURL);
        element.setAttribute('download', filename);

        element.style.display = 'none';
        document.body.appendChild(element);

        element.click();

        document.body.removeChild(element);
    };

    cameraExportChangedObservable.subscribe( cameraDesc => {
        const gltf = JSON.stringify(cameraDesc, undefined, 4);
        const dataURL = 'data:text/plain;charset=utf-8,' +  encodeURIComponent(gltf);
        downloadDataURL("camera.gltf", dataURL);
    });

    uiModel.captureCanvas.subscribe( () => {
        view.renderFrame(state, canvas.width, canvas.height);
        const dataURL = canvas.toDataURL();
        downloadDataURL("capture.png", dataURL);
    });

    let changed = false;
    const listenForChange = stream => stream.subscribe(() => changed = true);

    uiModel.scene.pipe(filter(scene => scene === -1)).subscribe( () => {
        state.sceneIndex = undefined;
    });
    uiModel.scene.pipe(filter(scene => scene !== -1)).subscribe( scene => {
        state.sceneIndex = scene;
    });
    listenForChange(uiModel.scene);

    uiModel.camera.pipe(filter(camera => camera === -1)).subscribe( () => {
        state.cameraIndex = undefined;
    });
    uiModel.camera.pipe(filter(camera => camera !== -1)).subscribe( camera => {
        state.cameraIndex = camera;
    });
    listenForChange(uiModel.camera);

    uiModel.variant.subscribe( variant => {
        state.variant = variant;
    });
    listenForChange(uiModel.variant);

    uiModel.tonemap.subscribe( tonemap => {
        state.renderingParameters.toneMap = tonemap;
    });
    listenForChange(uiModel.tonemap);

    uiModel.debugchannel.subscribe( debugchannel => {
        state.renderingParameters.debugOutput = debugchannel;
    });
    listenForChange(uiModel.debugchannel);

    uiModel.skinningEnabled.subscribe( skinningEnabled => {
        state.renderingParameters.skinning = skinningEnabled;
    });
    listenForChange(uiModel.skinningEnabled);

    uiModel.exposurecompensation.subscribe( exposurecompensation => {
        state.renderingParameters.exposure = Math.pow(2, exposurecompensation);
    });
    listenForChange(uiModel.exposurecompensation);

    uiModel.morphingEnabled.subscribe( morphingEnabled => {
        state.renderingParameters.morphing = morphingEnabled;
    });
    listenForChange(uiModel.morphingEnabled);

    uiModel.clearcoatEnabled.subscribe( clearcoatEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_clearcoat = clearcoatEnabled;
    });
    uiModel.sheenEnabled.subscribe( sheenEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_sheen = sheenEnabled;
    });
    uiModel.transmissionEnabled.subscribe( transmissionEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_transmission = transmissionEnabled;
    });
    uiModel.volumeEnabled.subscribe( volumeEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_volume = volumeEnabled;
    });
    uiModel.iorEnabled.subscribe( iorEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_ior = iorEnabled;
    });
    uiModel.specularEnabled.subscribe( specularEnabled => {
        state.renderingParameters.enabledExtensions.KHR_materials_specular = specularEnabled;
    });
    listenForChange(uiModel.clearcoatEnabled);
    listenForChange(uiModel.sheenEnabled);
    listenForChange(uiModel.transmissionEnabled);
    listenForChange(uiModel.volumeEnabled);
    listenForChange(uiModel.iorEnabled);
    listenForChange(uiModel.specularEnabled);

    uiModel.iblEnabled.subscribe( iblEnabled => {
        state.renderingParameters.useIBL = iblEnabled;
    });
    listenForChange(uiModel.iblEnabled);

    uiModel.renderEnvEnabled.subscribe( renderEnvEnabled => {
        state.renderingParameters.renderEnvironmentMap = renderEnvEnabled;
    });
    uiModel.blurEnvEnabled.subscribe( blurEnvEnabled => {
        state.renderingParameters.blurEnvironmentMap = blurEnvEnabled;
    });
    listenForChange(uiModel.renderEnvEnabled);
    listenForChange(uiModel.blurEnvEnabled);

    uiModel.punctualLightsEnabled.subscribe( punctualLightsEnabled => {
        state.renderingParameters.usePunctual = punctualLightsEnabled;
    });
    listenForChange(uiModel.punctualLightsEnabled);

    uiModel.environmentRotation.subscribe( environmentRotation => {
        switch (environmentRotation)
        {
        case "+Z":
            state.renderingParameters.environmentRotation = 90.0;
            break;
        case "-X":
            state.renderingParameters.environmentRotation = 180.0;
            break;
        case "-Z":
            state.renderingParameters.environmentRotation = 270.0;
            break;
        case "+X":
            state.renderingParameters.environmentRotation = 0.0;
            break;
        }
    });
    listenForChange(uiModel.environmentRotation);

    uiModel.clearColor.subscribe( clearColor => {
        state.renderingParameters.clearColor = clearColor;
    });
    listenForChange(uiModel.clearColor);

    uiModel.animationPlay.subscribe( animationPlay => {
        if(animationPlay)
        {
            state.animationTimer.unpause();
        }
        else
        {
            state.animationTimer.pause();
        }
    });

    uiModel.activeAnimations.subscribe( animations => {
        state.animationIndices = animations;
    });
    listenForChange(uiModel.activeAnimations);

    uiModel.hdr.subscribe( hdrFile => {
        resourceLoader.loadEnvironment(hdrFile).then( (environment) => {
            state.environment = environment;
        });
    });
    listenForChange(uiModel.hdr);

    uiModel.attachGltfLoaded(gltfLoadedMulticast);
    uiModel.updateStatistics(statisticsUpdateObservable);
    const sceneChangedStateObservable = uiModel.scene.pipe(map( newSceneIndex => state));
    uiModel.attachCameraChangeObservable(sceneChangedStateObservable);
    gltfLoadedMulticast.connect();

    uiModel.orbit.subscribe( orbit => {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.orbit(orbit.deltaPhi, orbit.deltaTheta);
        }
    });
    listenForChange(uiModel.orbit);

    uiModel.pan.subscribe( pan => {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.pan(pan.deltaX, -pan.deltaY);
        }
    });
    listenForChange(uiModel.pan);

    uiModel.zoom.subscribe( zoom => {
        if (state.cameraIndex === undefined)
        {
            state.userCamera.zoomBy(zoom.deltaZoom);
        }
    });
    listenForChange(uiModel.zoom);

    // configure the animation loop
    const past = {}
    const update = () =>
    {
        const devicePixelRatio = window.devicePixelRatio || 1;

        // set the size of the drawingBuffer based on the size it's displayed.
        canvas.width = Math.floor(canvas.clientWidth * devicePixelRatio);
        canvas.height = Math.floor(canvas.clientHeight * devicePixelRatio);

        let skip = true
        skip &= state.animationTimer.paused || state.animationIndices.length == 0
        skip &= past.width == canvas.width
        skip &= past.height == canvas.height

        past.width = canvas.width
        past.height = canvas.height

        if (changed || !skip) {
            view.renderFrame(state, canvas.width, canvas.height);
        }

        changed = false

        window.requestAnimationFrame(update);
    };

    // After this start executing animation loop.
    window.requestAnimationFrame(update);
}

export { main };
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import d3 from 'd3';

//some examples
import {barChart} from './charts/barChart';
import {pieChart} from './charts/pieChart';
import d3Store from './stores/D3stores.js';
import d3Actions from './actions/D3actions.js';

class D3Component extends React.Component {
  constructor() {
    super();
    this.state = {
      chartObject: {},
      chartStyle: { //svg-container
        display: 'block',
        position: 'relative',
        width: '100%',
        'paddingBottom': '50%', //adjust below for other aspect ratios!
        'verticalAlign': 'middle',
        overflow: 'hidden'
      }
    };
  }

  componentWillUnmount() {
    if (this.state.chartObject.destroyFunction) {
      this.state.chartObject.destroyFunction();
    }
    this.unsubscribe();
  }

  componentDidMount() {
    //load reflux store
    this.unsubscribe = d3Store.listen(this.onStatusChange.bind(this));
    //create new chart
    this.createNewChart(this.props.chartType, this.props);
  }

  componentWillReceiveProps(newProps) {
    //we check if we need to create a new chart or update the existing one
    if (!this.state.chartObject.mainFunction ||
      newProps.chartType !== this.props.chartType) {
      this.createNewChart(newProps.chartType, newProps);
    } else {
      this.state.chartObject.updateFunction(newProps.data, newProps.params);
    }
    //also, check if padding has changed
    if (this.props.paddingBottom !== newProps.paddingBottom) {
      this.setState({
        chartStyle: Object.assign({}, this.state.chartStyle, {paddingBottom: newProps.paddingBottom})
      });
    }
  }

  onStatusChange(obj, emitGroups) {
    //check if you have an overlap between highlightEmit and highlightListen
    let listenGroups = this.props.highlightListen;
    let intersection = emitGroups.filter(function(n) {
      return listenGroups.indexOf(n) != -1
    });

    if (intersection.length && this.props.highlight && this.state.chartObject.onEvent) {
      this.state.chartObject.onEvent(obj);
    }
  }

  createNewChart(chartPrototype, props) {
    //clean up existing stuff
    d3.select(React.findDOMNode(this)).select('#d3graphSVG').remove();
    //Create afresh
    let chartObject, paddingBottom;
    switch (chartPrototype) {
      case 'bar':
        chartObject = Object.create(barChart);
        paddingBottom = '100%';
        break;
      case 'pie':
        chartObject = Object.create(pieChart);
        paddingBottom = '100%';
        break;
      case 'custom':
        chartObject = Object.create(props.chartModule);
        paddingBottom = this.props.paddingBottom;
        break;
      default:
        chartObject = Object.create(barChart);
        paddingBottom = '100%';
    }

    this.setState({
      chartObject,
      chartStyle: Object.assign({}, this.state.chartStyle, {paddingBottom})
    });

    //and create it:
    chartObject.mainFunction(d3.select(React.findDOMNode(this)),
      props.data, props.params, this);
  }

  handleChartEvent(d, event) {
    //call action
    d3Actions.d3Event(d, event, this.props.highlightEmit);
  }

  render() {
    return (<div
      style={this.state.chartStyle}
      />);
  }
}

D3Component.defaultProps = {
  params: {},
  chartType: 'bar',
  paddingBottom: '100%',
  chartModule: barChart,
  data: [],
  highlight: true,
  highlightEmit: ['default'],
  highlightListen: ['default']
};

D3Component.propTypes = {
  params: React.PropTypes.object,
  chartType: React.PropTypes.string,
  paddingBottom: React.PropTypes.string,
  chartModule: React.PropTypes.object,
  data: React.PropTypes.array,
  highlight: React.PropTypes.bool,
  highlightEmit: React.PropTypes.array,
  highlightListen: React.PropTypes.array
};

module.exports = D3Component;<commit_msg>Add bubble up of chart event.<commit_after>'use strict';

import React from 'react';
import d3 from 'd3';

//some examples
import {barChart} from './charts/barChart';
import {pieChart} from './charts/pieChart';
import d3Store from './stores/D3stores.js';
import d3Actions from './actions/D3actions.js';

class D3Component extends React.Component {
  constructor() {
    super();
    this.state = {
      chartObject: {},
      chartStyle: { //svg-container
        display: 'block',
        position: 'relative',
        width: '100%',
        'paddingBottom': '50%', //adjust below for other aspect ratios!
        'verticalAlign': 'middle',
        overflow: 'hidden'
      }
    };
  }

  componentWillUnmount() {
    if (this.state.chartObject.destroyFunction) {
      this.state.chartObject.destroyFunction();
    }
    this.unsubscribe();
  }

  componentDidMount() {
    //load reflux store
    this.unsubscribe = d3Store.listen(this.onStatusChange.bind(this));
    //create new chart
    this.createNewChart(this.props.chartType, this.props);
  }

  componentWillReceiveProps(newProps) {
    //we check if we need to create a new chart or update the existing one
    if (!this.state.chartObject.mainFunction ||
      newProps.chartType !== this.props.chartType) {
      this.createNewChart(newProps.chartType, newProps);
    } else {
      this.state.chartObject.updateFunction(newProps.data, newProps.params);
    }
    //also, check if padding has changed
    if (this.props.paddingBottom !== newProps.paddingBottom) {
      this.setState({
        chartStyle: Object.assign({}, this.state.chartStyle, {paddingBottom: newProps.paddingBottom})
      });
    }
  }

  onStatusChange(obj, emitGroups) {
    //check if you have an overlap between highlightEmit and highlightListen
    let listenGroups = this.props.highlightListen;
    let intersection = emitGroups.filter(function(n) {
      return listenGroups.indexOf(n) != -1
    });

    if (intersection.length && this.props.highlight && this.state.chartObject.onEvent) {
      this.state.chartObject.onEvent(obj);
    }
  }

  createNewChart(chartPrototype, props) {
    //clean up existing stuff
    d3.select(React.findDOMNode(this)).select('#d3graphSVG').remove();
    //Create afresh
    let chartObject, paddingBottom;
    switch (chartPrototype) {
      case 'bar':
        chartObject = Object.create(barChart);
        paddingBottom = '100%';
        break;
      case 'pie':
        chartObject = Object.create(pieChart);
        paddingBottom = '100%';
        break;
      case 'custom':
        chartObject = Object.create(props.chartModule);
        paddingBottom = this.props.paddingBottom;
        break;
      default:
        chartObject = Object.create(barChart);
        paddingBottom = '100%';
    }

    this.setState({
      chartObject,
      chartStyle: Object.assign({}, this.state.chartStyle, {paddingBottom})
    });

    //and create it:
    chartObject.mainFunction(d3.select(React.findDOMNode(this)),
      props.data, props.params, this);
  }

  handleChartEvent(d, event) {
    //call action
    if (this.props.onChartEvent) {
      this.props.onChartEvent(d, event);
    }
    d3Actions.d3Event(d, event, this.props.highlightEmit);
  }

  render() {
    return (<div
      style={this.state.chartStyle}
      />);
  }
}

D3Component.defaultProps = {
  params: {},
  chartType: 'bar',
  paddingBottom: '100%',
  chartModule: barChart,
  data: [],
  highlight: true,
  highlightEmit: ['default'],
  highlightListen: ['default']
};

D3Component.propTypes = {
  params: React.PropTypes.object,
  chartType: React.PropTypes.string,
  paddingBottom: React.PropTypes.string,
  chartModule: React.PropTypes.object,
  data: React.PropTypes.array,
  highlight: React.PropTypes.bool,
  highlightEmit: React.PropTypes.array,
  highlightListen: React.PropTypes.array
};

module.exports = D3Component;<|endoftext|>


<commit_before><commit_msg>pushing new compiled js<commit_after><|endoftext|>


<commit_before>// Plugin order is critical:
Leap.loop()
  .use('transform', {
    vr: true
  })
  .use('boneHand', {
    scene: null, // this tells boneHand to use defer scene usage/creation.
    opacity: 0.7,
    jointColor: new THREE.Color(0x222222),
    arm: true
  })
  .use('proximity')
  .use('pinchEvent')
  .use('playback', {
    pauseOnHand: true,
    loop: false,
    overlay: false,
    resumeOnHandLost: false,
    autoPlay: false
  })
  .use('twoHandRecognizer');

Leap.loopController.setBackground(true);

Leap.loopController.on('ready', function(){
  console.log('Leap Motion Controller ready');
  ga('send', 'event', 'Leap', 'ready');

  var connection = this.connection;
  this.connection.on('focus', function(){
    if (!VRClientReady) return;

    connection.reportFocus(VRClientFocused);
  });

});

var VRClientReady = false;
var VRClientFocused = true;
VRClient.onFocus = function(){
  VRClientFocused = true;

  cursor.enable();

  var connection = Leap.loopController.connection;
  if (!connection) return;

  connection.reportFocus(true);
};

VRClient.onBlur = function(){
  VRClientFocused = false;

  cursor.disable();

  var connection = Leap.loopController.connection;
  if (!connection) return;

  connection.reportFocus(false);
};

angular.module('index', ['directives']);

// Trying to figure out where to go next?
// Check out directives/scene.js<commit_msg>Fix issue where framerate would drop without hands in the scene; Remove backgrounding.<commit_after>// Plugin order is critical:
Leap.loop()
  .use('transform', {
    vr: true
  })
  .use('boneHand', {
    scene: null, // this tells boneHand to use defer scene usage/creation.
    opacity: 0.7,
    jointColor: new THREE.Color(0x222222),
    arm: true
  })
  .use('proximity')
  .use('pinchEvent')
  .use('playback', {
    pauseOnHand: true,
    loop: false,
    overlay: false,
    resumeOnHandLost: false,
    autoPlay: false
  })
  .use('twoHandRecognizer');

Leap.loopController.loopWhileDisconnected = true;

Leap.loopController.on('ready', function(){
  console.log('Leap Motion Controller ready');
  ga('send', 'event', 'Leap', 'ready');

  var connection = this.connection;
  this.connection.on('focus', function(){
    if (!VRClientReady) return;

    connection.reportFocus(VRClientFocused);
  });

});

var VRClientReady = false;
var VRClientFocused = true;
VRClient.onFocus = function(){
  VRClientFocused = true;

  cursor.enable();

  var connection = Leap.loopController.connection;
  if (!connection) return;

  connection.reportFocus(true);
};

VRClient.onBlur = function(){
  VRClientFocused = false;

  cursor.disable();

  var connection = Leap.loopController.connection;
  if (!connection) return;

  connection.reportFocus(false);
};

angular.module('index', ['directives']);

// Trying to figure out where to go next?
// Check out directives/scene.js<|endoftext|>


<commit_before>// Expose plugin as an AMD module if AMD loader is present:
(function (factory) {
  'use strict';

  if (typeof define === 'function' && define.amd) {
    define(['knockout', 'jquery'], factory);
  } else {
    window.ko.bindingHandlers.attache = factory(ko, jQuery);
  }

}(function (ko, $) {
  'use strict';

  /**
   * A Knockout binding handler for Attache
   *
   * @global
   */
  var attache = {
    /** @lends ko.bindingHandlers.attache */
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
      var $element        = $(element),
          user            = ko.unwrap(valueAccessor()),
          attacheTemplate = allBindingsAccessor().attacheTemplate,
          attacheOptions  = allBindingsAccessor().attacheOptions || {},
          contentCallback;

      contentCallback = function(anchor, popover) {
        ko.renderTemplate(attacheTemplate, bindingContext, {}, popover.get(0));
      };

      $element.attache(attacheOptions);
      $element.attache().addCallback('afterCreate', contentCallback);

      $element.on('click', function(ev) {
        if ($element.attache().isActive()) {
          $element.attache().hide();
        } else {
          $element.attache().show();
        }

        ev.stopPropagation();
      });

      ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
        try {
          $(element).attache().destroy();
        } catch (e) {

        }
      });
    }
  };

  return attache;
}));<commit_msg>Actually use the context given to us as the value accessor<commit_after>// Expose plugin as an AMD module if AMD loader is present:
(function (factory) {
  'use strict';

  if (typeof define === 'function' && define.amd) {
    define(['knockout', 'jquery'], factory);
  } else {
    window.ko.bindingHandlers.attache = factory(ko, jQuery);
  }

}(function (ko, $) {
  'use strict';

  /**
   * A Knockout binding handler for Attache
   *
   * @global
   */
  var attache = {
    /** @lends ko.bindingHandlers.attache */
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
      var $element        = $(element),
          context         = ko.unwrap(valueAccessor()),
          attacheTemplate = allBindingsAccessor().attacheTemplate,
          attacheOptions  = allBindingsAccessor().attacheOptions || {},
          contentCallback;

      contentCallback = function(anchor, popover) {
        ko.renderTemplate(attacheTemplate, context, {}, popover.get(0));
      };

      $element.attache(attacheOptions);
      $element.attache().addCallback('afterCreate', contentCallback);

      $element.on('click', function(ev) {
        if ($element.attache().isActive()) {
          $element.attache().hide();
        } else {
          $element.attache().show();
        }

        ev.stopPropagation();
      });

      ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
        try {
          $(element).attache().destroy();
        } catch (e) {

        }
      });
    }
  };

  return attache;
}));<|endoftext|>


<commit_before>import Grid from '../utils/Grid';
import Peg from '../objects/Peg';
import {
    getGamePosition,
    getGridPosition,
} from '../utils/position';

import {
    AUDIO_ERROR_URI,
    AUDIO_JUMP_URI,
    BOARD_SIZE,
    MIDDLE,
    NUM_PEG_TYPES,
    NUM_TILE_TYPES,
    DEATH_DURATION,
    DEATH_SCALE,
    DEATH_ALPHA,
    RESET_URI,
    JUMP_DURATION,
} from '../utils/constants';

const TILE_URI = 'images/tiles.png';
const PEGS_URI = 'images/pegs.png';

class GameState extends Phaser.State {
    preload() {
        this.game.load.audio('jump', AUDIO_JUMP_URI);
        this.game.load.audio('error', AUDIO_ERROR_URI);
        this.game.load.spritesheet('tiles', TILE_URI, 65, 89, NUM_TILE_TYPES);
        this.game.load.spritesheet('pegs', PEGS_URI, 40, 66, NUM_PEG_TYPES);
        this.game.load.image('reset', RESET_URI, 190, 49);
    }

    create() {
        this.game.stage.backgroundColor = 0x333333;

        this.grid = new Grid(BOARD_SIZE);

        // initialize groups for tiles and pegs
        this.boardGroup = this.game.add.group(undefined, 'board');
        this.tilesGroup = this.game.add.group(this.boardGroup, 'tiles');
        this.pegsGroup = this.game.add.group(this.boardGroup, 'pegs');
        this.deadPegsGroup = this.game.add.group(this.boardGroup, 'deadPegs');

        // populate board
        for (const { x, y } of this.grid) {
            const gamePosition = getGamePosition(x, y);
            this.addTile(gamePosition);

            if (y > 0) {
                this.addPeg(gamePosition);
                this.grid.fill({ x, y });
            }
        }

        this.grid.log();

        // center board
        this.boardGroup.x = MIDDLE;
        this.boardGroup.y = (MIDDLE - this.boardGroup.height / 2) * 1.6;

        // add reset button
        this.game.add.button(0, 0, 'reset', this.reset, this);
    }

    reset() {
        this.game.state.start('GameState', true, false);
    }

    addTile({ x, y }) {
        const tile = this.game.add.sprite(x, y, 'tiles', 0, this.tilesGroup);

        tile.anchor.x = 0.5;
        tile.anchor.y = 0.375;

        tile.inputEnabled = true;
        tile.events.onInputUp.add(this.onTileClick, this);
    }

    onTileClick(sprite) {
        const gridPos = getGridPosition(sprite);

        if (this.excited) {
            this.jumpTo(gridPos);
        }
    }

    jumpTo(endPos) {
        this.game.sound.play('jump');

        const startPos = getGridPosition(this.excited);
        const isValid = this.isValidMove(startPos, endPos);

        if (!isValid) {
            return this.disappoint();
        }

        const middle = this.getMiddle(startPos, endPos);
        this.grid.fill(endPos);
        this.grid.empty(startPos);
        this.grid.empty(middle);

        // move the sprite
        const endGamePos = getGamePosition(endPos.x, endPos.y);

        this.game.tweens.create(this.excited)
            .to(endGamePos, JUMP_DURATION, Phaser.Easing.Back.InOut)
            .start()
        ;

        // clear excited state
        this.excitedTween.loop(false);
        this.excited = null;

        // kill the jumped peg
        const middlePeg = this.getPegAt(middle);
        this.kill(middlePeg);

        this.grid.log();
    }

    kill(sprite) {
        this.game.tweens.create(sprite)
            .to({ alpha: DEATH_ALPHA }, DEATH_DURATION)
            .start()
        ;

        this.game.tweens.create(sprite.scale)
            .to(DEATH_SCALE, DEATH_DURATION)
            .start()
        ;

        this.pegsGroup.remove(sprite);
        this.deadPegsGroup.add(sprite);
    }

    isValidMove(startPos, endPos) {
        const middle = this.getMiddle(startPos, endPos);

        if (this.grid.isEmpty(middle)) {
            return false;
        }

        if (this.grid.isEmpty(endPos)) {
            return middle;
        }

        return false;
    }

    getPegAt({ x, y }) {
        const peg = this.pegsGroup.children.find(sprite => {
            const gridPos = getGridPosition(sprite);
            if (gridPos.x === x && gridPos.y === y) {
                return true;
            }
            return false;
        });
        return peg;
    }

    getMiddle(startPos, endPos) {
        const deltaX = startPos.x - endPos.x;
        const deltaY = startPos.y - endPos.y;
        return {
            x: deltaX / 2 + endPos.x,
            y: deltaY / 2 + endPos.y,
        };
    }

    addPeg({ x, y }) {
        const peg = new Peg(this.game, this.pegsGroup, x, y);
        peg.sprite.events.onInputUp.add(this.onPegClick, this);
        return peg;
    }

    onPegClick(sprite) {
        if (this.excited) {
            this.disappoint();
        }

        // figure out pegboard position of click
        const pos = getGridPosition(sprite);

        if (this.hasValidMoves(pos)) {
            this.excite(sprite);
            this.selected = sprite;
        } else {
            this.shake(sprite);
        }
    }

    excite(sprite) {
        const swing = 0.05;
        const swingDuration = 90;
        const tween = this.game.tweens.create(sprite)
            .to({ rotation: swing }, swingDuration, Phaser.Easing.Back.Out)
            .to({ rotation: 0 }, swingDuration, Phaser.Easing.Linear.None)
            .to({ rotation: -swing }, swingDuration, Phaser.Easing.Back.Out)
            .to({ rotation: 0 }, swingDuration, Phaser.Easing.Linear.None)
            .loop(true)
            .start()
        ;
        this.excited = sprite;
        this.excitedTween = tween;
    }

    disappoint() {
        this.shake(this.excited);
        this.excitedTween.loop(false);
        this.excited = null;
    }

    shake(sprite) {
        const duration = 100;
        const dist = 5;
        const start = { x: sprite.x };
        const right = { x: sprite.x + dist };
        const left = { x: sprite.x - dist };
        this.game.tweens.create(sprite)
            .to(left, duration / 4)
            .to(right, duration / 2)
            .to(start, duration / 4)
            .start()
        ;
        this.game.sound.play('error');
    }

    hasValidMoves({ x, y }) {
        const jumpDist = 2;
        const start = { x, y };
        const ends = [
            {
                x,
                y: y + jumpDist,
            },
            {
                x,
                y: y - jumpDist,
            },
            {
                x: x - jumpDist,
                y: y - jumpDist,
            },
            {
                x: x + jumpDist,
                y: y + jumpDist,
            },
            {
                x: x + jumpDist,
                y,
            },
            {
                x: x - jumpDist,
                y,
            },
        ];

        for (let i in ends) {
            const end = ends[i];
            if (this.isValidMove(start, end)) {
                return true;
            }
        }

        return false;
    }

    hasPeg(x, y) {
        if (this.isOutOfBounds(x, y)) {
            return false;
        }
    }

    getId(x, y) {
        const row = this.grid[y];

        if (row === undefined) {
            return true;
        }

        const pos = row[x];

        if (pos === undefined) {
            return true;
        }
    }

}

export default GameState;
<commit_msg>move board population into a function<commit_after>import Grid from '../utils/Grid';
import Peg from '../objects/Peg';
import {
    getGamePosition,
    getGridPosition,
} from '../utils/position';

import {
    AUDIO_ERROR_URI,
    AUDIO_JUMP_URI,
    BOARD_SIZE,
    MIDDLE,
    NUM_PEG_TYPES,
    NUM_TILE_TYPES,
    DEATH_DURATION,
    DEATH_SCALE,
    DEATH_ALPHA,
    RESET_URI,
    JUMP_DURATION,
} from '../utils/constants';

const TILE_URI = 'images/tiles.png';
const PEGS_URI = 'images/pegs.png';

class GameState extends Phaser.State {
    preload() {
        this.game.load.audio('jump', AUDIO_JUMP_URI);
        this.game.load.audio('error', AUDIO_ERROR_URI);
        this.game.load.spritesheet('tiles', TILE_URI, 65, 89, NUM_TILE_TYPES);
        this.game.load.spritesheet('pegs', PEGS_URI, 40, 66, NUM_PEG_TYPES);
        this.game.load.image('reset', RESET_URI, 190, 49);
    }

    create() {
        this.game.stage.backgroundColor = 0x333333;

        this.grid = new Grid(BOARD_SIZE);

        // initialize groups for tiles and pegs
        this.boardGroup = this.game.add.group(undefined, 'board');
        this.tilesGroup = this.game.add.group(this.boardGroup, 'tiles');
        this.pegsGroup = this.game.add.group(this.boardGroup, 'pegs');
        this.deadPegsGroup = this.game.add.group(this.boardGroup, 'deadPegs');

        // build board
        for (const { x, y } of this.grid) {
            const gamePosition = getGamePosition(x, y);
            this.addTile(gamePosition);
        }

        // center board
        this.boardGroup.x = MIDDLE;
        this.boardGroup.y = (MIDDLE - this.boardGroup.height / 2) * 1.6;

        // add reset button
        this.game.add.button(0, 0, 'reset', this.reset, this);

        // populate board
        this.populate({ x: 0, y: 0 });
    }

    populate(emptyPos) {
        // populate board
        for (const { x, y } of this.grid) {
            const gamePosition = getGamePosition(x, y);
            if (x !== emptyPos.x || y !== emptyPos.y) {
                this.addPeg(gamePosition);
                this.grid.fill({ x, y });
            }
        }
    }

    reset() {
        this.game.state.start('GameState', true, false);
    }

    addTile({ x, y }) {
        const tile = this.game.add.sprite(x, y, 'tiles', 0, this.tilesGroup);

        tile.anchor.x = 0.5;
        tile.anchor.y = 0.375;

        tile.inputEnabled = true;
        tile.events.onInputUp.add(this.onTileClick, this);
    }

    onTileClick(sprite) {
        const gridPos = getGridPosition(sprite);

        if (this.excited) {
            this.jumpTo(gridPos);
        }
    }

    jumpTo(endPos) {
        this.game.sound.play('jump');

        const startPos = getGridPosition(this.excited);
        const isValid = this.isValidMove(startPos, endPos);

        if (!isValid) {
            return this.disappoint();
        }

        const middle = this.getMiddle(startPos, endPos);
        this.grid.fill(endPos);
        this.grid.empty(startPos);
        this.grid.empty(middle);

        // move the sprite
        const endGamePos = getGamePosition(endPos.x, endPos.y);

        this.game.tweens.create(this.excited)
            .to(endGamePos, JUMP_DURATION, Phaser.Easing.Back.InOut)
            .start()
        ;

        // clear excited state
        this.excitedTween.loop(false);
        this.excited = null;

        // kill the jumped peg
        const middlePeg = this.getPegAt(middle);
        this.kill(middlePeg);

        this.grid.log();
    }

    kill(sprite) {
        this.game.tweens.create(sprite)
            .to({ alpha: DEATH_ALPHA }, DEATH_DURATION)
            .start()
        ;

        this.game.tweens.create(sprite.scale)
            .to(DEATH_SCALE, DEATH_DURATION)
            .start()
        ;

        this.pegsGroup.remove(sprite);
        this.deadPegsGroup.add(sprite);
    }

    isValidMove(startPos, endPos) {
        const middle = this.getMiddle(startPos, endPos);

        if (this.grid.isEmpty(middle)) {
            return false;
        }

        if (this.grid.isEmpty(endPos)) {
            return middle;
        }

        return false;
    }

    getPegAt({ x, y }) {
        const peg = this.pegsGroup.children.find(sprite => {
            const gridPos = getGridPosition(sprite);
            if (gridPos.x === x && gridPos.y === y) {
                return true;
            }
            return false;
        });
        return peg;
    }

    getMiddle(startPos, endPos) {
        const deltaX = startPos.x - endPos.x;
        const deltaY = startPos.y - endPos.y;
        return {
            x: deltaX / 2 + endPos.x,
            y: deltaY / 2 + endPos.y,
        };
    }

    addPeg({ x, y }) {
        const peg = new Peg(this.game, this.pegsGroup, x, y);
        peg.sprite.events.onInputUp.add(this.onPegClick, this);
        return peg;
    }

    onPegClick(sprite) {
        if (this.excited) {
            this.disappoint();
        }

        // figure out pegboard position of click
        const pos = getGridPosition(sprite);

        if (this.hasValidMoves(pos)) {
            this.excite(sprite);
            this.selected = sprite;
        } else {
            this.shake(sprite);
        }
    }

    excite(sprite) {
        const swing = 0.05;
        const swingDuration = 90;
        const tween = this.game.tweens.create(sprite)
            .to({ rotation: swing }, swingDuration, Phaser.Easing.Back.Out)
            .to({ rotation: 0 }, swingDuration, Phaser.Easing.Linear.None)
            .to({ rotation: -swing }, swingDuration, Phaser.Easing.Back.Out)
            .to({ rotation: 0 }, swingDuration, Phaser.Easing.Linear.None)
            .loop(true)
            .start()
        ;
        this.excited = sprite;
        this.excitedTween = tween;
    }

    disappoint() {
        this.shake(this.excited);
        this.excitedTween.loop(false);
        this.excited = null;
    }

    shake(sprite) {
        const duration = 100;
        const dist = 5;
        const start = { x: sprite.x };
        const right = { x: sprite.x + dist };
        const left = { x: sprite.x - dist };
        this.game.tweens.create(sprite)
            .to(left, duration / 4)
            .to(right, duration / 2)
            .to(start, duration / 4)
            .start()
        ;
        this.game.sound.play('error');
    }

    hasValidMoves({ x, y }) {
        const jumpDist = 2;
        const start = { x, y };
        const ends = [
            {
                x,
                y: y + jumpDist,
            },
            {
                x,
                y: y - jumpDist,
            },
            {
                x: x - jumpDist,
                y: y - jumpDist,
            },
            {
                x: x + jumpDist,
                y: y + jumpDist,
            },
            {
                x: x + jumpDist,
                y,
            },
            {
                x: x - jumpDist,
                y,
            },
        ];

        for (let i in ends) {
            const end = ends[i];
            if (this.isValidMove(start, end)) {
                return true;
            }
        }

        return false;
    }

    hasPeg(x, y) {
        if (this.isOutOfBounds(x, y)) {
            return false;
        }
    }

    getId(x, y) {
        const row = this.grid[y];

        if (row === undefined) {
            return true;
        }

        const pos = row[x];

        if (pos === undefined) {
            return true;
        }
    }

}

export default GameState;
<|endoftext|>