<commit_before>spacialistApp.controller('threeCtrl', ['$scope', function($scope) {
    $scope.threeContainer = 'threejs-container';
    var container;
    var camera, scene, renderer, controls, animationId;
    var sdisplay, controller1, controller2, group;
    var raycaster = new THREE.Raycaster();
    var intersected = [];
	var tempMatrix = new THREE.Matrix4();
    var fileUrl, extension;
    var width, height;
    var mouse = new THREE.Vector2();
    // var raycaster = new THREE.Raycaster();
    var particles = [];
    var measureLine;
    var ts = Date.now();

    $scope.status = {
        progress: 0,
        vr: {
            errored: false,
            message: '',
            button: {
                text: 'No VR Display',
                toggle: function() {
                    if(sdisplay) {
        				sdisplay.isPresenting ? sdisplay.exitPresent() : sdisplay.requestPresent([
                            {
                                source: renderer.domElement
                            }
                        ]);
                    }
                }
            }
        }
    };
    $scope.points = 0;
    $scope.measurementEnabled = false;
    $scope.props = {
        length: 0.0
    };

    $scope.initThree = function(url) {
        fileUrl = url;
        extension = url.substr(url.lastIndexOf('.')+1);
        init();
        animate();
    };

    $scope.toggleMeasurement = function() {
        $scope.measurementEnabled = !$scope.measurementEnabled;
        if(!$scope.measurementEnabled) {
            $scope.points = 0;
            if(measureLine) scene.remove(measureLine);
            for(var i=0; i<particles.length; i++) {
                scene.remove(particles[i]);
            }
            particles.length = 0;
        }
    };

    $scope.zoomOut = function() {
        // console.log(camera.zoom);
        camera.zoom -= 0.1;
        camera.updateProjectionMatrix();
    };

    $scope.zoomIn = function() {
        camera.zoom += 0.1;
        camera.updateProjectionMatrix();
    };

    function loadObj(path, objFile, materials) {
        var objLoader = new THREE.OBJLoader2();
        if(materials) {
            objLoader.setMaterials(materials.materials);
        }
        objLoader.setPath(path);
        objLoader.load(objFile,
            function(object) { // onSuccess
                object.castShadow = true;
				object.receiveShadow = true;
                group.add(object);
                onWindowResize();
            },
            function(event) { // onProgress
                if(event.lengthComputable) {
                    $scope.status.progress = Math.round(event.loaded / event.total * 100);
                    $scope.$apply();
                    console.log('Downloaded ' + $scope.status.progress + '% of model');
                }
            },
            function(event) { // onError
            }
        );
    }

    function init() {
        WEBVR.checkAvailability().then(function() {
            renderer.vr.enabled = true;
            renderer.vr.standing = true;
        }).catch(function(message) {
            renderer.vr.enabled = false;
            renderer.vr.standing = false;
            $scope.status.vr.errored = true;
            $scope.status.vr.message = message;
        });

        container = document.getElementById($scope.threeContainer);
        width = container.clientWidth;
        height = container.clientHeight;
        scene = new THREE.Scene();
		camera = new THREE.PerspectiveCamera(45, width/height, 0.1, 2000);
		camera.position.set(7, 5, 7);
        scene.add(camera);

		var gridHelper = new THREE.GridHelper(10, 20);
		scene.add(gridHelper);

        scene.add(new THREE.HemisphereLight(0x808080, 0x606060));
		var directionalLight = new THREE.DirectionalLight(0xffffff);
        directionalLight.position = camera.position;
        directionalLight.castShadow = true;
		directionalLight.shadow.camera.top = 2;
		directionalLight.shadow.camera.bottom = -2;
		directionalLight.shadow.camera.right = 2;
		directionalLight.shadow.camera.left = -2;
		directionalLight.shadow.mapSize.set( 4096, 4096 );
		scene.add(directionalLight);

        group = new THREE.Group();
		scene.add(group);

        if(extension == 'dae') { // collada
            var loader = new THREE.ColladaLoader();
            loader.options.convertUpAxis = true;
            loader.load(fileUrl, function(collada) {
                var object = collada.scene;
                var material, children;
                var parent = object;
                do {
                    children = parent.children;
                    if(!children || !children[0]) break;
                    material = children[0].material;
                    parent = children[0];
                } while(!material);
                if(material) {
                    material.side = THREE.DoubleSide;
                }
                object.castShadow = true;
                object.receiveShadow = true;
                group.add(object);
                onWindowResize();
            },
            function(event) { // onProgress
                if(event.lengthComputable) {
                    $scope.status.progress = Math.round(event.loaded / event.total * 100);
                    $scope.$apply();
                    console.log('Downloaded ' + $scope.status.progress + '% of model');
                }
            },
            function(event) { // onError
            });
        } else if(extension == 'obj') { // obj
            var sep = fileUrl.lastIndexOf('/')+1;
            var path = fileUrl.substr(0, sep);
            var objUrl = fileUrl.substr(sep);
            // we assume that the mtl file has the same name as the obj file
            var mtlUrl = objUrl.substr(0, objUrl.lastIndexOf('.obj')) + '.mtl';
            THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader());
            var mtlLoader = new THREE.MTLLoader();
            mtlLoader.setMaterialOptions({
                side: THREE.DoubleSide
            });
            mtlLoader.setPath(path);
            // try to load mtl file
            mtlLoader.load(mtlUrl, function(materials) {
                // load obj file with loaded materials
                materials.preload();
                loadObj(path, objUrl, materials);
            }, function() {}, function() {
                // onError: try to load obj without materials
                loadObj(path, objUrl);
            });
        }

		renderer = new THREE.WebGLRenderer({
            antialias: true
        });
		renderer.setPixelRatio(window.devicePixelRatio);
		renderer.setSize(width, height);
		renderer.shadowMap.enabled = true;
		renderer.gammaInput = true;
		renderer.gammaOutput = true;
		container.appendChild(renderer.domElement);

        if(renderer.vr.enabled) {
            // Add Vive Controller
            controller1 = new THREE.ViveController(0);
    		controller1.standingMatrix = renderer.vr.getStandingMatrix();
    		controller1.addEventListener( 'triggerdown', onTriggerDown );
    		controller1.addEventListener( 'triggerup', onTriggerUp );
    		scene.add(controller1);
    		controller2 = new THREE.ViveController(1);
    		controller2.standingMatrix = renderer.vr.getStandingMatrix();
    		controller2.addEventListener( 'triggerdown', onTriggerDown );
    		controller2.addEventListener( 'triggerup', onTriggerUp );
    		scene.add(controller2);

            // Add Models to Vive Controller
            var cloader = new THREE.OBJLoader2();
    		cloader.setPath('models/obj/vive-controller/');
    		cloader.load('vr_controller_vive_1_5.obj', function(object) {
    			var loader = new THREE.TextureLoader();
    			loader.setPath('models/obj/vive-controller/');
    			var controller = object.children[0];
    			controller.material.map = loader.load('onepointfive_texture.png');
    			controller.material.specularMap = loader.load('onepointfive_spec.png');
    			controller1.add(object.clone());
    			controller2.add(object.clone());
    		} );

            // Add Controller Rays
    		var geometry = new THREE.Geometry();
    		geometry.vertices.push(new THREE.Vector3(0, 0, 0));
    		geometry.vertices.push(new THREE.Vector3(0, 0, -1));
    		var line = new THREE.Line(geometry);
    		line.name = 'line';
    		line.scale.z = 5;
    		controller1.add(line.clone());
    		controller2.add(line.clone());

            WEBVR.getVRDisplay(function(display) {
    			renderer.vr.setDevice(display);
                sdisplay = display;

                window.addEventListener('vrdisplaypresentchange', function() {
                    $scope.status.vr.button.text = sdisplay.isPresenting ? 'Exit VR' : 'Enter VR';
                }, false );
    		});
        } else {
            controls = new THREE.OrbitControls(camera, renderer.domElement);
        }

		window.addEventListener('resize', onWindowResize, false);
        renderer.domElement.addEventListener('mousedown', onDocumentMouseDown, false);
    }

    function onWindowResize() {
        width = renderer.domElement.clientWidth;
        height = renderer.domElement.clientHeight;
		camera.aspect = width/height;
		camera.updateProjectionMatrix();
		renderer.setSize(width, height);
	}

    function onDocumentMouseDown(event) {
        if(!$scope.measurementEnabled) return;
        if(event.which != 1) return; // not left mouse button
        if($scope.points == 2) {
            $scope.points = 0;
            scene.remove(measureLine);
            measureLine = null;
            for(var i=0; i<particles.length; i++) {
                scene.remove(particles[i]);
                particles[i] = null;
            }
            particles.length = 0;
        }
		event.preventDefault();
		mouse.x = (event.offsetX/width)*2 - 1;
		mouse.y = -(event.offsetY/height)*2 + 1;
		raycaster.setFromCamera(mouse, camera);
		var intersects = raycaster.intersectObjects(group.children, true);
		if(intersects.length > 0) {
			var particle = new THREE.Sprite();
			particle.position.copy(intersects[0].point);
			particle.scale.x = particle.scale.y = 0.1;
			scene.add(particle);
            particles.push(particle);
            $scope.points++;
		}

        if($scope.points == 2) {
            var pointA = particles[0];
            var pointB = particles[1];
            var geometry = new THREE.Geometry();
            geometry.vertices.push(pointA);
            geometry.vertices.push(pointB);
            var material = new THREE.LineBasicMaterial();
            line = new THREE.Line(geometry, material);
            scene.add(line);
            measureLine = line;
            var length = pointA.getWorldPosition().distanceTo(pointB.getWorldPosition());
            $scope.props.length = length.toFixed(2);
        }
	}

	function onTriggerDown(event) {
		var controller = event.target;
		var intersections = getIntersections(controller);
		if(intersections.length > 0) {
			var intersection = intersections[0];
			tempMatrix.getInverse(controller.matrixWorld);
			var object = intersection.object;
			object.matrix.premultiply(tempMatrix);
			object.matrix.decompose(object.position, object.quaternion, object.scale);
			controller.add(object);
			controller.userData.selected = object;
		}
	}

	function onTriggerUp(event) {
		var controller = event.target;
		if(controller.userData.selected !== undefined) {
			var object = controller.userData.selected;
			object.matrix.premultiply(controller.matrixWorld);
			object.matrix.decompose(object.position, object.quaternion, object.scale);
			group.add(object);
			controller.userData.selected = undefined;
		}
	}

	function getIntersections(controller) {
		tempMatrix.identity().extractRotation(controller.matrixWorld);
		raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
		raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
		return raycaster.intersectObjects(group.children, true);
	}

	function intersectObjects(controller) {
		if(controller.userData.selected !== undefined) return;
		var line = controller.getObjectByName('line');
		var intersections = getIntersections(controller);
		if(intersections.length > 0) {
			var intersection = intersections[0];
			var object = intersection.object;
			intersected.push(object);
			line.scale.z = intersection.distance;
		} else {
			line.scale.z = 5;
		}
	}

	function cleanIntersected() {
		while(intersected.length) {
			var object = intersected.pop();
		}
	}

    function animate() {
        animationId = requestAnimationFrame(animate);
        renderer.animate(render);
    }

    function render() {
        if(renderer.vr.enabled) {
            controller1.update();
            controller2.update();
            cleanIntersected();
            intersectObjects(controller1);
    		intersectObjects(controller2);
        }
        renderer.render(scene, camera);
    }

    $scope.$on('$destroy', function() {
        cancelAnimationFrame(animationId);
        for(var i=0; i<group.length; i++) {
            scene.remove(group[i]);
            group[i] = null;
        }
        for(var i=0; i<particles.length; i++) {
            scene.remove(particles[i]);
            particles[i] = null;
        }
        group.length = 0;
        particles.length = 0;
        group = null;
        particles = null;
		window.addEventListener('resize', null, false);
        window.addEventListener('vrdisplaypresentchange', null, false);
        renderer.domElement.addEventListener('mousedown', null, false);
        var domParent = renderer.domElement.parentElement;
        domParent.removeChild(renderer.domElement);
        renderer = null;
        scene = null;
        controls = null;
        camera = null;
    });
}]);
<commit_msg>load vive stuff in availability check to avoid async-related bugs<commit_after>spacialistApp.controller('threeCtrl', ['$scope', function($scope) {
    $scope.threeContainer = 'threejs-container';
    var container;
    var camera, scene, renderer, controls, animationId;
    var sdisplay, controller1, controller2, group;
    var raycaster = new THREE.Raycaster();
    var intersected = [];
	var tempMatrix = new THREE.Matrix4();
    var fileUrl, extension;
    var width, height;
    var mouse = new THREE.Vector2();
    // var raycaster = new THREE.Raycaster();
    var particles = [];
    var measureLine;
    var ts = Date.now();

    $scope.status = {
        progress: 0,
        vr: {
            errored: false,
            message: '',
            button: {
                text: 'No VR Display',
                toggle: function() {
                    if(sdisplay) {
        				sdisplay.isPresenting ? sdisplay.exitPresent() : sdisplay.requestPresent([
                            {
                                source: renderer.domElement
                            }
                        ]);
                    }
                }
            }
        }
    };
    $scope.points = 0;
    $scope.measurementEnabled = false;
    $scope.props = {
        length: 0.0
    };

    $scope.initThree = function(url) {
        fileUrl = url;
        extension = url.substr(url.lastIndexOf('.')+1);
        init();
        animate();
    };

    $scope.toggleMeasurement = function() {
        $scope.measurementEnabled = !$scope.measurementEnabled;
        if(!$scope.measurementEnabled) {
            $scope.points = 0;
            if(measureLine) scene.remove(measureLine);
            for(var i=0; i<particles.length; i++) {
                scene.remove(particles[i]);
            }
            particles.length = 0;
        }
    };

    $scope.zoomOut = function() {
        // console.log(camera.zoom);
        camera.zoom -= 0.1;
        camera.updateProjectionMatrix();
    };

    $scope.zoomIn = function() {
        camera.zoom += 0.1;
        camera.updateProjectionMatrix();
    };

    function loadObj(path, objFile, materials) {
        var objLoader = new THREE.OBJLoader2();
        if(materials) {
            objLoader.setMaterials(materials.materials);
        }
        objLoader.setPath(path);
        objLoader.load(objFile,
            function(object) { // onSuccess
                object.castShadow = true;
				object.receiveShadow = true;
                group.add(object);
                onWindowResize();
            },
            function(event) { // onProgress
                if(event.lengthComputable) {
                    $scope.status.progress = Math.round(event.loaded / event.total * 100);
                    $scope.$apply();
                    console.log('Downloaded ' + $scope.status.progress + '% of model');
                }
            },
            function(event) { // onError
            }
        );
    }

    function init() {
        WEBVR.checkAvailability().then(function() {
            renderer.vr.enabled = true;
            renderer.vr.standing = true;
            controller1 = new THREE.ViveController(0);
    		controller1.standingMatrix = renderer.vr.getStandingMatrix();
    		controller1.addEventListener( 'triggerdown', onTriggerDown );
    		controller1.addEventListener( 'triggerup', onTriggerUp );
    		scene.add(controller1);
    		controller2 = new THREE.ViveController(1);
    		controller2.standingMatrix = renderer.vr.getStandingMatrix();
    		controller2.addEventListener( 'triggerdown', onTriggerDown );
    		controller2.addEventListener( 'triggerup', onTriggerUp );
    		scene.add(controller2);

            // Add Models to Vive Controller
            var cloader = new THREE.OBJLoader2();
    		cloader.setPath('models/obj/vive-controller/');
    		cloader.load('vr_controller_vive_1_5.obj', function(object) {
    			var loader = new THREE.TextureLoader();
    			loader.setPath('models/obj/vive-controller/');
    			var controller = object.children[0];
    			controller.material.map = loader.load('onepointfive_texture.png');
    			controller.material.specularMap = loader.load('onepointfive_spec.png');
    			controller1.add(object.clone());
    			controller2.add(object.clone());
    		} );

            // Add Controller Rays
    		var geometry = new THREE.Geometry();
    		geometry.vertices.push(new THREE.Vector3(0, 0, 0));
    		geometry.vertices.push(new THREE.Vector3(0, 0, -1));
    		var line = new THREE.Line(geometry);
    		line.name = 'line';
    		line.scale.z = 5;
    		controller1.add(line.clone());
    		controller2.add(line.clone());

            WEBVR.getVRDisplay(function(display) {
    			renderer.vr.setDevice(display);
                sdisplay = display;

                window.addEventListener('vrdisplaypresentchange', function() {
                    $scope.status.vr.button.text = sdisplay.isPresenting ? 'Exit VR' : 'Enter VR';
                }, false );
    		});
        }).catch(function(message) {
            renderer.vr.enabled = false;
            renderer.vr.standing = false;
            $scope.status.vr.errored = true;
            $scope.status.vr.message = message;
            controls = new THREE.OrbitControls(camera, renderer.domElement);
        });

        container = document.getElementById($scope.threeContainer);
        width = container.clientWidth;
        height = container.clientHeight;
        scene = new THREE.Scene();
		camera = new THREE.PerspectiveCamera(45, width/height, 0.1, 2000);
		camera.position.set(7, 5, 7);
        scene.add(camera);

		var gridHelper = new THREE.GridHelper(10, 20);
		scene.add(gridHelper);

        scene.add(new THREE.HemisphereLight(0x808080, 0x606060));
		var directionalLight = new THREE.DirectionalLight(0xffffff);
        directionalLight.position = camera.position;
        directionalLight.castShadow = true;
		directionalLight.shadow.camera.top = 2;
		directionalLight.shadow.camera.bottom = -2;
		directionalLight.shadow.camera.right = 2;
		directionalLight.shadow.camera.left = -2;
		directionalLight.shadow.mapSize.set( 4096, 4096 );
		scene.add(directionalLight);

        group = new THREE.Group();
		scene.add(group);

        if(extension == 'dae') { // collada
            var loader = new THREE.ColladaLoader();
            loader.options.convertUpAxis = true;
            loader.load(fileUrl, function(collada) {
                var object = collada.scene;
                var material, children;
                var parent = object;
                do {
                    children = parent.children;
                    if(!children || !children[0]) break;
                    material = children[0].material;
                    parent = children[0];
                } while(!material);
                if(material) {
                    material.side = THREE.DoubleSide;
                }
                object.castShadow = true;
                object.receiveShadow = true;
                group.add(object);
                onWindowResize();
            },
            function(event) { // onProgress
                if(event.lengthComputable) {
                    $scope.status.progress = Math.round(event.loaded / event.total * 100);
                    $scope.$apply();
                    console.log('Downloaded ' + $scope.status.progress + '% of model');
                }
            },
            function(event) { // onError
            });
        } else if(extension == 'obj') { // obj
            var sep = fileUrl.lastIndexOf('/')+1;
            var path = fileUrl.substr(0, sep);
            var objUrl = fileUrl.substr(sep);
            // we assume that the mtl file has the same name as the obj file
            var mtlUrl = objUrl.substr(0, objUrl.lastIndexOf('.obj')) + '.mtl';
            THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader());
            var mtlLoader = new THREE.MTLLoader();
            mtlLoader.setMaterialOptions({
                side: THREE.DoubleSide
            });
            mtlLoader.setPath(path);
            // try to load mtl file
            mtlLoader.load(mtlUrl, function(materials) {
                // load obj file with loaded materials
                materials.preload();
                loadObj(path, objUrl, materials);
            }, function() {}, function() {
                // onError: try to load obj without materials
                loadObj(path, objUrl);
            });
        }

		renderer = new THREE.WebGLRenderer({
            antialias: true
        });
		renderer.setPixelRatio(window.devicePixelRatio);
		renderer.setSize(width, height);
		renderer.shadowMap.enabled = true;
		renderer.gammaInput = true;
		renderer.gammaOutput = true;
		container.appendChild(renderer.domElement);

		window.addEventListener('resize', onWindowResize, false);
        renderer.domElement.addEventListener('mousedown', onDocumentMouseDown, false);
    }

    function onWindowResize() {
        width = renderer.domElement.clientWidth;
        height = renderer.domElement.clientHeight;
		camera.aspect = width/height;
		camera.updateProjectionMatrix();
		renderer.setSize(width, height);
	}

    function onDocumentMouseDown(event) {
        if(!$scope.measurementEnabled) return;
        if(event.which != 1) return; // not left mouse button
        if($scope.points == 2) {
            $scope.points = 0;
            scene.remove(measureLine);
            measureLine = null;
            for(var i=0; i<particles.length; i++) {
                scene.remove(particles[i]);
                particles[i] = null;
            }
            particles.length = 0;
        }
		event.preventDefault();
		mouse.x = (event.offsetX/width)*2 - 1;
		mouse.y = -(event.offsetY/height)*2 + 1;
		raycaster.setFromCamera(mouse, camera);
		var intersects = raycaster.intersectObjects(group.children, true);
		if(intersects.length > 0) {
			var particle = new THREE.Sprite();
			particle.position.copy(intersects[0].point);
			particle.scale.x = particle.scale.y = 0.1;
			scene.add(particle);
            particles.push(particle);
            $scope.points++;
		}

        if($scope.points == 2) {
            var pointA = particles[0];
            var pointB = particles[1];
            var geometry = new THREE.Geometry();
            geometry.vertices.push(pointA);
            geometry.vertices.push(pointB);
            var material = new THREE.LineBasicMaterial();
            line = new THREE.Line(geometry, material);
            scene.add(line);
            measureLine = line;
            var length = pointA.getWorldPosition().distanceTo(pointB.getWorldPosition());
            $scope.props.length = length.toFixed(2);
        }
	}

	function onTriggerDown(event) {
		var controller = event.target;
		var intersections = getIntersections(controller);
		if(intersections.length > 0) {
			var intersection = intersections[0];
			tempMatrix.getInverse(controller.matrixWorld);
			var object = intersection.object;
			object.matrix.premultiply(tempMatrix);
			object.matrix.decompose(object.position, object.quaternion, object.scale);
			controller.add(object);
			controller.userData.selected = object;
		}
	}

	function onTriggerUp(event) {
		var controller = event.target;
		if(controller.userData.selected !== undefined) {
			var object = controller.userData.selected;
			object.matrix.premultiply(controller.matrixWorld);
			object.matrix.decompose(object.position, object.quaternion, object.scale);
			group.add(object);
			controller.userData.selected = undefined;
		}
	}

	function getIntersections(controller) {
		tempMatrix.identity().extractRotation(controller.matrixWorld);
		raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
		raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
		return raycaster.intersectObjects(group.children, true);
	}

	function intersectObjects(controller) {
		if(controller.userData.selected !== undefined) return;
		var line = controller.getObjectByName('line');
		var intersections = getIntersections(controller);
		if(intersections.length > 0) {
			var intersection = intersections[0];
			var object = intersection.object;
			intersected.push(object);
			line.scale.z = intersection.distance;
		} else {
			line.scale.z = 5;
		}
	}

	function cleanIntersected() {
		while(intersected.length) {
			var object = intersected.pop();
		}
	}

    function animate() {
        animationId = requestAnimationFrame(animate);
        renderer.animate(render);
    }

    function render() {
        if(renderer.vr.enabled) {
            controller1.update();
            controller2.update();
            cleanIntersected();
            intersectObjects(controller1);
    		intersectObjects(controller2);
        }
        renderer.render(scene, camera);
    }

    $scope.$on('$destroy', function() {
        cancelAnimationFrame(animationId);
        for(var i=0; i<group.length; i++) {
            scene.remove(group[i]);
            group[i] = null;
        }
        for(var i=0; i<particles.length; i++) {
            scene.remove(particles[i]);
            particles[i] = null;
        }
        group.length = 0;
        particles.length = 0;
        group = null;
        particles = null;
		window.addEventListener('resize', null, false);
        window.addEventListener('vrdisplaypresentchange', null, false);
        renderer.domElement.addEventListener('mousedown', null, false);
        var domParent = renderer.domElement.parentElement;
        domParent.removeChild(renderer.domElement);
        renderer = null;
        scene = null;
        controls = null;
        camera = null;
    });
}]);
<|endoftext|>


<commit_before>var five = require('johnny-five');

var board = new five.Board({ port: '/dev/ttyAMA0'});
board.on('ready', function () {
  console.log('Board is ready');
});

var controllers = {};

var addController = function (path) {
  // 1. Require the controller
  var controller = require(path)(five);
  console.log('new controller added');
  console.log(controller);

  // 2. Add the controller to controller list
  controllers[controller.id] = controller;
};

var execute = function(controllerID, functionName) {
  controllers[controllerID][functionName]();
};

// var listControllers = function () {

// };

module.exports = {
  addController: addController,
  execute: execute,
  board: board,
};<commit_msg>refactor all the way<commit_after>var five = require('johnny-five');

var board = new five.Board({ port: '/dev/ttyAMA0'});
board.on('ready', function () {
  console.log('Board is ready');
});

var controllers = {};

var addController = function (path) {
  // 1. Require the controller
  var controller = require(path)(five);
  console.log('new controller added');
  console.log(controller);

  // 2. Add the controller to controller list
  controllers[controller.id] = controller;
};

var execute = function(controllerID, functionName) {
  console.log(controllerID, functionName);

  controllers[controllerID][functionName]();
};

// var listControllers = function () {

// };

module.exports = {
  addController: addController,
  execute: execute,
  board: board,
};<|endoftext|>


<commit_before>// database migrations
// http://stackoverflow.com/questions/10365496/meteor-how-to-perform-database-migrations
Migrations = new Meteor.Collection('migrations');

Meteor.startup(function () {
  allMigrations = Object.keys(migrationsList);
  _.each(allMigrations, function(migrationName){
    runMigration(migrationName);
  });
});

// wrapper function for all migrations
var runMigration = function (migrationName) {
  // migration updatePostStatus: make sure posts have a status
  if (!Migrations.findOne({name: migrationName})) {
    console.log("//----------------------------------------------------------------------//");
    console.log("//------------//    Starting "+migrationName+" Migration    //-----------//");
    console.log("//----------------------------------------------------------------------//");
    Migrations.insert({name: migrationName, startedAt: new Date(), finished: false});

    // execute migration function
    var itemsAffected = migrationsList[migrationName]();

    Migrations.update({name: migrationName}, {$set: {finishedAt: new Date(), finished: true, itemsAffected: itemsAffected}});
    console.log("//----------------------------------------------------------------------//");
    console.log("//------------//     Ending "+migrationName+" Migration     //-----------//");
    console.log("//----------------------------------------------------------------------//");
  }
}

var migrationsList = {
  updatePostStatus: function () {
    var i = 0;
    Posts.find({status: {$exists : false}}).forEach(function (post) {
      i++;
      Posts.update(post._id, {$set: {status: 2}});
      console.log("---------------------");
      console.log("Post: "+post.title);
      console.log("Updating status to approved");
    });
    return i;
  },
  updateCategories: function () {
    var i = 0;
    Categories.find({slug: {$exists : false}}).forEach(function (category) {
        i++;
        var slug = slugify(category.name);
        Categories.update(category._id, {$set: {slug: slug}});
        console.log("---------------------");
        console.log("Category: "+category.name);
        console.log("Updating category with new slug: "+slug);

    });
    return i;
  },
  updatePostCategories: function () {
    var i = 0;
    Posts.find().forEach(function (post) {
      i++;
      var oldCategories = post.categories;
      var newCategories = [];
      var category = {};
      var updating = false; // by default, assume we're not going to do anything

      // iterate over the post.categories array
      // if the post has no categories then nothing will happen
      _.each(oldCategories, function(value, key, list){
        // make sure the categories are strings
        if((typeof value === "string") && (category = Categories.findOne({name: value}))){
          // if value is a string, then look for the matching category object
          // and if it exists push it to the newCategories array
          updating = true; // we're updating at least one category for this post
          newCategories.push(category);
        }else{
          // if category A) is already an object, or B) it's a string but a matching category object doesn't exist
          // just keep the current value
          newCategories.push(value);
        }
      });

      if(updating){
        // update categories property on post
        Posts.update(post._id, {$set: {categories: newCategories}});
      }

      // START CONSOLE LOGS
      console.log("---------------------");
      console.log("Post: "+post.title);
      if(updating){
        console.log(oldCategories.length+" categories: "+oldCategories);
        console.log("Updating categories array to: ");
        console.log(newCategories);
      }else{
        console.log("No updates");
      }
      // END CONSOLE LOGS
    });
    return i;
  },
  updateUserProfiles: function () {
    var i = 0;
    var allUsers = Meteor.users.find();
    console.log('> Found '+allUsers.count()+' users.\n');

    allUsers.forEach(function(user){
      i++;
      console.log('> Updating user '+user._id+' ('+user.username+')');

      // update user slug
      if(getUserName(user))
        Meteor.users.update(user._id, {$set:{slug: slugify(getUserName(user))}});

      // update user isAdmin flag
      if(typeof user.isAdmin === 'undefined')
        Meteor.users.update(user._id, {$set: {isAdmin: false}});

      // update postCount
      var postsByUser = Posts.find({userId: user._id});
      Meteor.users.update(user._id, {$set: {postCount: postsByUser.count()}});

      // update commentCount
      var commentsByUser = Comments.find({userId: user._id});
      Meteor.users.update(user._id, {$set: {commentCount: commentsByUser.count()}});

    });
    return i;
  },
  resetUpvotesDownvotes: function () {
    var i = 0;
    Posts.find().forEach(function (post) {
      i++;
      var upvotes = 0,
          downvotes = 0;
      console.log("Post: "+post.title);
      if(post.upvoters){
        upvotes = post.upvoters.length;
        console.log("Found "+upvotes+" upvotes.")
      }
      if(post.downvoters){
        downvotes = post.downvoters.length;
        console.log("Found "+downvotes+" downvotes.")
      }
      Posts.update(post._id, {$set: {upvotes: upvotes, downvotes: downvotes}});
      console.log("---------------------");
    });
    return i;
  },
  resetCommentsUpvotesDownvotes: function () {
    var i = 0;
    Comments.find().forEach(function (comment) {
      i++;
      var upvotes = 0,
          downvotes = 0;
      console.log("Comment: "+comment._id);
      if(comment.upvoters){
        upvotes = comment.upvoters.length;
        console.log("Found "+upvotes+" upvotes.")
      }
      if(comment.downvoters){
        downvotes = comment.downvoters.length;
        console.log("Found "+downvotes+" downvotes.")
      }
      Comments.update(comment._id, {$set: {upvotes: upvotes, downvotes: downvotes}});
      console.log("---------------------");
    });
    return i;
  },
  headlineToTitle: function () {
    var i = 0;
    Posts.find({title: {$exists : false}}).forEach(function (post) {
      i++;
      console.log("Post: "+post.headline+" "+post.title);
      Posts.update(post._id, { $rename: { 'headline': 'title'}}, {multi: true, validate: false});
      console.log("---------------------");
    });
    return i;
  },
  commentsSubmittedToCreatedAt: function () {
    var i = 0;
    Comments.find().forEach(function (comment) {
      i++;
      console.log("Comment: "+comment._id);
      Comments.update(comment._id, { $rename: { 'submitted': 'createdAt'}}, {multi: true, validate: false});
      console.log("---------------------");
    });
    return i;
  },
  commentsPostToPostId: function () {
    var i = 0;
    Comments.find({postId: {$exists : false}}).forEach(function (comment) {
      i++;
      console.log("Comment: "+comment._id);
      Comments.update(comment._id, { $rename: { 'post': 'postId'}}, {multi: true, validate: false});
      console.log("---------------------");
    });
    return i;
  },
  createdAtSubmittedToDate: function () {
    var i = 0;
    Posts.find().forEach(function (post) {
      if(typeof post.submitted == "number" || typeof post.createdAt == "number"){
        i++;
        console.log("Posts: "+post.title);
        var createdAt = new Date(post.createdAt);
        var submitted = new Date(post.submitted);
        console.log(createdAt)
        Posts.update(post._id, { $set: { 'createdAt': createdAt, submitted: submitted}}, {multi: true, validate: false});
        console.log("---------------------");
      }
    });
    return i;
  },
  commentsCreatedAtToDate: function () {
    var i = 0;
    Comments.find().forEach(function (comment) {
      if(typeof comment.createdAt == "number"){
        i++;
        console.log("Comment: "+comment._id);
        var createdAt = new Date(comment.createdAt);
        console.log(createdAt)
        Comments.update(comment._id, { $set: { 'createdAt': createdAt}}, {multi: true, validate: false});
        console.log("---------------------");
      }
    });
    return i;
  }
}<commit_msg>migrations tweaks<commit_after>// database migrations
// http://stackoverflow.com/questions/10365496/meteor-how-to-perform-database-migrations
Migrations = new Meteor.Collection('migrations');

Meteor.startup(function () {
  allMigrations = Object.keys(migrationsList);
  _.each(allMigrations, function(migrationName){
    runMigration(migrationName);
  });
});

// wrapper function for all migrations
var runMigration = function (migrationName) {
  // migration updatePostStatus: make sure posts have a status
  if (!Migrations.findOne({name: migrationName})) {
    console.log("//----------------------------------------------------------------------//");
    console.log("//------------//    Starting "+migrationName+" Migration    //-----------//");
    console.log("//----------------------------------------------------------------------//");
    Migrations.insert({name: migrationName, startedAt: new Date(), completed: false});

    // execute migration function
    var itemsAffected = migrationsList[migrationName]() || 0;

    Migrations.update({name: migrationName}, {$set: {finishedAt: new Date(), completed: true, itemsAffected: itemsAffected}});
    console.log("//----------------------------------------------------------------------//");
    console.log("//------------//     Ending "+migrationName+" Migration     //-----------//");
    console.log("//----------------------------------------------------------------------//");
  }
}

var migrationsList = {
  updatePostStatus: function () {
    var i = 0;
    Posts.find({status: {$exists : false}}).forEach(function (post) {
      i++;
      Posts.update(post._id, {$set: {status: 2}});
      console.log("---------------------");
      console.log("Post: "+post.title);
      console.log("Updating status to approved");
    });
    return i;
  },
  updateCategories: function () {
    var i = 0;
    Categories.find({slug: {$exists : false}}).forEach(function (category) {
        i++;
        var slug = slugify(category.name);
        Categories.update(category._id, {$set: {slug: slug}});
        console.log("---------------------");
        console.log("Category: "+category.name);
        console.log("Updating category with new slug: "+slug);

    });
    return i;
  },
  updatePostCategories: function () {
    var i = 0;
    Posts.find().forEach(function (post) {
      i++;
      var oldCategories = post.categories;
      var newCategories = [];
      var category = {};
      var updating = false; // by default, assume we're not going to do anything

      // iterate over the post.categories array
      // if the post has no categories then nothing will happen
      _.each(oldCategories, function(value, key, list){
        // make sure the categories are strings
        if((typeof value === "string") && (category = Categories.findOne({name: value}))){
          // if value is a string, then look for the matching category object
          // and if it exists push it to the newCategories array
          updating = true; // we're updating at least one category for this post
          newCategories.push(category);
        }else{
          // if category A) is already an object, or B) it's a string but a matching category object doesn't exist
          // just keep the current value
          newCategories.push(value);
        }
      });

      if(updating){
        // update categories property on post
        Posts.update(post._id, {$set: {categories: newCategories}});
      }

      // START CONSOLE LOGS
      console.log("---------------------");
      console.log("Post: "+post.title);
      if(updating){
        console.log(oldCategories.length+" categories: "+oldCategories);
        console.log("Updating categories array to: ");
        console.log(newCategories);
      }else{
        console.log("No updates");
      }
      // END CONSOLE LOGS
    });
    return i;
  },
  updateUserProfiles: function () {
    var i = 0;
    var allUsers = Meteor.users.find();
    console.log('> Found '+allUsers.count()+' users.\n');

    allUsers.forEach(function(user){
      i++;
      console.log('> Updating user '+user._id+' ('+user.username+')');

      // update user slug
      if(getUserName(user))
        Meteor.users.update(user._id, {$set:{slug: slugify(getUserName(user))}});

      // update user isAdmin flag
      if(typeof user.isAdmin === 'undefined')
        Meteor.users.update(user._id, {$set: {isAdmin: false}});

      // update postCount
      var postsByUser = Posts.find({userId: user._id});
      Meteor.users.update(user._id, {$set: {postCount: postsByUser.count()}});

      // update commentCount
      var commentsByUser = Comments.find({userId: user._id});
      Meteor.users.update(user._id, {$set: {commentCount: commentsByUser.count()}});

    });
    return i;
  },
  resetUpvotesDownvotes: function () {
    var i = 0;
    Posts.find().forEach(function (post) {
      i++;
      var upvotes = 0,
          downvotes = 0;
      console.log("Post: "+post.title);
      if(post.upvoters){
        upvotes = post.upvoters.length;
        console.log("Found "+upvotes+" upvotes.")
      }
      if(post.downvoters){
        downvotes = post.downvoters.length;
        console.log("Found "+downvotes+" downvotes.")
      }
      Posts.update(post._id, {$set: {upvotes: upvotes, downvotes: downvotes}});
      console.log("---------------------");
    });
    return i;
  },
  resetCommentsUpvotesDownvotes: function () {
    var i = 0;
    Comments.find().forEach(function (comment) {
      i++;
      var upvotes = 0,
          downvotes = 0;
      console.log("Comment: "+comment._id);
      if(comment.upvoters){
        upvotes = comment.upvoters.length;
        console.log("Found "+upvotes+" upvotes.")
      }
      if(comment.downvoters){
        downvotes = comment.downvoters.length;
        console.log("Found "+downvotes+" downvotes.")
      }
      Comments.update(comment._id, {$set: {upvotes: upvotes, downvotes: downvotes}});
      console.log("---------------------");
    });
    return i;
  },
  headlineToTitle: function () {
    var i = 0;
    Posts.find({title: {$exists : false}}).forEach(function (post) {
      i++;
      console.log("Post: "+post.headline+" "+post.title);
      Posts.update(post._id, { $rename: { 'headline': 'title'}}, {multi: true, validate: false});
      console.log("---------------------");
    });
    return i;
  },
  commentsSubmittedToCreatedAt: function () {
    var i = 0;
    Comments.find().forEach(function (comment) {
      i++;
      console.log("Comment: "+comment._id);
      Comments.update(comment._id, { $rename: { 'submitted': 'createdAt'}}, {multi: true, validate: false});
      console.log("---------------------");
    });
    return i;
  },
  commentsPostToPostId: function () {
    var i = 0;
    Comments.find({postId: {$exists : false}}).forEach(function (comment) {
      i++;
      console.log("Comment: "+comment._id);
      Comments.update(comment._id, { $rename: { 'post': 'postId'}}, {multi: true, validate: false});
      console.log("---------------------");
    });
    return i;
  },
  createdAtSubmittedToDate: function () {
    var i = 0;
    Posts.find().forEach(function (post) {
      if(typeof post.submitted == "number" || typeof post.createdAt == "number"){
        i++;
        console.log("Posts: "+post.title);
        var createdAt = new Date(post.createdAt);
        var submitted = new Date(post.submitted);
        console.log(createdAt)
        Posts.update(post._id, { $set: { 'createdAt': createdAt, submitted: submitted}}, {multi: true, validate: false});
        console.log("---------------------");
      }
    });
    return i;
  },
  commentsCreatedAtToDate: function () {
    var i = 0;
    Comments.find().forEach(function (comment) {
      if(typeof comment.createdAt == "number"){
        i++;
        console.log("Comment: "+comment._id);
        var createdAt = new Date(comment.createdAt);
        console.log(createdAt)
        Comments.update(comment._id, { $set: { 'createdAt': createdAt}}, {multi: true, validate: false});
        console.log("---------------------");
      }
    });
    return i;
  }
}<|endoftext|>


<commit_before>/* @flow */

import {BLOCK_TYPE, ENTITY_TYPE, INLINE_STYLE} from './Constants';
import getEntityRanges from './getEntityRanges';
import {Entity} from 'draft-js';

import type {ContentState, ContentBlock} from 'draft-js';

const {
  BOLD,
  CODE,
  ITALIC,
  STRIKETHROUGH,
  UNDERLINE,
} = INLINE_STYLE;

const CODE_INDENT = '    ';

class MarkupGenerator {
  blocks: Array<ContentBlock>;
  contentState: ContentState;
  currentBlock: number;
  output: Array<string>;
  totalBlocks: number;
  listItemCounts: Object;

  constructor(contentState: ContentState) {
    this.contentState = contentState;
  }

  generate(): string {
    this.output = [];
    this.blocks = this.contentState.getBlockMap().toArray();
    this.totalBlocks = this.blocks.length;
    this.currentBlock = 0;
    this.listItemCounts = {};
    while (this.currentBlock < this.totalBlocks) {
      this.processBlock();
    }
    return this.output.join('');
  }

  processBlock() {
    let block = this.blocks[this.currentBlock];
    let blockType = block.getType();
    switch (blockType) {
      case BLOCK_TYPE.HEADER_ONE: {
        this.insertLineBreaks(1);
        this.output.push('# ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.HEADER_TWO: {
        this.insertLineBreaks(1);
        this.output.push('## ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.HEADER_THREE: {
        this.insertLineBreaks(1);
        this.output.push('### ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.UNORDERED_LIST_ITEM: {
        let blockDepth = block.getDepth();
        let lastBlock = this.getLastBlock();
        let lastBlockType = lastBlock ? lastBlock.getType() : null;
        let lastBlockDepth = lastBlock && canHaveDepth(lastBlockType) ?
          lastBlock.getDepth() :
          null;
        if (
          lastBlockType !== blockType &&
          lastBlockDepth !== blockDepth - 1
        ) {
          this.insertLineBreaks(1);
          // Insert an additional line break if following opposite list type.
          if (lastBlockType === BLOCK_TYPE.ORDERED_LIST_ITEM) {
            this.insertLineBreaks(1);
          }
        }
        let indent = ' '.repeat(block.depth * 2);
        this.output.push(
          indent + '- ' + this.renderBlockContent(block) + '\n'
        );
        break;
      }
      case BLOCK_TYPE.ORDERED_LIST_ITEM: {
        let blockDepth = block.getDepth();
        let lastBlock = this.getLastBlock();
        let lastBlockType = lastBlock ? lastBlock.getType() : null;
        let lastBlockDepth = lastBlock && canHaveDepth(lastBlockType) ?
          lastBlock.getDepth() :
          null;
        if (lastBlockType !== blockType && lastBlockDepth !== blockDepth - 1) {
          this.insertLineBreaks(1);
          // Insert an additional line break if following opposite list type.
          if (lastBlockType === BLOCK_TYPE.UNORDERED_LIST_ITEM) {
            this.insertLineBreaks(1);
          }
        }
        let indent = ' '.repeat(blockDepth * 2);
        // TODO: figure out what to do with two-digit numbers
        let count = this.getListItemCount(block) % 10;
        this.output.push(
          indent + `${count}. ` + this.renderBlockContent(block) + '\n'
        );
        break;
      }
      case BLOCK_TYPE.BLOCKQUOTE: {
        this.insertLineBreaks(1);
        this.output.push(' > ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.CODE: {
        this.insertLineBreaks(1);
        this.output.push(CODE_INDENT + this.renderBlockContent(block) + '\n');
        break;
      }
      default: {
        this.insertLineBreaks(1);
        this.output.push(this.renderBlockContent(block) + '\n');
        break;
      }
    }
    this.currentBlock += 1;
  }

  getLastBlock(): ContentBlock {
    return this.blocks[this.currentBlock - 1];
  }

  getNextBlock(): ContentBlock {
    return this.blocks[this.currentBlock + 1];
  }

  getListItemCount(block: ContentBlock): number {
    let blockType = block.getType();
    let blockDepth = block.getDepth();
    // To decide if we need to start over we need to backtrack (skipping list
    // items that are of greater depth)
    let index = this.currentBlock - 1;
    let prevBlock = this.blocks[index];
    while (
      prevBlock &&
      canHaveDepth(prevBlock.getType()) &&
      prevBlock.getDepth() > blockDepth
    ) {
      index -= 1;
      prevBlock = this.blocks[index];
    }
    if (
      !prevBlock ||
      prevBlock.getType() !== blockType ||
      prevBlock.getDepth() !== blockDepth
    ) {
      this.listItemCounts[blockDepth] = 0;
    }
    return (
      this.listItemCounts[blockDepth] = this.listItemCounts[blockDepth] + 1
    );
  }

  insertLineBreaks() {
    if (this.currentBlock > 0) {
      this.output.push('\n');
    }
  }

  renderBlockContent(block: ContentBlock): string {
    let text = block.getText();
    if (text === '') {
      // Prevent element collapse if completely empty.
      // TODO: Replace with constant.
      return '\u200B';
    }
    let charMetaList = block.getCharacterList();
    let entityPieces = getEntityRanges(text, charMetaList);
    return entityPieces.map(([entityKey, stylePieces]) => {
      let content = stylePieces.map(([text, style]) => {
        // Don't allow empty inline elements.
        if (!text) {
          return '';
        }
        let content = encodeContent(text);
        if (style.has(BOLD)) {
          content = `**${content}**`;
        }
        if (style.has(UNDERLINE)) {
          // TODO: encode `+`?
          content = `++${content}++`;
        }
        if (style.has(ITALIC)) {
          content = `_${content}_`;
        }
        if (style.has(STRIKETHROUGH)) {
          // TODO: encode `~`?
          content = `~~${content}~~`;
        }
        if (style.has(CODE)) {
          content = '`' + content + '`';
        }
        return content;
      }).join('');
      let entity = entityKey ? Entity.get(entityKey) : null;
      if (entity != null && entity.getType() === ENTITY_TYPE.LINK) {
        let url = entity.getData().url || '';
        return `[${content}](${encodeURL(url)})`;
      } else {
        return content;
      }
    }).join('');
  }
}

function canHaveDepth(blockType: any): boolean {
  switch (blockType) {
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return true;
    default:
      return false;
  }
}

function encodeContent(text) {
  return text.replace(/[*_`]/g, '\\$&');
}

// Encode chars that would normally be allowed in a URL but would conflict with
// our markdown syntax: `[foo](http://foo/)`
function encodeURL(url) {
  return url.replace(/\)/g, '%29');
}

export default function stateToMarkdown(content: ContentState): string {
  return new MarkupGenerator(content).generate();
}
<commit_msg>don't render code inside code block (markdown)<commit_after>/* @flow */

import {BLOCK_TYPE, ENTITY_TYPE, INLINE_STYLE} from './Constants';
import getEntityRanges from './getEntityRanges';
import {Entity} from 'draft-js';

import type {ContentState, ContentBlock} from 'draft-js';

const {
  BOLD,
  CODE,
  ITALIC,
  STRIKETHROUGH,
  UNDERLINE,
} = INLINE_STYLE;

const CODE_INDENT = '    ';

class MarkupGenerator {
  blocks: Array<ContentBlock>;
  contentState: ContentState;
  currentBlock: number;
  output: Array<string>;
  totalBlocks: number;
  listItemCounts: Object;

  constructor(contentState: ContentState) {
    this.contentState = contentState;
  }

  generate(): string {
    this.output = [];
    this.blocks = this.contentState.getBlockMap().toArray();
    this.totalBlocks = this.blocks.length;
    this.currentBlock = 0;
    this.listItemCounts = {};
    while (this.currentBlock < this.totalBlocks) {
      this.processBlock();
    }
    return this.output.join('');
  }

  processBlock() {
    let block = this.blocks[this.currentBlock];
    let blockType = block.getType();
    switch (blockType) {
      case BLOCK_TYPE.HEADER_ONE: {
        this.insertLineBreaks(1);
        this.output.push('# ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.HEADER_TWO: {
        this.insertLineBreaks(1);
        this.output.push('## ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.HEADER_THREE: {
        this.insertLineBreaks(1);
        this.output.push('### ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.UNORDERED_LIST_ITEM: {
        let blockDepth = block.getDepth();
        let lastBlock = this.getLastBlock();
        let lastBlockType = lastBlock ? lastBlock.getType() : null;
        let lastBlockDepth = lastBlock && canHaveDepth(lastBlockType) ?
          lastBlock.getDepth() :
          null;
        if (
          lastBlockType !== blockType &&
          lastBlockDepth !== blockDepth - 1
        ) {
          this.insertLineBreaks(1);
          // Insert an additional line break if following opposite list type.
          if (lastBlockType === BLOCK_TYPE.ORDERED_LIST_ITEM) {
            this.insertLineBreaks(1);
          }
        }
        let indent = ' '.repeat(block.depth * 2);
        this.output.push(
          indent + '- ' + this.renderBlockContent(block) + '\n'
        );
        break;
      }
      case BLOCK_TYPE.ORDERED_LIST_ITEM: {
        let blockDepth = block.getDepth();
        let lastBlock = this.getLastBlock();
        let lastBlockType = lastBlock ? lastBlock.getType() : null;
        let lastBlockDepth = lastBlock && canHaveDepth(lastBlockType) ?
          lastBlock.getDepth() :
          null;
        if (lastBlockType !== blockType && lastBlockDepth !== blockDepth - 1) {
          this.insertLineBreaks(1);
          // Insert an additional line break if following opposite list type.
          if (lastBlockType === BLOCK_TYPE.UNORDERED_LIST_ITEM) {
            this.insertLineBreaks(1);
          }
        }
        let indent = ' '.repeat(blockDepth * 2);
        // TODO: figure out what to do with two-digit numbers
        let count = this.getListItemCount(block) % 10;
        this.output.push(
          indent + `${count}. ` + this.renderBlockContent(block) + '\n'
        );
        break;
      }
      case BLOCK_TYPE.BLOCKQUOTE: {
        this.insertLineBreaks(1);
        this.output.push(' > ' + this.renderBlockContent(block) + '\n');
        break;
      }
      case BLOCK_TYPE.CODE: {
        this.insertLineBreaks(1);
        this.output.push(CODE_INDENT + this.renderBlockContent(block) + '\n');
        break;
      }
      default: {
        this.insertLineBreaks(1);
        this.output.push(this.renderBlockContent(block) + '\n');
        break;
      }
    }
    this.currentBlock += 1;
  }

  getLastBlock(): ContentBlock {
    return this.blocks[this.currentBlock - 1];
  }

  getNextBlock(): ContentBlock {
    return this.blocks[this.currentBlock + 1];
  }

  getListItemCount(block: ContentBlock): number {
    let blockType = block.getType();
    let blockDepth = block.getDepth();
    // To decide if we need to start over we need to backtrack (skipping list
    // items that are of greater depth)
    let index = this.currentBlock - 1;
    let prevBlock = this.blocks[index];
    while (
      prevBlock &&
      canHaveDepth(prevBlock.getType()) &&
      prevBlock.getDepth() > blockDepth
    ) {
      index -= 1;
      prevBlock = this.blocks[index];
    }
    if (
      !prevBlock ||
      prevBlock.getType() !== blockType ||
      prevBlock.getDepth() !== blockDepth
    ) {
      this.listItemCounts[blockDepth] = 0;
    }
    return (
      this.listItemCounts[blockDepth] = this.listItemCounts[blockDepth] + 1
    );
  }

  insertLineBreaks() {
    if (this.currentBlock > 0) {
      this.output.push('\n');
    }
  }

  renderBlockContent(block: ContentBlock): string {
    let blockType = block.getType();
    let text = block.getText();
    if (text === '') {
      // Prevent element collapse if completely empty.
      // TODO: Replace with constant.
      return '\u200B';
    }
    let charMetaList = block.getCharacterList();
    let entityPieces = getEntityRanges(text, charMetaList);
    return entityPieces.map(([entityKey, stylePieces]) => {
      let content = stylePieces.map(([text, style]) => {
        // Don't allow empty inline elements.
        if (!text) {
          return '';
        }
        let content = encodeContent(text);
        if (style.has(BOLD)) {
          content = `**${content}**`;
        }
        if (style.has(UNDERLINE)) {
          // TODO: encode `+`?
          content = `++${content}++`;
        }
        if (style.has(ITALIC)) {
          content = `_${content}_`;
        }
        if (style.has(STRIKETHROUGH)) {
          // TODO: encode `~`?
          content = `~~${content}~~`;
        }
        if (style.has(CODE)) {
          content = (blockType === BLOCK_TYPE.CODE) ? content : '`' + content + '`';
        }
        return content;
      }).join('');
      let entity = entityKey ? Entity.get(entityKey) : null;
      if (entity != null && entity.getType() === ENTITY_TYPE.LINK) {
        let url = entity.getData().url || '';
        return `[${content}](${encodeURL(url)})`;
      } else {
        return content;
      }
    }).join('');
  }
}

function canHaveDepth(blockType: any): boolean {
  switch (blockType) {
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return true;
    default:
      return false;
  }
}

function encodeContent(text) {
  return text.replace(/[*_`]/g, '\\$&');
}

// Encode chars that would normally be allowed in a URL but would conflict with
// our markdown syntax: `[foo](http://foo/)`
function encodeURL(url) {
  return url.replace(/\)/g, '%29');
}

export default function stateToMarkdown(content: ContentState): string {
  return new MarkupGenerator(content).generate();
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    async = require('async');

/**
 * Web Schema
 */
var WebSchema = new Schema({
    title: { type: String, default: '' },
    detail: { type: String, required: true },
    link: { type: String, default: '' },
    source: { type: String, default: '' },
    keyword: { type: Array, default: [] },	// 1:male, 2:female
    createTime: { type: Date, required: true } //생성 시간
}, {collection: 'web'});

WebSchema.index({ keyword: 1, createTime: -1 });

/**
 * Model Methods
 */

module.exports = mongoose.model('User', UserSchema);<commit_msg>Update Web<commit_after>var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    async = require('async');

/**
 * Web Schema
 */
var WebSchema = new Schema({
    title: { type: String, default: '' },
    detail: { type: String, required: true },
    link: { type: String, default: '' },
    source: { type: String, default: '' },
    keyword: { type: Array, default: [] },	// 1:male, 2:female
    createTime: { type: Date, required: true } //생성 시간
}, {collection: 'web'});

WebSchema.index({ keyword: 1, createTime: -1 });

/**
 * Model Methods
 */

module.exports = mongoose.model('Web', WebSchema);<|endoftext|>


<commit_before>var self = module.exports = {

	init: function(app, mongoose) {

		var UserSchema = new mongoose.Schema({
			name: String,
			email: String
		});

		var User = mongoose.model('User', UserSchema);

	}

};<commit_msg>Chat log model<commit_after>var self = module.exports = {

	init: function(app, mongoose) {

		var UserSchema = new mongoose.Schema({
			name: String,
			email: String
		});

		var ChatSchema = new mongoose.Schema({
			gameId: String,
			user: String,
			message: String
		});

		var User = mongoose.model('User', UserSchema);
		var Chat = mongoose.model('Chat', ChatSchema);

	}

};<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

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

import {getCSSAnimation, AnimationPropType} from '../utils/animation-utils';

import {
  CONTINUOUS_COLOR_RANGE,
  DEFAULT_COLOR,
  OPACITY_RANGE} from '../theme';

function _c(className) {
  const prefix = 'rv-treemap';
  if (!className) {
    return prefix;
  }
  return `${prefix}__${className}`;
}

const DEFAULT_SCALES = {
  color: {
    range: CONTINUOUS_COLOR_RANGE
  },
  opacity: {
    range: OPACITY_RANGE
  }
};

class Treemap extends React.Component {

  static get propTypes() {
    return {
      width: React.PropTypes.number.isRequired,
      height: React.PropTypes.number.isRequired,
      data: React.PropTypes.object.isRequired,
      mode: React.PropTypes.string.isRequired,
      padding: React.PropTypes.number.isRequired,
      animation: AnimationPropType
    };
  }

  static get defaultProps() {
    return {
      mode: 'squarify',
      padding: 0,
      data: {
        children: []
      }
    };
  }

  constructor(props) {
    super(props);
    this._renderLeaf = this._renderLeaf.bind(this);
  }

  /**
   * Walk through the tree and collect all values from the tree.
   * @param {Object} data Tree object.
   * @param {string} propName Property name.
   * @returns {Array} Flat array of values.
   * @private
   */
  _collectValuesFromTree(data, propName) {
    let result = [];
    if (data[propName]) {
      result.push(data[propName]);
    }
    if (Array.isArray(data.children)) {
      data.children.forEach(function getValuesFromChildren(child) {
        result = result.concat(this._collectValuesFromTree(child, propName));
      }, this);
    }
    return result;
  }

  /**
   * Get the scale function from the options and from the array.
   * @param {Object} scaleOptions Scale options that are passed in props.
   * @param {Array} defaultDomain Default domain.
   * @param {Array} defaultRange Default range.
   * @returns {*} Scale function or undefined if scaleOptions are not defined.
   * @private
   */
  _getScaleFunction(scaleOptions, defaultDomain, defaultRange) {
    let domain;
    let range;
    let scaleFn;
    if (scaleOptions) {
      domain = scaleOptions.domain;
      range = scaleOptions.range;
      scaleFn = scaleOptions.scaleFn;
    }
    if (!scaleFn) {
      if (!domain) {
        domain = defaultDomain;
      }
      if (!range) {
        range = defaultRange;
      }
      scaleFn = d3.scale.linear().range(range).domain(domain);
    }
    return scaleFn;
  }

  _getColorScaleFunction(data) {
    const values = this._collectValuesFromTree(data, 'color');
    if (values.length) {
      const range = DEFAULT_SCALES.color.range;
      const domain = d3.extent(values);
      const options = this.props.scales ? this.props.scales.color : null;
      return this._getScaleFunction(options, domain, range);
    }
    return () => DEFAULT_COLOR;
  }

  _getOpacityScaleFunction(data) {
    const values = this._collectValuesFromTree(data, 'opacity');
    if (values.length) {
      const range = DEFAULT_SCALES.opacity.range;
      const domain = d3.extent(values);
      const options = this.props.scales ? this.props.scales.opacity : null;
      return this._getScaleFunction(options, domain, range);
    }
  }

  /**
   * Create the list of nodes to render.
   * @returns {Array} Array of nodes.
   * @private
   */
  _getNodesToRender() {
    const data = this.props.data;
    const height = this.props.height;
    const width = this.props.width;
    const mode = this.props.mode;
    const padding = this.props.padding;
    let nodes = [];
    let layout;
    if (data) {
      layout = d3.layout.treemap()
        .padding(padding)
        .mode(mode)
        .sort((a, b) => a.size - b.size);
      nodes = layout
        .size([width, height])
        .value(d => d.size)
        .sticky(true)
        .nodes(data);
    }

    return nodes;
  }

  _renderLeaf(node, i) {
    if (!i) {
      return null;
    }
    const {data} = this.props;
    const colorScale = this._getColorScaleFunction(data);
    const opacityScale = this._getOpacityScaleFunction(data);
    // Don't draw the background for the first item: it's a container
    // for the entire treemap.
    const background = colorScale ? colorScale(node.color) : null;
    const opacity = opacityScale ? opacityScale(node.opacity) : null;
    let color = null;
    if (background) {
      color = d3.hsl(background).l > 0.57 ? '#222' : '#fff';
    }

    const width = Math.max(0, node.dx - 1);
    const height = Math.max(0, node.dy - 1);
    const style = getCSSAnimation(this.props, {
      top: `${node.y}px`,
      left: `${node.x}px`,
      width: `${width}px`,
      height: `${height}px`,
      background,
      opacity,
      color
    });
    return (

{node.title}

    );
  }

  render() {
    const {width, height} = this.props;
    const nodes = this._getNodesToRender();
    return (
      <div
        className={_c()}
        style={{
          width: `${width}px`,
          height: `${height}px`
        }}>
        {nodes.map(this._renderLeaf)}

    );
  }

}

Treemap.displayName = 'Treemap';

export default Treemap;
<commit_msg>Ability to use colorRange and opacityRange for the treemap<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

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

import {getCSSAnimation, AnimationPropType} from '../utils/animation-utils';
import {getAttributeFunctor} from '../utils/scales-utils';

import {
  CONTINUOUS_COLOR_RANGE,
  DEFAULT_COLOR,
  OPACITY_RANGE} from '../theme';

function getFontColorFromBackground(background) {
  if (background) {
    return d3.hsl(background).l > 0.57 ? '#222' : '#fff';
  }
  return null;
}

class Treemap extends React.Component {

  static get propTypes() {
    return {
      width: React.PropTypes.number.isRequired,
      height: React.PropTypes.number.isRequired,
      data: React.PropTypes.object.isRequired,
      mode: React.PropTypes.string.isRequired,
      padding: React.PropTypes.number.isRequired,
      animation: AnimationPropType
    };
  }

  static get defaultProps() {
    return {
      mode: 'squarify',
      padding: 0,
      data: {
        children: []
      },
      colorRange: CONTINUOUS_COLOR_RANGE,
      _colorValue: DEFAULT_COLOR,
      opacityRange: OPACITY_RANGE,
      _opacityValue: 1
    };
  }

  constructor(props) {
    super(props);
    this._renderLeaf = this._renderLeaf.bind(this);
    this.state = {scales: this._getScaleFns(props)};
  }

  componentWillReceiveProps(props) {
    this.setState({scales: this._getScaleFns(props)});
  }

  /**
   * Get the map of scale functions from the given props.
   * @param {Object} props Props for the component.
   * @returns {Object} Map of scale functions.
   * @private
   */
  _getScaleFns(props) {
    const {data} = props;

    // Adding _allData property to the object to reuse the existing
    // getAttributeFunctor function.
    const compatibleProps = {
      ...props,
      _allData: data.children || []
    };
    return {
      opacity: getAttributeFunctor(compatibleProps, 'opacity'),
      color: getAttributeFunctor(compatibleProps, 'color')
    };
  }

  /**
   * Create the list of nodes to render.
   * @returns {Array} Array of nodes.
   * @private
   */
  _getNodesToRender() {
    const data = this.props.data;
    const height = this.props.height;
    const width = this.props.width;
    const mode = this.props.mode;
    const padding = this.props.padding;
    let nodes = [];
    let layout;
    if (data) {
      layout = d3.layout.treemap()
        .padding(padding)
        .mode(mode)
        .sort((a, b) => a.size - b.size);
      nodes = layout
        .size([width, height])
        .value(d => d.size)
        .sticky(true)
        .nodes(data);
    }

    return nodes;
  }

  _renderLeaf(node, i) {
    if (!i) {
      return null;
    }
    const {scales} = this.state;

    const background = scales.color(node);
    const opacity = scales.opacity(node);
    const color = getFontColorFromBackground(background);
    const width = Math.max(0, node.dx - 1);
    const height = Math.max(0, node.dy - 1);
    const style = getCSSAnimation(this.props, {
      top: `${node.y}px`,
      left: `${node.x}px`,
      width: `${width}px`,
      height: `${height}px`,
      background,
      opacity,
      color
    });
    return (

{node.title}

    );
  }

  render() {
    const {width, height} = this.props;
    const nodes = this._getNodesToRender();
    return (
      <div
        className="rv-treemap"
        style={{
          width: `${width}px`,
          height: `${height}px`
        }}>
        {nodes.map(this._renderLeaf)}

    );
  }

}

Treemap.displayName = 'Treemap';

export default Treemap;
<|endoftext|>


<commit_before>var express = require('express');
var { ObjectID } = require('mongodb');
var router = express.Router();
var config = require('../../common-config.json');

/*
 * POST /api/attendee
 *
 * 讓參加者申請上台
 */
router.post('/attendee', function(req, res, next) {

  console.log('GET /api/attendee');
  console.log('g_recaptcha_response: %s', req.body.g_recaptcha_response);

  // Check post body data
  if (req.body.attendee_name === undefined ||
    req.body.summary === undefined ||
    req.body.email === undefined) {
    console.log(req.body);
    console.log('Bad Request');
    res.status(400).send('Bad Request');
  }

  var moment = require('moment');
  var md5 = require('md5');

  var attendee = {
    attendee_name: req.body.attendee_name,
    summary: req.body.summary,
    email: req.body.email,
    gravatar: 'https://www.gravatar.com/avatar/' + md5(req.body.email) + '?s=150',
    created_at: moment().unix(),
    recognized_at: 0, // To be toggled by moderators
    spoken_at: 0,
    removed_at: 0
  };

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  var insertAttendee = function(attendeeObj, db, callback) {
    // get collection
    var collection = db.collection('attendee');
    // insert attendee
    collection.insertOne(attendeeObj, function(err, ret) {
      assert.equal(err, null);
      assert.equal(1, ret.result.n);
      assert.equal(1, ret.ops.length);
      callback(ret);
      db.close();
    });
  };

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    //console.log("Connected successfully to server");

    insertAttendee(attendee, db, function(ret) {

      console.log('Insert attendee success');
      console.log(ret.ops);

      // Emit Event to moderate
      res.io.emit('newAttendee', ret.ops[0]);

      res.send({
        status: ret.result.ok
      });
    });
  });
});

/*
 * GET /api/attendee (AUTH)
 *
 * 給 moderate 拿到所有 (removed_at = 0 && recognized_at = 0) attendee
 */
router.get('/attendee', function(req, res, next) {

  console.log('GET /api/attendee');

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    var collection = db.collection('attendee');

    collection.find({
      removed_at: 0,
      recognized_at: 0
    }).toArray(function(err, ret) {
      console.log('get attendee list success');
      assert.equal(null, err);
      db.close();
      res.send(ret);
    });
  });
});

/*
 * PUT /api/attendee (AUTH)
 *
 * 1. 認可講者
 *   - `recognized_at` = timestamp
 * 2. 刪除講者
 *   - `removed_at` = timestamp
 */
router.put('/attendee', function(req, res, next) {

  console.log('PUT /api/attendee');

  var payload = JSON.parse(req.body.payload);

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    var collection = db.collection('attendee');

    collection.updateOne({
      _id: ObjectID(req.body._id)
    }, {
      $set: payload
    }, function(err, ret) {
      console.log('update attendee success');
      assert.equal(null, err);
      assert.equal(1, ret.result.n);
      db.close();
      res.send({
        status: ret.result.ok
      });
    });
  });
});

/*
  wow Queue!
*/
router.get('/queue', function(req, res, next) {

  console.log('GET /api/queue');

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    var collection = db.collection('attendee');

    collection.find({
      recognized_at: { $gt: 0 },
      spoken_at: 0
    }).sort({
      recognized_at: 1,
      created_at: 1
    }).toArray(function(err, ret) {
      console.log('get queue list success');
      assert.equal(null, err);
      db.close();
      res.send(ret);
    });
  });
});

module.exports = router;
<commit_msg>New PUT attendee API<commit_after>var express = require('express');
var { ObjectID } = require('mongodb');
var router = express.Router();
var config = require('../../common-config.json');

/*
 * POST /api/attendee
 *
 * 讓參加者申請上台
 */
router.post('/attendee', function(req, res, next) {

  console.log('GET /api/attendee');
  console.log('g_recaptcha_response: %s', req.body.g_recaptcha_response);

  // Check post body data
  if (req.body.attendee_name === undefined ||
    req.body.summary === undefined ||
    req.body.email === undefined) {
    console.log(req.body);
    console.log('Bad Request');
    res.status(400).send('Bad Request');
  }

  var moment = require('moment');
  var md5 = require('md5');

  var attendee = {
    attendee_name: req.body.attendee_name,
    summary: req.body.summary,
    email: req.body.email,
    gravatar: 'https://www.gravatar.com/avatar/' + md5(req.body.email) + '?s=150',
    created_at: moment().unix(),
    recognized_at: 0, // To be toggled by moderators
    spoken_at: 0,
    removed_at: 0
  };

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  var insertAttendee = function(attendeeObj, db, callback) {
    // get collection
    var collection = db.collection('attendee');
    // insert attendee
    collection.insertOne(attendeeObj, function(err, ret) {
      assert.equal(err, null);
      assert.equal(1, ret.result.n);
      assert.equal(1, ret.ops.length);
      callback(ret);
      db.close();
    });
  };

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    //console.log("Connected successfully to server");

    insertAttendee(attendee, db, function(ret) {

      console.log('Insert attendee success');
      console.log(ret.ops);

      // Emit Event to moderate
      res.io.emit('newAttendee', ret.ops[0]);

      res.send({
        status: ret.result.ok
      });
    });
  });
});

/*
 * GET /api/attendee (AUTH)
 *
 * 給 moderate 拿到所有 (removed_at = 0 && recognized_at = 0) attendee
 */
router.get('/attendee', function(req, res, next) {

  console.log('GET /api/attendee');

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    var collection = db.collection('attendee');

    collection.find({
      removed_at: 0,
      recognized_at: 0
    }).toArray(function(err, ret) {
      console.log('get attendee list success');
      assert.equal(null, err);
      db.close();
      res.send(ret);
    });
  });
});

/*
 * PUT /api/attendee/{_id} (AUTH)

1. 認可講者
    - `recognized` = boolean
2. 刪除講者
    - `removed` = boolean
3. 更新講者講完
    - `spoken` = boolean

```json
{
    "recognized": 'true' or 'false',
    // OR
    "removed": 'true' or 'false',
    // OR
    "spoken": 'true' or 'false'
}
```
 */
router.put('/attendee/:id', function(req, res, next) {

  var moment = require('moment');
  console.log('PUT /api/attendee');

  var op = req.body;
  if ('recognized' in op) {
    var payload = {'recognized_at': (op['recognized'] == 'true' ? moment().unix() : 0)};
  } else if ('removed' in op) {
    var payload = {'removed_at': (op['removed'] == 'true' ? moment().unix() : 0)};
  } else if ('spoken' in op) {
    var payload = {'spoken_at': (op['spoken'] == 'true' ? moment().unix() : 0)};
  } else {
    console.log(req.body);
    console.log('Bad Request');
    res.status(400).send('Bad Request');
  }

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    var collection = db.collection('attendee');

    collection.updateOne({
      _id: ObjectID(req.params.id)
    }, {
      $set: payload
    }, function(err, ret) {
      console.log('update attendee success');
      assert.equal(null, err);
      assert.equal(1, ret.result.n);
      db.close();
      res.send({
        status: ret.result.ok
      });
    });
  });
});

/*
  GET /api/queue
  取得目前進入 Queue 的所有講者
*/
router.get('/queue', function(req, res, next) {

  console.log('GET /api/queue');

  var MongoClient = require('mongodb').MongoClient,
    assert = require('assert');

  // Connection URL
  var url = 'mongodb://localhost:27017/pdmod';

  // Use connect method to connect to the server
  MongoClient.connect(url, function(err, db) {

    assert.equal(null, err);
    var collection = db.collection('attendee');

    collection.find({
      recognized_at: { $gt: 0 },
      spoken_at: 0
    }).sort({
      recognized_at: 1,
      created_at: 1
    }).toArray(function(err, ret) {
      console.log('get queue list success');
      assert.equal(null, err);
      db.close();
      res.send(ret);
    });
  });
});

module.exports = router;
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import {shallow} from 'enzyme';

import <%= name %> from 'components/<%= name %>';

it('<%= name %>', () => {
  const wrapper = shallow(
    <<%= name %> />
  );
  expect(wrapper.html()).toBe('This is <%= name %>!');
});
<commit_msg>Update test component.js<commit_after>'use strict';

import React from 'react';
import {mount} from 'enzyme';

import <%= name %> from 'components/<%= name %>';

it('<%= name %>', () => {
  const wrapper = mount(
    <<%= name %> />
  );

  expect(wrapper.containsAnyMatchingElements([

This is <%= name %>!

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


<commit_before>/* eslint no-console:0 */

// `@babel/polyfill` is deprecated
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babel
import "core-js/stable";
import "regenerator-runtime/runtime";

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { FlashMessages } from 'lib/flashes';
import dialogs from 'lib/dialogs';

import { createStore, combineReducers, applyMiddleware } from 'redux'
import ReduxThunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import { setPolicy } from 'policy';
import { configureAjaxHelper } from 'ajax_helper';

const isIterable = (obj) => {
  // checks for null and undefined
  if (obj == null) {
    return false;
  }
  return typeof obj[Symbol.iterator] === 'function';
}

const getDataset = (element) => {
  const set = {}
  for(let a of element.attributes) {
    if(a.name.indexOf('data-')==0) set[a.name] = a.value
  }
  return set
}

const setAttributes = (element, attributes) => {
  for(let key of attributes) element.setAttribute(key,attributes[key])
}

class Widget {
  constructor(reactContainers, config) {
    // reactContainers should always be an array (support global widgets)
    this.reactContainers = isIterable(reactContainers) ? reactContainers : [reactContainers]
    this.config = config
    this.createStore = this.createStore.bind(this)
    this.render = this.render.bind(this)
    this.configureAjaxHelper = this.configureAjaxHelper.bind(this)
    this.setPolicy = this.setPolicy.bind(this)
  }

  createStore(reducers){
    const devOptions = this.config.devOptions || {}
    const composeEnhancers = composeWithDevTools(devOptions);

    this.store = createStore(combineReducers(reducers), /* preloadedState, */ composeEnhancers(
      applyMiddleware(ReduxThunk),
      // other store enhancers if any
    ));
  }

  configureAjaxHelper(options) {
    options = options || {}
    const ajaxHelperOptions = Object.assign({},this.config.ajaxHelper,options)

    configureAjaxHelper(ajaxHelperOptions)
  }

  setPolicy(policy) {
    policy = policy || this.config.policy
    setPolicy(this.config.policy);
  }

  render(container){
    // const Container = React.createElement(container, this.config.params)

    for(let reactContainer of this.reactContainers) {
      let dataset = getDataset(reactContainer)
      let wrappedComponent = React.createElement(container, Object.assign({},dataset,this.config.params))

      if(this.store) {
        ReactDOM.render(
          <Provider store = { this.store }>
            <React.Fragment>
              { (this.config.params.flashescontainer !== "custom") && <FlashMessages/>}
              { wrappedComponent }
            </React.Fragment>
          </Provider>, reactContainer
        )
      } else {
        ReactDOM.render(
          <React.Fragment>
            { (this.config.params.flashescontainer !== "custom") && <FlashMessages/>}
            { wrappedComponent }
          </React.Fragment>, reactContainer
        )
      }
    }
  }
}

export const getWidgetName = (dirname) => {
if(!dirname) return null
const name_regex = /.*plugins\/([^\/]+)\/app\/javascript\/([^\.]+)/
const name_tokens = dirname.match(name_regex);
if(name_tokens.length<2) return null
return `${name_tokens[1]}_${name_tokens[2]}`
}

const getCurrentScript = (widgetName) => {
  if(widgetName) {
    let script = document.querySelector(`script[src*="/${widgetName}"]`);
    if(script) return script
  }
  let scripts = document.getElementsByTagName( 'script' );
  return scripts[ scripts.length - 1 ];
}

export const createConfig = (widgetName, params) => {
  // get current url without params and bind it to baseURL
  let origin = window.location.origin
  if(!origin) {
    const originMatch = window.location.href.match(/(http(s)?:\/\/[^\/]+).*/)
    if (originMatch) origin = originMatch[1]
  }

  return {
    params,
    scriptParams: params,
    devOptions: { name: widgetName },
    ajaxHelper: {
      baseURL: `${origin}${window.location.pathname}`,
      headers: {}
    },
    policy: window.policy
  }
}

export const createWidget = (dirname, options={}) => {
  const widgetName = getWidgetName(dirname)
  let reactContainers = options.containers
  let params = options.params || {}

  if(!reactContainers) {
    let scriptTagContainer = getContainerFromCurrentScript(widgetName)
    reactContainers = [scriptTagContainer.reactContainer]
    params = scriptTagContainer.scriptParams
  }

  let htmlOptions = options.html || {};
  let defaultHtmlOptions = {class: '.react-widget-content'};
  htmlOptions = Object.assign({}, defaultHtmlOptions, htmlOptions);
  for(let attr in htmlOptions) {
    for(let reactContainer of reactContainers) {
      reactContainer.setAttribute(attr, htmlOptions[attr])
    }
  }

  // if document is already loaded then resolve Promise immediately
  // with a new widget object
  if (document.readyState === "complete")
    return Promise.resolve(new Widget(reactContainers, createConfig(widgetName, params)))

  // document is not loaded yet -> create a new Promise and resolve it as soon
  // as document is loaded.
  return new Promise((resolve, reject) => {
    document.addEventListener('DOMContentLoaded', () => {
      resolve(new Widget(reactContainers, createConfig(widgetName, params)))
    })
  })
}

export const getContainerFromCurrentScript = (widgetName) => {
  const currentScript = getCurrentScript(widgetName)
  const scriptParams = JSON.parse(JSON.stringify(currentScript.dataset))
  const srcTokens = currentScript && currentScript.getAttribute('src') ? currentScript.getAttribute('src').split('/') : []
  const reactContainer = window.document.createElement('div');

  currentScript.parentNode.replaceChild(reactContainer, currentScript);
  return {
    reactContainer,
    scriptParams
  }
}
<commit_msg>check if reactContaniner exists (#837)<commit_after>/* eslint no-console:0 */

// `@babel/polyfill` is deprecated
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md#babel
import "core-js/stable";
import "regenerator-runtime/runtime";

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { FlashMessages } from 'lib/flashes';
import dialogs from 'lib/dialogs';

import { createStore, combineReducers, applyMiddleware } from 'redux'
import ReduxThunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import { setPolicy } from 'policy';
import { configureAjaxHelper } from 'ajax_helper';

const isIterable = (obj) => {
  // checks for null and undefined
  if (obj == null) {
    return false;
  }
  return typeof obj[Symbol.iterator] === 'function';
}

const getDataset = (element) => {
  const set = {}
  for(let a of element.attributes) {
    if(a.name.indexOf('data-')==0) set[a.name] = a.value
  }
  return set
}

const setAttributes = (element, attributes) => {
  for(let key of attributes) element.setAttribute(key,attributes[key])
}

class Widget {
  constructor(reactContainers, config) {
    // reactContainers should always be an array (support global widgets)
    this.reactContainers = isIterable(reactContainers) ? reactContainers : [reactContainers]
    this.config = config
    this.createStore = this.createStore.bind(this)
    this.render = this.render.bind(this)
    this.configureAjaxHelper = this.configureAjaxHelper.bind(this)
    this.setPolicy = this.setPolicy.bind(this)
  }

  createStore(reducers){
    const devOptions = this.config.devOptions || {}
    const composeEnhancers = composeWithDevTools(devOptions);

    this.store = createStore(combineReducers(reducers), /* preloadedState, */ composeEnhancers(
      applyMiddleware(ReduxThunk),
      // other store enhancers if any
    ));
  }

  configureAjaxHelper(options) {
    options = options || {}
    const ajaxHelperOptions = Object.assign({},this.config.ajaxHelper,options)

    configureAjaxHelper(ajaxHelperOptions)
  }

  setPolicy(policy) {
    policy = policy || this.config.policy
    setPolicy(this.config.policy);
  }

  render(container){
    // const Container = React.createElement(container, this.config.params)

    for(let reactContainer of this.reactContainers) {
      let dataset = getDataset(reactContainer)
      let wrappedComponent = React.createElement(container, Object.assign({},dataset,this.config.params))

      if(this.store) {
        ReactDOM.render(
          <Provider store = { this.store }>
            <React.Fragment>
              { (this.config.params.flashescontainer !== "custom") && <FlashMessages/>}
              { wrappedComponent }
            </React.Fragment>
          </Provider>, reactContainer
        )
      } else {
        ReactDOM.render(
          <React.Fragment>
            { (this.config.params.flashescontainer !== "custom") && <FlashMessages/>}
            { wrappedComponent }
          </React.Fragment>, reactContainer
        )
      }
    }
  }
}

export const getWidgetName = (dirname) => {
if(!dirname) return null
const name_regex = /.*plugins\/([^\/]+)\/app\/javascript\/([^\.]+)/
const name_tokens = dirname.match(name_regex);
if(name_tokens.length<2) return null
return `${name_tokens[1]}_${name_tokens[2]}`
}

const getCurrentScript = (widgetName) => {
  if(widgetName) {
    let script = document.querySelector(`script[src*="/${widgetName}"]`);
    if(script) return script
  }
  let scripts = document.getElementsByTagName( 'script' );
  return scripts[ scripts.length - 1 ];
}

export const createConfig = (widgetName, params) => {
  // get current url without params and bind it to baseURL
  let origin = window.location.origin
  if(!origin) {
    const originMatch = window.location.href.match(/(http(s)?:\/\/[^\/]+).*/)
    if (originMatch) origin = originMatch[1]
  }

  return {
    params,
    scriptParams: params,
    devOptions: { name: widgetName },
    ajaxHelper: {
      baseURL: `${origin}${window.location.pathname}`,
      headers: {}
    },
    policy: window.policy
  }
}

export const createWidget = (dirname, options={}) => {
  const widgetName = getWidgetName(dirname)
  let reactContainers = options.containers
  let params = options.params || {}

  if(!reactContainers) {
    let scriptTagContainer = getContainerFromCurrentScript(widgetName)
    reactContainers = [scriptTagContainer.reactContainer]
    params = scriptTagContainer.scriptParams
  }

  let htmlOptions = options.html || {};
  let defaultHtmlOptions = {class: '.react-widget-content'};
  htmlOptions = Object.assign({}, defaultHtmlOptions, htmlOptions);
  for(let attr in htmlOptions) {
    for(let reactContainer of reactContainers) {
      if(reactContainer) reactContainer.setAttribute(attr, htmlOptions[attr])
    }
  }

  // if document is already loaded then resolve Promise immediately
  // with a new widget object
  if (document.readyState === "complete")
    return Promise.resolve(new Widget(reactContainers, createConfig(widgetName, params)))

  // document is not loaded yet -> create a new Promise and resolve it as soon
  // as document is loaded.
  return new Promise((resolve, reject) => {
    document.addEventListener('DOMContentLoaded', () => {
      resolve(new Widget(reactContainers, createConfig(widgetName, params)))
    })
  })
}

export const getContainerFromCurrentScript = (widgetName) => {
  const currentScript = getCurrentScript(widgetName)
  const scriptParams = JSON.parse(JSON.stringify(currentScript.dataset))
  const srcTokens = currentScript && currentScript.getAttribute('src') ? currentScript.getAttribute('src').split('/') : []
  const reactContainer = window.document.createElement('div');

  currentScript.parentNode.replaceChild(reactContainer, currentScript);
  return {
    reactContainer,
    scriptParams
  }
}
<|endoftext|>