<commit_before>pointCloud = {
    name: "PointCloud",
    curElID: undefined,
    panelHTML: function(uniqueParentElementID) {
        var html = '';
        html += ""
        return html;
    },
    /*
    panelContextMenu: function(parentElement) {
        // TODO implement
    },
    topMenuItems : {

    },*/

    linkDataKeys: [
        'xyz',
        'c',
        'rgb',
        'xyzc',
        'xyzrgb'
    ],

    getPanelData: function(parentElement, key){
      if (key == 'xyz'){
         return;
      } else if (key == 'c') {
         return;
      } else if (key == 'rgb') {
         return;
      } else if (key == 'xyzc') {
         return;
      } else if (key == 'xyzrgb') {
         return;
      } else {
          return;
      }
    },

    setPanelData: function(parentElement, data, key) {
      if (key == 'xyz'){
         return;
      } else if (key == 'c') {
         return;
      } else if (key == 'rgb') {
         return;
      } else if (key == 'xyzc') {
         return;
      } else if (key == 'xyzrgb') {
         return;
      } else {
          return;
      }
    },

    init: function (elID){
        var showStats = true;

        if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

        panelContainer = $('#' + elID);

        var container;
        var camera, scene, renderer, particles, geometry, material, i, h, color, sprite, size;
        var controls;
        var mouseX = 0, mouseY = 0;
        var pcPtRange = 10;
        var pcMaxRange = 64*64;
        var pcNextI = 0;

        var windowHalfX = panelContainer.width() / 2;
        var windowHalfY = panelContainer.height() / 2;

        // Initialize panelContainer
        container = document.createElement( 'div' );
        panelContainer.append( container );

        camera = new THREE.PerspectiveCamera( 55, panelContainer.width() / panelContainer.height(), 0.05, 200 );
        camera.position.z = 0;
        camera.position.x = 1;
        camera.position.y = 3;
        camera.up.set(0, 0, 1);

        //

        scene = new THREE.Scene();
//                 scene.fog = new THREE.FogExp2( 0x000000, 0.001 );

        geometry = new THREE.Geometry();

//                 sprite = new THREE.TextureLoader().load( "disc.png" );

        var numPoints = pcMaxRange;
        var positions = new Float32Array(numPoints*3);
        var intensities = new Float32Array(numPoints);
        var moments = new Float32Array(numPoints*3);

        for(var i = 0; i < 10; i++){
            positions[pcNextI*3 + 0] = Math.random();
            positions[pcNextI*3 + 1] = Math.random()
            positions[pcNextI*3 + 2] = Math.random()
            intensities[pcNextI + 0] = Math.random()
            moments[pcNextI*3 + 0] = Math.random()
            moments[pcNextI*3 + 1] = Math.random()
            moments[pcNextI*3 + 2] = Math.random()
            pcNextI = pointCloud.incrementPcNextI(pcNextI, pcMaxRange);
        }

        geometry = new THREE.BufferGeometry();
        geometry.addAttribute("position", new THREE.BufferAttribute(positions, 3));
        geometry.addAttribute("intensities", new THREE.BufferAttribute(intensities, 1));
        geometry.addAttribute("moments", new THREE.BufferAttribute(moments, 3));
        geometry.addAttribute("color", new THREE.BufferAttribute(moments, 3));

        geometry.dynamic = true;
        geometry.setDrawRange(0, 10);

        material = new THREE.PointsMaterial( {
            size: 0.02,
            vertexColors: THREE.VertexColors,
            sizeAttenuation: true,
            alphaTest: 0.5,
            transparent: true,
            opacity: 0.9
//                 	color: 0x555555,
// 	                map: sprite
         } );

        particles = new THREE.Points( geometry, material );
        scene.add( particles );

        //

        renderer = new THREE.WebGLRenderer();
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( panelContainer.width(), panelContainer.height() );
        container.appendChild( renderer.domElement );

        //

        controls = new THREE.OrbitControls( camera, renderer.domElement );
        controls.addEventListener( 'change', pointCloud.render );

        //
        if (showStats) {
            var stats;
            stats = new Stats();
            container.appendChild( stats.dom );
        }

        //
        // BET THIS WON'T WORK, Need the equivalent for a div...
        // THIS STILL DOESN"T WORK
        panelContainer[0].addEventListener( 'resize', pointCloud.onWindowResize, false );
        window.addEventListener( 'resize', pointCloud.onWindowResize, false );
        // NEED TO CHANGE CUREL
        panelContainer.click(function (event) {
           var container = $(divIDE.getCtxTarget(event.target));
           pointCloud.curElID = container.attr('id');
        });
        renderer.domElement.addEventListener("dblclick", pointCloud.ondblclick, false);

        divIDE.panelJSData[elID] = {
            container: container,
            camera: camera,
            scene: scene,
            renderer: renderer,
            particles: particles,
            geometry: geometry,
            material: material,
            i: i,
            h: h,
            color: color,
            sprite: sprite,
            size: size,
            controls: controls,
            mouseX: mouseX,
            mouseY: mouseY,
            pcPtRange: pcPtRange,
            pcMaxRange: pcMaxRange,
            pcNextI: pcNextI,
            windowHalfX: windowHalfX,
            windowHalfY: windowHalfY,
        };

        if (showStats){
            divIDE.panelJSData[elID].stats = stats;
        };

        // Animate
        pointCloud.curElID = elID;
        pointCloud.animate();
        pointCloud.onWindowResize();
    },

    incrementPcNextI: function (pcNextI, pcMaxRange){
        pcNextI += 1;
        if (pcNextI == pcMaxRange){
            pcNextI = 0;
        }
        return pcNextI;
    },

    onWindowResize: function () {
        var elID = pointCloud.curElID;
        var panelContainer = $('#' + elID);

        var windowHalfX = panelContainer.width() / 2;
        var windowHalfY = panelContainer.height() / 2;

        if (divIDE.panelJSData[elID].windowHalfX == windowHalfX &&
        divIDE.panelJSData[elID].windowHalfY == windowHalfY){
            return;
        }
        divIDE.panelJSData[elID].windowHalfX = windowHalfX;
        divIDE.panelJSData[elID].windowHalfY = windowHalfY;

        var camera = divIDE.panelJSData[elID].camera;
        var renderer = divIDE.panelJSData[elID].renderer;

        camera.aspect = panelContainer.width() / panelContainer.height();
        camera.updateProjectionMatrix();

        renderer.setSize( panelContainer.width(), panelContainer.height() );

//                 controls.handleResize();

//         pointCloud.render();

    },

    animate: function() {
        var elID = pointCloud.curElID;
        var controls = divIDE.panelJSData[elID].controls;
        var stats = divIDE.panelJSData[elID].stats;
        requestAnimationFrame( pointCloud.animate );
        controls.update()

        pointCloud.render(elID);
        stats.update();

    },

    render: function () {
        var elID = pointCloud.curElID;
        var scene = divIDE.panelJSData[elID].scene;
        var camera = divIDE.panelJSData[elID].camera;

        divIDE.panelJSData[elID].renderer.render( scene, camera );

        pointCloud.onWindowResize();
    },

    // Functionality to set the color based on a colormap
    setPointColor: function(colorArray, array, arrayStride, arrayOffset, vmin, vmax){
        if (arrayStride == undefined){
            var arrayStride = 1;
        }
        if (arrayOffset == undefined){
            var arrayOffset = 0;
        }
        if (vmax == undefined){
            var vmax = -Infinity;
            for (var i = 0; i < array.length / arrayStride; i++){
                if (array[i*arrayStride + arrayOffset] > vmax){
                    vmax = array[i*arrayStride + arrayOffset];
                }
            }
        }
        if (vmin == undefined){
            var vmin = Infinity;
            for (var i = 0; i < array.length / arrayStride; i++){
                if (array[i*arrayStride + arrayOffset] < vmin){
                    vmin = array[i*arrayStride + arrayOffset];
                }
            }
        }
        var colorMap = 'rainbow';
        var numberOfColors = 256;
        var lut = new THREE.Lut(colorMap, numberOfColors);
        lut.setMax ( vmax );
        lut.setMin ( vmin );
        var c;
        for (var i = 0; i < array.length / arrayStride; i++){
            c = lut.getColor ( array[i*arrayStride + arrayOffset] )
            colorArray[i*3 + 0] = c.r;
            colorArray[i*3 + 1] = c.g;
            colorArray[i*3 + 2] = c.b;
        }
    },

    updatePointCloudColors: function(geometry, attr, col, vmin, vmax){
        if (col == undefined){
            var col = 0;
        }
        setPointColor(
            geometry.attributes.color.array,
            geometry.attributes[attr].array,
            geometry.attributes[attr].itemSize, col, vmin, vmax);
        geometry.attributes.color.needsUpdate = true;
    },

    // Functionality to change the focus point of the orbit controls
    ondblclick: function (event) {
        var elID = pointCloud.curElID;
        var panelContainer = $('#' + elID);
        var camera = divIDE.panelJSData[elID].camera;
        var controls = divIDE.panelJSData[elID].controls;
        var particles = divIDE.panelJSData[elID].particles;

        x = ((event.clientX - panelContainer.offset().left) / panelContainer.width()) * 2 - 1;
        y = -((event.clientY - panelContainer.offset().top) / panelContainer.height()) * 2 + 1;
        dir = new THREE.Vector3(x, y, -1)
        dir.unproject(camera)

        ray = new THREE.Raycaster(camera.position, dir.sub(camera.position).normalize())
        var intersects = ray.intersectObject(particles);
        if ( intersects.length > 0 )
        {
            var minI = 0;
            var minD = intersects[0].distanceToRay;
            for (var i=0; i < intersects.length; i++){
                if (minD > intersects[i].distanceToRay){
                    minI = i;
                    minD = intersects[i].distanceToRay;
                }
            }
            controls.target.set(intersects[minI].point.x, intersects[minI].point.y, intersects[minI].point.z);
        }
    },

    streamData: function () {
        ws.send('sendData');
        wsStreaming = true;
    },

    stopStreamData: function (){
        wsStreaming = false;
    },

    updatePointCloud: function (buffer, elID){
        container = $('#' + elID);
        var pcNextI = divIDE.panelJSData[container].pcNextI;
        var pcMaxRange = divIDE.panelJSData[container].pcMaxRange;
        var n_columns = 7;
        var n_bytes = 4;  // astype np.float32 on server side
        var stride = n_columns * n_bytes;
        var view = new DataView(buffer);
        var numPoints = buffer.byteLength / stride;
        pcPtRange += numPoints;
        pcPtRange = Math.min(pcPtRange, pcMaxRange);

        var position = geometry.attributes.position.array
        var intensities = geometry.attributes.intensities.array
        var moments = geometry.attributes.moments.array

        for(var i = 0; i < numPoints; i++){
            position[pcNextI*3 + 0] = view.getFloat32(i*stride + 0 * n_bytes, true);
            position[pcNextI*3 + 1] = view.getFloat32(i*stride + 1 * n_bytes, true);
            position[pcNextI*3 + 2] = view.getFloat32(i*stride + 2 * n_bytes, true);
            intensities[pcNextI + 0] = view.getFloat32(i*stride + 3 * n_bytes, true);
            moments[pcNextI*3 + 0] = view.getFloat32(i*stride + 4 * n_bytes, true);
            moments[pcNextI*3 + 1] = view.getFloat32(i*stride + 5 * n_bytes, true);
            moments[pcNextI*3 + 2] = view.getFloat32(i*stride + 6 * n_bytes, true);
            pointCloud.incrementPcNextI(pcNextI, pcMaxRange)
        }
        updatePointCloudColors(geometry, 'position', 2);
        geometry.attributes.position.needsUpdate = true;
        geometry.setDrawRange(0, pcPtRange);

        // try to free some of this memory
        buffer = null;
        view = null;

        // Ask for the next set of data
        if (wsStreaming){
            ws.send('sendData');
// 					setTimeout(ws.send('stream'), 1000);
        }

    }
}

divIDE.registerPanel(pointCloud);
<commit_msg>Rearranged some functions. Mainly working on setStringGeometry -- savepoint now that something works.<commit_after>pointCloud = {
    name: "PointCloud",
    curElID: undefined,
    panelHTML: function(uniqueParentElementID) {
        var html = '';
        html += ""
        return html;
    },
    /*
    panelContextMenu: function(parentElement) {
        // TODO implement
    },
    topMenuItems : {

    },*/

    linkDataKeys: [
        'xyz',
        'c',
        'rgb',
        'xyzc',
        'xyzrgb'
    ],

    getPanelData: function(parentElement, key){
      if (key == 'xyz'){
         return;
      } else if (key == 'c') {
         return;
      } else if (key == 'rgb') {
         return;
      } else if (key == 'xyzc') {
         return;
      } else if (key == 'xyzrgb') {
         return;
      } else {
          return;
      }
    },

    setPanelData: function(parentElement, data, key) {
      if (key == 'xyz'){
         return;
      } else if (key == 'c') {
         return;
      } else if (key == 'rgb') {
         return;
      } else if (key == 'xyzc') {
         return;
      } else if (key == 'xyzrgb') {
         return;
      } else {
          return;
      }
    },

    init: function (elID){
        var showStats = true;

        if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

        panelContainer = $('#' + elID);

        var container;
        var camera, scene, renderer, particles, geometry, material, i, h, color, sprite, size;
        var controls;
        var mouseX = 0, mouseY = 0;
        var pcPtRange = 10;
        var pcMaxRange = 64*64;
        var pcNextI = 0;

        var windowHalfX = panelContainer.width() / 2;
        var windowHalfY = panelContainer.height() / 2;

        // Initialize panelContainer
        container = document.createElement( 'div' );
        panelContainer.append( container );

        camera = new THREE.PerspectiveCamera( 55, panelContainer.width() / panelContainer.height(), 0.05, 200 );
        camera.position.z = 0;
        camera.position.x = 1;
        camera.position.y = 3;
        camera.up.set(0, 0, 1);

        //

        scene = new THREE.Scene();
//                 scene.fog = new THREE.FogExp2( 0x000000, 0.001 );

        geometry = new THREE.Geometry();

//                 sprite = new THREE.TextureLoader().load( "disc.png" );

        var numPoints = pcMaxRange;
        var positions = new Float32Array(numPoints*3);
        var intensities = new Float32Array(numPoints);
        var moments = new Float32Array(numPoints*3);

        for(var i = 0; i < 10; i++){
            positions[pcNextI*3 + 0] = Math.random();
            positions[pcNextI*3 + 1] = Math.random()
            positions[pcNextI*3 + 2] = Math.random()
            intensities[pcNextI + 0] = Math.random()
            moments[pcNextI*3 + 0] = Math.random()
            moments[pcNextI*3 + 1] = Math.random()
            moments[pcNextI*3 + 2] = Math.random()
            pcNextI = pointCloud.incrementPcNextI(pcNextI, pcMaxRange);
        }

//         geometry = new THREE.BufferGeometry();
//         geometry.addAttribute("position", new THREE.BufferAttribute(positions, 3));
//         geometry.addAttribute("intensities", new THREE.BufferAttribute(intensities, 1));
//         geometry.addAttribute("moments", new THREE.BufferAttribute(moments, 3));
//         geometry.addAttribute("color", new THREE.BufferAttribute(moments, 3));

//         geometry.dynamic = true;
//         geometry.setDrawRange(0, 10);

//         material = new THREE.PointsMaterial( {
//             size: 0.02,
//             vertexColors: THREE.VertexColors,
//             sizeAttenuation: true,
//             alphaTest: 0.5,
//             transparent: true,
//             opacity: 0.9
// //                 	color: 0x555555,
// // 	                map: sprite
//          } );

//         particles = new THREE.Points( geometry, material );
//         scene.add( particles );

        //

        renderer = new THREE.WebGLRenderer();
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( panelContainer.width(), panelContainer.height() );
        container.appendChild( renderer.domElement );

        //

        controls = new THREE.OrbitControls( camera, renderer.domElement );
        controls.addEventListener( 'change', pointCloud.render );

        //
        if (showStats) {
            var stats;
            stats = new Stats();
            container.appendChild( stats.dom );
        }

        //
        // BET THIS WON'T WORK, Need the equivalent for a div...
        // THIS STILL DOESN"T WORK
        panelContainer[0].addEventListener( 'resize', pointCloud.onWindowResize, false );
        window.addEventListener( 'resize', pointCloud.onWindowResize, false );
        // NEED TO CHANGE CUREL
        panelContainer.click(function (event) {
           var container = $(divIDE.getCtxTarget(event.target));
           pointCloud.curElID = container.attr('id');
        });
        renderer.domElement.addEventListener("dblclick", pointCloud.ondblclick, false);

        divIDE.panelJSData[elID] = {
            container: container,
            camera: camera,
            scene: scene,
            renderer: renderer,
            particles: particles,
            geometry: geometry,
            material: material,
            i: i,
            h: h,
            color: color,
            sprite: sprite,
            size: size,
            controls: controls,
            mouseX: mouseX,
            mouseY: mouseY,
            pcPtRange: pcPtRange,
            pcMaxRange: pcMaxRange,
            pcNextI: pcNextI,
            windowHalfX: windowHalfX,
            windowHalfY: windowHalfY,
        };

        if (showStats){
            divIDE.panelJSData[elID].stats = stats;
        };

        // Animate
        pointCloud.curElID = elID;
        pointCloud.animate();
        pointCloud.onWindowResize();
    },

    incrementPcNextI: function (pcNextI, pcMaxRange){
        pcNextI += 1;
        if (pcNextI == pcMaxRange){
            pcNextI = 0;
        }
        return pcNextI;
    },

    onWindowResize: function () {
        var elID = pointCloud.curElID;
        var panelContainer = $('#' + elID);

        var windowHalfX = panelContainer.width() / 2;
        var windowHalfY = panelContainer.height() / 2;

        if (divIDE.panelJSData[elID].windowHalfX == windowHalfX &&
        divIDE.panelJSData[elID].windowHalfY == windowHalfY){
            return;
        }
        divIDE.panelJSData[elID].windowHalfX = windowHalfX;
        divIDE.panelJSData[elID].windowHalfY = windowHalfY;

        var camera = divIDE.panelJSData[elID].camera;
        var renderer = divIDE.panelJSData[elID].renderer;

        camera.aspect = panelContainer.width() / panelContainer.height();
        camera.updateProjectionMatrix();

        renderer.setSize( panelContainer.width(), panelContainer.height() );
    },

    // Functionality to change the focus point of the orbit controls
    ondblclick: function (event) {
        var elID = pointCloud.curElID;
        var panelContainer = $('#' + elID);
        var camera = divIDE.panelJSData[elID].camera;
        var controls = divIDE.panelJSData[elID].controls;
        var particles = divIDE.panelJSData[elID].particles;
        if (particles == undefined){
            particles = divIDE.panelJSData[elID].stringParticles;
        }

        x = ((event.clientX - panelContainer.offset().left) / panelContainer.innerWidth()) * 2 - 1;
        y = -((event.clientY - panelContainer.offset().top) / panelContainer.innerHeight()) * 2 + 1;
        dir = new THREE.Vector3(x, y, -1)
        dir.unproject(camera)

        ray = new THREE.Raycaster(camera.position, dir.sub(camera.position).normalize())
        var intersects = ray.intersectObject(particles);
        if ( intersects.length > 0 )
        {
            var minI = 0;
            var minD = intersects[0].distanceToRay;
            for (var i=0; i < intersects.length; i++){
                if (minD > intersects[i].distanceToRay){
                    minI = i;
                    minD = intersects[i].distanceToRay;
                }
            }
            controls.target.set(intersects[minI].point.x, intersects[minI].point.y, intersects[minI].point.z);
        }
    },

    streamData: function () {
        ws.send('sendData');
        wsStreaming = true;
    },

    stopStreamData: function (){
        wsStreaming = false;
    },

    animate: function() {
        var elID = pointCloud.curElID;
        var controls = divIDE.panelJSData[elID].controls;
        var stats = divIDE.panelJSData[elID].stats;
        requestAnimationFrame( pointCloud.animate );
        controls.update()

        pointCloud.render(elID);
        stats.update();

    },

    render: function () {
        var elID = pointCloud.curElID;
        var scene = divIDE.panelJSData[elID].scene;
        var camera = divIDE.panelJSData[elID].camera;

        divIDE.panelJSData[elID].renderer.render( scene, camera );

        pointCloud.onWindowResize();
    },

    setStringGeometry: function (elID, vertices_json, colors_json){
        var scene = divIDE.panelJSData[elID].scene;
        var material = divIDE.panelJSData[elID].material;
        var geometry = divIDE.panelJSData[elID].stringGeometry;

        if (geometry != undefined){
            scene.remove(divIDE.panelJSData[elID].stringParticles);
            divIDE.panelJSData[elID].stringParticles.geometry.dispose();
        }

        geometry = new THREE.Geometry();
        var vertices = JSON.parse(vertices_json);
        for (var i=0; i < vertices.length; i++){
            var vertex = new THREE.Vector3()
            vertex.x = vertices[i][0];
            vertex.y = vertices[i][1];
            vertex.z = vertices[i][2];
            geometry.vertices.push(vertex);
        }
        var stringParticles = new THREE.Points( geometry, material );
        scene.add( stringParticles );
        divIDE.panelJSData[elID].stringParticles = stringParticles;
        divIDE.panelJSData[elID].stringGeometry = geometry;

    },

    // Functionality to set the color based on a colormap
    setPointColor: function(colorArray, array, arrayStride, arrayOffset, vmin, vmax){
        if (arrayStride == undefined){
            var arrayStride = 1;
        }
        if (arrayOffset == undefined){
            var arrayOffset = 0;
        }
        if (vmax == undefined){
            var vmax = -Infinity;
            for (var i = 0; i < array.length / arrayStride; i++){
                if (array[i*arrayStride + arrayOffset] > vmax){
                    vmax = array[i*arrayStride + arrayOffset];
                }
            }
        }
        if (vmin == undefined){
            var vmin = Infinity;
            for (var i = 0; i < array.length / arrayStride; i++){
                if (array[i*arrayStride + arrayOffset] < vmin){
                    vmin = array[i*arrayStride + arrayOffset];
                }
            }
        }
        var colorMap = 'rainbow';
        var numberOfColors = 256;
        var lut = new THREE.Lut(colorMap, numberOfColors);
        lut.setMax ( vmax );
        lut.setMin ( vmin );
        var c;
        for (var i = 0; i < array.length / arrayStride; i++){
            c = lut.getColor ( array[i*arrayStride + arrayOffset] )
            colorArray[i*3 + 0] = c.r;
            colorArray[i*3 + 1] = c.g;
            colorArray[i*3 + 2] = c.b;
        }
    },

    updatePointCloudColors: function(geometry, attr, col, vmin, vmax){
        if (col == undefined){
            var col = 0;
        }
        setPointColor(
            geometry.attributes.color.array,
            geometry.attributes[attr].array,
            geometry.attributes[attr].itemSize, col, vmin, vmax);
        geometry.attributes.color.needsUpdate = true;
    },

    updatePointCloud: function (buffer, elID){
        container = $('#' + elID);
        var pcNextI = divIDE.panelJSData[container].pcNextI;
        var pcMaxRange = divIDE.panelJSData[container].pcMaxRange;
        var n_columns = 7;
        var n_bytes = 4;  // astype np.float32 on server side
        var stride = n_columns * n_bytes;
        var view = new DataView(buffer);
        var numPoints = buffer.byteLength / stride;
        pcPtRange += numPoints;
        pcPtRange = Math.min(pcPtRange, pcMaxRange);

        var position = geometry.attributes.position.array
        var intensities = geometry.attributes.intensities.array
        var moments = geometry.attributes.moments.array

        for(var i = 0; i < numPoints; i++){
            position[pcNextI*3 + 0] = view.getFloat32(i*stride + 0 * n_bytes, true);
            position[pcNextI*3 + 1] = view.getFloat32(i*stride + 1 * n_bytes, true);
            position[pcNextI*3 + 2] = view.getFloat32(i*stride + 2 * n_bytes, true);
            intensities[pcNextI + 0] = view.getFloat32(i*stride + 3 * n_bytes, true);
            moments[pcNextI*3 + 0] = view.getFloat32(i*stride + 4 * n_bytes, true);
            moments[pcNextI*3 + 1] = view.getFloat32(i*stride + 5 * n_bytes, true);
            moments[pcNextI*3 + 2] = view.getFloat32(i*stride + 6 * n_bytes, true);
            pointCloud.incrementPcNextI(pcNextI, pcMaxRange)
        }
        updatePointCloudColors(geometry, 'position', 2);
        geometry.attributes.position.needsUpdate = true;
        geometry.setDrawRange(0, pcPtRange);

        // try to free some of this memory
        buffer = null;
        view = null;

        // Ask for the next set of data
        if (wsStreaming){
            ws.send('sendData');
// 					setTimeout(ws.send('stream'), 1000);
        }

    }
}

divIDE.registerPanel(pointCloud);
<|endoftext|>


<commit_before>class SpaceBalls {
    constructor(canvasID) {
        let canvas = document.getElementById(canvasID);
        this.engine = new BABYLON.Engine(canvas, true, {stencil: true});
        this.scene = new BABYLON.Scene(this.engine);
        this.score = new Score();
        this.level = 0;
        this.lastFrame = performance.now();
        this.isPaused = true;

        let skySphere = new SkySphere(2000000000000000000, '/../images/StarField/mw.jpg', this.scene);
        this.music = new BABYLON.Sound("background", "sounds/background.mp3", this.scene, null, { loop: true, autoplay: true});

        this.camera = new BABYLON.FreeCamera('Camera', new BABYLON.Vector3(0, 0, -10), this.scene);
        this.camera.speed = 4;
        this.camera.setTarget(new BABYLON.Vector3(0, 0, 20));
        this.camera.maxZ = 2000000000000000000000;

        this.player = new Player('/../images/gameAssets/player.png', this.scene, this.camera, 0.1);

        window.addEventListener('resize', () => this.engine.resize());
        window.addEventListener('blur', () => {
            this.isPaused = true;
            this.music.pause();
        });
        canvas.addEventListener('click', () => {
            if(this.player.isHit) {
                this.scene.meshes.splice(2, this.scene.meshes.length - 2);
                this.player.resetPosition();
            }
            this.isPaused = false;
            if(this.music.isPaused) {
                this.music.play();
            }
        });

        configEvents(this);

        setInterval(() => {
            if(!this.isPaused && this.player.alive) {
                let minZ = this.camera.position.z + 500,
                    maxZ = this.camera.position.z + 1500,
                    minX = this.camera.position.x - 500,
                    maxX = this.camera.position.x + 500,
                    minY = this.camera.position.y - 300,
                    maxY = this.camera.position.y + 300;

                new Meteor(randomNum(minX, maxX), randomNum(minY, maxY), randomNum(minZ, maxZ),
                    '/../images/gameAssets/meteor1.jpg', this.scene, this.player);
            }
        }, 200);

        this.engine.displayLoadingUI();
        this.engine.loadingUIText = 'Loading components...';
        this.scene.executeWhenReady(() => {
            window.dispatchEvent(new Event('gameLoaded'));
            this.engine.hideLoadingUI();
        });
    }
    render(){
        this.scene.beforeRender = () => {
            let currFrame = performance.now();
            let deltaT = (currFrame - this.lastFrame) / 100;
            this.lastFrame = currFrame;

            if(!this.isPaused) {
                if(this.player.alive) {
                    this.score.increaseScore(deltaT);
                    let fullNumScore = Math.round(this.score.score);
                    if(fullNumScore % 150 === 0 && fullNumScore !== 0) {
                        this.score.score = fullNumScore + 1;
                        window.dispatchEvent(new Event('levelUp'));
                    }
                    this.player.move(deltaT);
                }
                else {
                    window.dispatchEvent(new Event('gameOver'));
                }
            }
        };

        this.engine.runRenderLoop(() => this.scene.render());
    }
    resetGame() {
        this.level = 0;
        this.scene.meshes.splice(2, this.scene.meshes.length);
        this.score.score = 0;
        this.isPaused = true;
        window.dispatchEvent(new Event('reset'));
    }
}
<commit_msg>Update SpaceBalls.js<commit_after>class SpaceBalls {
    constructor(canvasID) {
        let canvas = document.getElementById(canvasID);
        this.engine = new BABYLON.Engine(canvas, true, {stencil: true});
        this.scene = new BABYLON.Scene(this.engine);
        this.score = new Score();
        this.level = 0;
        this.lastFrame = performance.now();
        this.isPaused = true;

        let skySphere = new SkySphere(2000000000000000000, '/../images/StarField/mw.jpg', this.scene);
        this.music = new BABYLON.Sound("background", "sounds/background.mp3", this.scene, null, { loop: true, autoplay: true});

        this.camera = new BABYLON.FreeCamera('Camera', new BABYLON.Vector3(0, 0, -10), this.scene);
        this.camera.speed = 4;
        this.camera.setTarget(new BABYLON.Vector3(0, 0, 20));
        this.camera.maxZ = 2000000000000000000000;

        this.player = new Player('/../images/gameAssets/player.png', this.scene, this.camera, 0.1);

        window.addEventListener('resize', () => this.engine.resize());
        window.addEventListener('blur', () => {
            this.isPaused = true;
            this.music.pause();
        });
        window.addEventListener("keydown", (e) => {
            if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
                e.preventDefault();
            }
        }, false);
        canvas.addEventListener('click', () => {
            if(this.player.isHit) {
                this.scene.meshes.splice(2, this.scene.meshes.length - 2);
                this.player.resetPosition();
            }
            this.isPaused = false;
            if(this.music.isPaused) {
                this.music.play();
            }
        });

        configEvents(this);

        setInterval(() => {
            if(!this.isPaused && this.player.alive) {
                let minZ = this.camera.position.z + 500,
                    maxZ = this.camera.position.z + 1500,
                    minX = this.camera.position.x - 500,
                    maxX = this.camera.position.x + 500,
                    minY = this.camera.position.y - 300,
                    maxY = this.camera.position.y + 300;

                new Meteor(randomNum(minX, maxX), randomNum(minY, maxY), randomNum(minZ, maxZ),
                    '/../images/gameAssets/meteor1.jpg', this.scene, this.player);
            }
        }, 200);

        this.engine.displayLoadingUI();
        this.engine.loadingUIText = 'Loading components...';
        this.scene.executeWhenReady(() => {
            window.dispatchEvent(new Event('gameLoaded'));
            this.engine.hideLoadingUI();
        });
    }
    render(){
        this.scene.beforeRender = () => {
            let currFrame = performance.now();
            let deltaT = (currFrame - this.lastFrame) / 100;
            this.lastFrame = currFrame;

            if(!this.isPaused) {
                if(this.player.alive) {
                    this.score.increaseScore(deltaT);
                    let fullNumScore = Math.round(this.score.score);
                    if(fullNumScore % 150 === 0 && fullNumScore !== 0) {
                        this.score.score = fullNumScore + 1;
                        window.dispatchEvent(new Event('levelUp'));
                    }
                    this.player.move(deltaT);
                }
                else {
                    window.dispatchEvent(new Event('gameOver'));
                }
            }
        };

        this.engine.runRenderLoop(() => this.scene.render());
    }
    resetGame() {
        this.level = 0;
        this.scene.meshes.splice(2, this.scene.meshes.length);
        this.score.score = 0;
        this.isPaused = true;
        window.dispatchEvent(new Event('reset'));
    }
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Pavel Aksonov
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */
import { assert } from './Util';

function getStateFromScenes(route, scenes, props) {
  const getters = [];
  let result = {};
  let scene = route;
  while (scene) {
    if (scene.getInitialState) {
      getters.push(scene.getInitialState);
    }
    scene = scenes[scene.parent];
  }

  if (scenes.rootProps && scenes.rootProps.getInitialState) {
    getters.push(scenes.rootProps.getInitialState);
  }

  getters.reverse().forEach(fn => {
    result = { ...result, ...fn(props) };
  });

  return result;
}

export function getInitialState(
  route: {string: any},
  scenes: {string: any},
  position = 0,
  props = {}
) {
  // eslint-disable-next-line no-unused-vars
  const { key, style, type, ...parentProps } = props;
  if (!route.children) {
    return {
      ...scenes.rootProps,
      ...route,
      key: `${position}_${route.sceneKey}`,
      ...parentProps,
      ...getStateFromScenes(route, scenes, props),
    };
  }
  const res = { ...route, ...scenes.rootProps, ...parentProps };
  let index = 0;
  route.children.forEach((r, i) => {
    assert(scenes[r], `Empty scene for key=${route.key}`);
    if (scenes[r].initial) {
      index = i;
    }
  });

  if (route.tabs) {
    res.children = route.children.map((r, i) => getInitialState(scenes[r], scenes, i, props));
    res.index = index;
  } else {
    res.children = [getInitialState(scenes[route.children[index]], scenes, 0, props)];
    res.index = 0;
  }
  res.key = `${position}_${res.key}`;
  return res;
}

export default function (scenes:{string: any}) {
  // find "root" component and get state from it
  const rootRoute = Object.keys(scenes).find((route) =>
    scenes.hasOwnProperty(route) && !scenes[route].parent);

  return getInitialState(scenes[rootRoute], scenes);
}
<commit_msg>revert 495d2835e7e7b850da8824c06ebed4e326a7816f<commit_after>/**
 * Copyright (c) 2015-present, Pavel Aksonov
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */
import { assert } from './Util';

function getStateFromScenes(route, scenes, props) {
  const getters = [];
  let result = {};
  let scene = route;
  while (scene) {
    if (scene.getInitialState) {
      getters.push(scene.getInitialState);
    }
    scene = scenes[scene.parent];
  }

  if (scenes.rootProps && scenes.rootProps.getInitialState) {
    getters.push(scenes.rootProps.getInitialState);
  }

  getters.reverse().forEach(fn => {
    result = { ...result, ...fn(props) };
  });

  return result;
}

export function getInitialState(
  route: {string: any},
  scenes: {string: any},
  position = 0,
  props = {}
) {
  // eslint-disable-next-line no-unused-vars
  const { key, style, type, ...parentProps } = props;
  if (!route.children) {
    return {
      ...scenes.rootProps,
      ...route,
      key: `${position}_${route.sceneKey}`,
      ...parentProps,
      ...getStateFromScenes(route, scenes, props),
    };
  }
  const res = { ...route, ...parentProps };
  let index = 0;
  route.children.forEach((r, i) => {
    assert(scenes[r], `Empty scene for key=${route.key}`);
    if (scenes[r].initial) {
      index = i;
    }
  });

  if (route.tabs) {
    res.children = route.children.map((r, i) => getInitialState(scenes[r], scenes, i, props));
    res.index = index;
  } else {
    res.children = [getInitialState(scenes[route.children[index]], scenes, 0, props)];
    res.index = 0;
  }
  res.key = `${position}_${res.key}`;
  return res;
}

export default function (scenes:{string: any}) {
  // find "root" component and get state from it
  const rootRoute = Object.keys(scenes).find((route) =>
    scenes.hasOwnProperty(route) && !scenes[route].parent);

  return getInitialState(scenes[rootRoute], scenes);
}
<|endoftext|>


<commit_before><commit_msg>Update package-publish with new filesystem changes<commit_after><|endoftext|>


<commit_before>var casper = require('casper').create();
var fs = require('fs');
var baseUrl = 'http://localhost:5000';
var protocol = 'http://';
var ajaxes = [];
var resources = [];
var getDirectory = /^(.+)\/([^\/]+)$/;
var stripUrlParams = /(.+?)(\?.*)$/;
var caputuring = true;

function buildFauxJax() {
    casper.echo('Mocking AJAX...')
    if (ajaxes.length > 0) {
        var fauxJax = fs.open('jax.js', 'w');

        fauxJax.write('(function() {\n');
        var temp;
        for (var i = 0; i < ajaxes.length; i++) {
            casper.echo('Cloning request to ' + ajaxes[i] + ' (' + (i+1) + '/' + ajaxes.length + ')');
            if (ajaxes[i].indexOf('?') != -1)
                temp = ajaxes[i].substring(0, ajaxes[i].indexOf('?')).replace(baseUrl, '');
            else
                temp = ajaxes[i].replace(baseUrl, '');
            fauxJax.write('$.mockjax({\nurl: \'' + temp + '\',\ndataType: \'json\',\nresponseText: ');

            fauxJax.write(this.evaluate(function(ajax) {
                return __utils__.sendAJAX(ajax);
            }, ajaxes[i]));

            fauxJax.write('\n});');
        }

        fauxJax.write('})();');
        fauxJax.close();
        casper.echo('Finished mocking.')
        return true;
    }
    casper.echo('Finished mocking.')
    return false;
};

function saveResources() {
    casper.echo('Saving resources...');
    for (var i = 0; i < resources.length; i++) {
        this.download(resources[i][0], resources[i][1]);
        casper.echo('Saved ' + resources[i][1] + ' (' + (i+1) + '/' + resources.length + ')');
    }
    casper.echo('Finished saving resources.');
}

casper.start(baseUrl);

casper.then(function() {
    var html = fs.open('page.html', 'w');
    var src = this.evaluate(function(url) {
        return __utils__.sendAJAX(url);
    }, baseUrl);
    src = src.replace(/\"(?:\/\/|["\/]+)*\//g, "\"./");
    html.write(src);
    if (buildFauxJax.call(this))
        html.write('<script src="./jquery.mockjax.js"></script>\n<script src="./jax.js"></script>');
    html.close();
    caputuring = false;
    saveResources.call(this);
    this.echo('Finished cloning ' + baseUrl);
    this.exit();
});

casper.on('resource.requested', function(resource) {
    if (caputuring) {
        //Or up here
        if (resource.url.indexOf('.') == -1 && resource.url != baseUrl + '/' && ajaxes.indexOf(resource.url) == -1) {
            casper.echo('Pushed ' +  resource.url + ' to ajax queue.');
            //TODO regex parse urls here.
            ajaxes.push(resource.url);
        } else if (resources.indexOf(resource) == -1 && resource.url != baseUrl + '/') {
            //TODO fix resource duplication bug
            resource.url = resource.url.replace(stripUrlParams, '$1');
            resources.push([resource.url, resource.url.replace(/^(?:\/\/|[^\/]+)*\//, "./"), getDirectory.exec(resource.url.replace(/^(?:\/\/|[^\/]+)*\//, "./"))[1] + '/']);
        }
    }
});

if (!fs.exists(baseUrl.replace(protocol, '')))
    fs.makeDirectory(baseUrl.replace(protocol, ''));
fs.changeWorkingDirectory(baseUrl.replace(protocol, ''));

casper.run();
<commit_msg>Single page mostly completed, moving into a single function.<commit_after>var casper = require('casper').create();
var fs = require('fs');
var baseUrl = 'http://localhost:5000/62w3d/'; //The starting url should really only be http://localhost:500/
var protocol, domain;

var ajaxes = [];
var resources = [];
var getDirectory = /^(.+)\/([^\/]+)$/;
var stripUrlParams = /(.+?)(\?.*)$/;
var caputuring = true;

function parseUrl(url) {
    var matches = /^(.+:\/\/)(.+?)\/(.*)$/.exec(url);
    protocol = matches[1];
    domain = matches[2];
}

function getSaveName(url) {
    var save = url;
    url = url.replace(protocol + domain, '');
    if(url.charAt(url.length-1) === '/')
        url += 'index.html';
    casper.echo('Saving ' + save + ' as .' + url);
    return '.' + url;
}

function buildFauxJax() {
    casper.echo('Mocking AJAX...')
    if (ajaxes.length > 0) {
        var fauxJax = fs.open('.' + baseUrl.replace(protocol + domain, '') + 'fauxJax.js', 'w');

        fauxJax.write('(function() {\n');

        for (var i = 0; i < ajaxes.length; i++) {
            casper.echo('Mocking request to ' + ajaxes[i] + ' (' + (i+1) + '/' + ajaxes.length + ')');
            fauxJax.write('$.mockjax({\nurl: \'' + ajaxes[i] + '\',\ndataType: \'json\',\nresponseText: ');

            fauxJax.write(this.evaluate(function(ajax) {
                return __utils__.sendAJAX(ajax);
            }, ajaxes[i]));

            fauxJax.write('\n});');
        }

        fauxJax.write('})();');
        fauxJax.close();
        casper.echo('Finished mocking.')
        return true;
    }
    casper.echo('Finished mocking.')
    return false;
};

function saveResources() {
    casper.echo('Saving resources...');
    for (var i = 0; i < resources.length; i++) {
        this.download(resources[i][0], resources[i][1]);
        casper.echo('Saved ' + resources[i][1] + ' (' + (i+1) + '/' + resources.length + ')');
    }
    casper.echo('Finished saving resources.');
}

function cloneUrl(url)
{

}

casper.start(baseUrl);

casper.then(function() {
    var html = fs.open(getSaveName(baseUrl), 'w');
    caputuring = false;
    var src = this.evaluate(function(url) {
        return __utils__.sendAJAX(url);
    }, baseUrl);
    src = src.replace(/\"(?:\/\/|["\/]+)*\//g, '"' + fs.workingDirectory + '/');
    html.write(src);
    if (buildFauxJax.call(this))
        html.write('<script src="' + fs.workingDirectory + '/static/js/jquery.mockjax.js"></script>\n<script src="./fauxJax.js"></script>');
    html.close();
    saveResources.call(this);
    this.echo('Finished cloning ' + baseUrl);
    this.exit();
});

casper.on('resource.requested', function(resource) {
    if (caputuring) {
        //Or up here
        if (resource.url.indexOf('.') == -1 && resource.url != baseUrl + '/' && resource.url != baseUrl && ajaxes.indexOf(resource.url) == -1) {
            resource.url = resource.url.replace(stripUrlParams, '$1').replace(protocol + domain, '');
            casper.echo('Pushed ' +  resource.url + ' to ajax queue.');
            //TODO regex parse urls here.
            ajaxes.push(resource.url);
        } else if (resources.indexOf(resource) == -1 && resource.url != baseUrl + '/') {
            //TODO fix resource duplication bug
            resource.url = resource.url.replace(stripUrlParams, '$1');
            resources.push([resource.url, resource.url.replace(/^(?:\/\/|[^\/]+)*\//, "./"), getDirectory.exec(resource.url.replace(/^(?:\/\/|[^\/]+)*\//, "./"))[1] + '/']);
        }
    }
});

parseUrl(baseUrl);

//Creating the base directory
if (!fs.exists(domain))
    fs.makeDirectory(domain);
fs.changeWorkingDirectory(domain);

casper.run();
<|endoftext|>


<commit_before>(function($) {

  $.fn.extend({

    // Align an element relative to a target element's coordinates. Forces the
    // element to be absolutely positioned. Element must be visible.
    // Position string format is: "top -right".
    // You can pass an optional offset object with top and left offsets specified.
    align : function(target, pos, offset) {
      var el = this;
      pos = pos || '';
      offset = offset || {};
      var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0;
      var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || 0;
      var clientWidth = document.documentElement.clientWidth;
      var clientHeight = document.documentElement.clientHeight;

      if (target == window) {
        var b = {
          left    : scrollLeft,
          top     : scrollTop,
          width   : $(window).width(),
          height  : $(window).height()
        };
      } else {
        target = $(target);
        var targOff = target.offset();
        var b = {
          left    : targOff.left,
          top     : targOff.top,
          width   : target.innerWidth(),
          height  : target.innerHeight()
        };
      }

      var elb = {
        width : el.innerWidth(),
        height : el.innerHeight()
      };

      var left, top;

      if (pos.indexOf('-left') >= 0) {
        left = b.left;
      } else if (pos.indexOf('left') >= 0) {
        left = b.left - elb.width;
      } else if (pos.indexOf('-right') >= 0) {
        left = b.left + b.width - elb.width;
      } else if (pos.indexOf('right') >= 0) {
        left = b.left + b.width;
      } else { // Centered.
        left = b.left + (b.width - elb.width) / 2;
      }

      if (pos.indexOf('-top') >= 0) {
        top = b.top;
      } else if (pos.indexOf('top') >= 0) {
        top = b.top - elb.height;
      } else if (pos.indexOf('-bottom') >= 0) {
        top = b.top + b.height - elb.height;
      } else if (pos.indexOf('bottom') >= 0) {
        top = b.top + b.height;
      } else { // Centered.
        top = b.top + (b.height - elb.height) / 2;
      }

      var constrain = (pos.indexOf('no-constraint') >= 0) ? false : true;

      left += offset.left || 0;
      top += offset.top || 0;

      if (constrain) {
        left = Math.max(scrollLeft, Math.min(left, scrollLeft + clientWidth - elb.width));
        top = Math.max(scrollTop, Math.min(top, scrollTop + clientHeight - elb.height));
      }

      // var offParent;
      // if (offParent = el.offsetParent()) {
      //   left -= offParent.offset().left;
      //   top -= offParent.offset().top;
      // }

      $(el).css({position : 'absolute', left : left + 'px', top : top + 'px'});
      return el;
    },

    // See Backbone.View#setMode...
    setMode : function(state, group) {
      group = group || 'mode';
      var re = new RegExp("\\w+_" + group + "(\\s|$)", 'g');
      var mode = (state === null) ? "" : state + "_" + group;
      this.each(function(){
        this.className = (this.className.replace(re, '') + ' ' + mode).replace(/\s\s/g, ' ');
      });
      return mode;
    },

    // A-la serializeArray but returns a hash instead of a list.
    serializeJSON : function() {
      return _.reduce(this.serializeArray(), function(hash, pair) {
        hash[pair.name] = pair.value;
        return hash;
      }, {});
    },

    // When the next click or keypress happens, anywhere on the screen, hide the
    // element. 'clickable' makes the element and its contents clickable without
    // hiding. The 'onHide' callback runs when the hide fires, and has a chance
    // to cancel it.
    autohide : function(options) {
      var me = this;
      options = _.extend({clickable : null, onHide : null}, options || {});
      me._autoignore = true;
      setTimeout(function(){ delete me._autoignore; }, 0);

      if (!me._autohider) {
        me.forceHide = function(e) {
          if (!e && options.onHide) options.onHide();
          me.hide();
          $(document).unbind('click', me._autohider);
          $(document).unbind('keypress', me._autohider);
          me._autohider = null;
          me.forceHide = null;
        };
        me._autohider = function(e) {
          if (me._autoignore) return;
          if (options.clickable && (me[0] == e.target || _.include($(e.target).parents(), me[0]))) return;
          if (options.onHide && !options.onHide(e)) return;
          me.forceHide(e);
        };
        $(document).bind('click', this._autohider);
        $(document).bind('keypress', this._autohider);
      }
    },

    draggable : function(options) {
      options || (options = {});
      this.each(function() {
        var el    = this;
        var ghost = null, xOff = null, yOff = null;

        var stopEvent = function(e) {
          e.stopPropagation();
          e.preventDefault();
          return false;
        };

        var checkEvent = function(e) {
          var isNonDraggable = $(e.target).is('input, select, textarea, label, a, canvas, .minibutton, .text_link, .selectable_text, .not_draggable');
          if (isNonDraggable) return true;

          var draggableParent = $(e.target).parents('.is_draggable').andSelf().length;
          if (!draggableParent) return true;

          return stopEvent(e);
        };

        var drag = _.bind(function(e) {
          if (!el._drag) return stopEvent(e);
          if (ghost) {
            ghost.css({top : e.pageY - yOff, left : e.pageX - xOff});
          } else {
            el.style.left = el._drag.left + e.pageX - el._drag.x + 'px';
            el.style.top  = el._drag.top + e.pageY - el._drag.y + 'px';
          }
        }, el);

        var dragEnd = _.bind(function(e) {
          $(document.body).unbind('selectstart', stopEvent);
          $(document.body).unbind('mouseup', dragEnd);
          $(document.body).unbind('mousemove', drag);
          $(ghost || el).removeClass('dragging');
          if (ghost) $(ghost).remove();
          el._drag = null;
          if (options.onDrop) options.onDrop(e);
        }, el);

        var dragStart = _.bind(function(e) {
          if (checkEvent(e)) return true;
          if (options.ghost) {
            xOff  = $(el).width() / 2;
            yOff  = $(el).height() / 2;
            ghost = $(el).clone().css({
              position : 'absolute', cursor : 'copy', 'z-index' : 1000, top : e.pageY - yOff, left : e.pageX - xOff
            }).appendTo(document.body);
          }
          $(ghost || el).addClass('dragging');
          el._drag = {
            left : parseInt(el.style.left, 10) || 0,
            top  : parseInt(el.style.top, 10) || 0,
            x    : e.pageX,
            y    : e.pageY
          };
          $(document.body).bind('selectstart', stopEvent);
          $(document.body).bind('mouseup', dragEnd);
          $(document.body).bind('mousemove', drag);
        }, el);

        $(el).bind(options.ghost ? 'dragstart' : 'mousedown', dragStart);
      });
    },

    // TODO: share common bits of this with the annotation_editor.
    selectable : function(options) {
      var doc  = $(document);
      var edge = 17;
      var selection = $('.selection');

      $(this).bind('mousedown', _.bind(function(e) {
        if (options.ignore && $(e.target).closest(options.ignore).length) return;
        var off = this.offset();
        if ((e.pageX > off.left + this.outerWidth() - edge) ||
            (e.pageY > off.top + this.outerHeight() - edge)) return;
        e.preventDefault();
        if (dc.app.searchBox) dc.app.searchBox.blur();
        var targets = $(options.select);
        var scrTop  = this.scrollTop(), scrLeft = this.scrollLeft();
        var ox = e.pageX, oy = e.pageY;
        var coords = function(e) {
          return {
            left    : Math.min(ox, e.pageX),
            top     : Math.min(oy, e.pageY),
            width   : Math.abs(e.pageX - ox),
            height  : Math.abs(e.pageY - oy)
          };
        };
        var selectTargets = function(e) {
          if (e.pageX == ox && e.pageY == oy) return;
          var pos = coords(e);
          var x1 = pos.left + scrLeft, y1 = pos.top + scrTop, x2 = x1 + pos.width, y2 = y1 + pos.height;
          var hits = [];
          for (var i=0; i<targets.length; i++) {
            var el = $(targets[i]);
            pos = el.offset();
            var ex1 = pos.left + scrLeft, ey1 = pos.top + scrTop, ex2 = ex1 + el.outerWidth(), ey2 = ey1 + el.outerHeight();
            if (!(x1 > ex2 || x2 < ex1 || y1 > ey2 || y2 < ey1)) hits.push(el);
          }
          if (options.onSelect) options.onSelect(hits);
        };
        var drag = _.bind(function(e) {
          e.stopPropagation();
          e.preventDefault();
          selection.show().css(coords(e));
          selectTargets(e);
        }, this);
        var dragEnd = _.bind(function(e) {
          e.stopPropagation();
          e.preventDefault();
          doc.unbind('mouseup', dragEnd).unbind('mousemove', drag);
          selectTargets(e);
          selection.hide();
        }, this);
        doc.bind('mouseup', dragEnd).bind('mousemove', drag);
      }, this));
    },

    // jQuery's default text() method doesn't play nice with contentEditable
    // elements, which insert divs or paras instead of newline characters.
    // Convert them properly.
    textWithNewlines: function( text ) {
      var ret = "";
      $.each( text || this, function(){
        $.each( this.childNodes, function() {
          if ( this.nodeType != 8 ) {
            var tagName = this.tagName && this.tagName.toLowerCase();
            var isBreak = tagName == 'div' || tagName == 'p';
            if (isBreak && !_.include(["\n", ''], ret.charAt(ret.length - 1))) ret += "\n";
            ret += this.nodeType != 1 ? this.nodeValue : $.fn.textWithNewlines([this]);
            if (isBreak) ret += "\n";
          }
        });
      });
      return ret;
    },

    placeholder: function(opts) {
      var defaults = {
        message: '...',
        className: 'placeholder',
        clearClassName: 'show_cancel_search'
      };
      var options = $.extend({}, defaults, opts);

      var setPlaceholder = function($input) {
        $input.val($input.attr('placeholder') || options.message);
        $input.addClass(options.className);
      };

      return this.each(function() {
        var $this = $(this);

        if ($this.attr('type') == 'search') return;

        $this.bind('blur', function() {
          if ($this.val() == '') {
            setPlaceholder($this);
          }
        }).bind('focus', function() {
          if ($this.val() == ($this.attr('placeholder') || options.message)) {
            $this.val('');
          }
          $this.removeClass(options.className);
        }).bind('keyup', function() {
          var searchVal = $this.val();
          if (searchVal != '' && searchVal != options.message) {
            $this.parent().addClass(options.clearClassName);
          } else {
            $this.parent().removeClass(options.clearClassName);
          }
        });
        _.defer(function(){
          $this.keyup().blur();
        });
      });
    }

  });

  // Add mousewheel support:
  var types = ['DOMMouseScroll', 'mousewheel'];

  var mouseWheelHandler = function(event) {
      var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;
      event = $.event.fix(event || window.event);
      event.type = "mousewheel";
      if (event.wheelDelta) {
        delta = event.wheelDelta / 5;
      } else if (event.detail) {
        delta = -event.detail * 5;
      }
      args.unshift(event, delta);
      return $.event.handle.apply(this, args);
  };

  $.event.special.mousewheel = {
    setup: function() {
      if (this.addEventListener) {
        for (var i=types.length; i;) this.addEventListener(types[--i], mouseWheelHandler, false);
      } else {
        this.onmousewheel = mouseWheelHandler;
      }
    },
    teardown: function() {
      if (this.removeEventListener) {
        for (var i=types.length; i;) this.removeEventListener(types[--i], mouseWheelHandler, false);
      } else {
        this.onmousewheel = null;
      }
    }
  };

})(jQuery);<commit_msg>removing unused bits of the placeholder patch.<commit_after>(function($) {

  $.fn.extend({

    // Align an element relative to a target element's coordinates. Forces the
    // element to be absolutely positioned. Element must be visible.
    // Position string format is: "top -right".
    // You can pass an optional offset object with top and left offsets specified.
    align : function(target, pos, offset) {
      var el = this;
      pos = pos || '';
      offset = offset || {};
      var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0;
      var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || 0;
      var clientWidth = document.documentElement.clientWidth;
      var clientHeight = document.documentElement.clientHeight;

      if (target == window) {
        var b = {
          left    : scrollLeft,
          top     : scrollTop,
          width   : $(window).width(),
          height  : $(window).height()
        };
      } else {
        target = $(target);
        var targOff = target.offset();
        var b = {
          left    : targOff.left,
          top     : targOff.top,
          width   : target.innerWidth(),
          height  : target.innerHeight()
        };
      }

      var elb = {
        width : el.innerWidth(),
        height : el.innerHeight()
      };

      var left, top;

      if (pos.indexOf('-left') >= 0) {
        left = b.left;
      } else if (pos.indexOf('left') >= 0) {
        left = b.left - elb.width;
      } else if (pos.indexOf('-right') >= 0) {
        left = b.left + b.width - elb.width;
      } else if (pos.indexOf('right') >= 0) {
        left = b.left + b.width;
      } else { // Centered.
        left = b.left + (b.width - elb.width) / 2;
      }

      if (pos.indexOf('-top') >= 0) {
        top = b.top;
      } else if (pos.indexOf('top') >= 0) {
        top = b.top - elb.height;
      } else if (pos.indexOf('-bottom') >= 0) {
        top = b.top + b.height - elb.height;
      } else if (pos.indexOf('bottom') >= 0) {
        top = b.top + b.height;
      } else { // Centered.
        top = b.top + (b.height - elb.height) / 2;
      }

      var constrain = (pos.indexOf('no-constraint') >= 0) ? false : true;

      left += offset.left || 0;
      top += offset.top || 0;

      if (constrain) {
        left = Math.max(scrollLeft, Math.min(left, scrollLeft + clientWidth - elb.width));
        top = Math.max(scrollTop, Math.min(top, scrollTop + clientHeight - elb.height));
      }

      // var offParent;
      // if (offParent = el.offsetParent()) {
      //   left -= offParent.offset().left;
      //   top -= offParent.offset().top;
      // }

      $(el).css({position : 'absolute', left : left + 'px', top : top + 'px'});
      return el;
    },

    // See Backbone.View#setMode...
    setMode : function(state, group) {
      group = group || 'mode';
      var re = new RegExp("\\w+_" + group + "(\\s|$)", 'g');
      var mode = (state === null) ? "" : state + "_" + group;
      this.each(function(){
        this.className = (this.className.replace(re, '') + ' ' + mode).replace(/\s\s/g, ' ');
      });
      return mode;
    },

    // A-la serializeArray but returns a hash instead of a list.
    serializeJSON : function() {
      return _.reduce(this.serializeArray(), function(hash, pair) {
        hash[pair.name] = pair.value;
        return hash;
      }, {});
    },

    // When the next click or keypress happens, anywhere on the screen, hide the
    // element. 'clickable' makes the element and its contents clickable without
    // hiding. The 'onHide' callback runs when the hide fires, and has a chance
    // to cancel it.
    autohide : function(options) {
      var me = this;
      options = _.extend({clickable : null, onHide : null}, options || {});
      me._autoignore = true;
      setTimeout(function(){ delete me._autoignore; }, 0);

      if (!me._autohider) {
        me.forceHide = function(e) {
          if (!e && options.onHide) options.onHide();
          me.hide();
          $(document).unbind('click', me._autohider);
          $(document).unbind('keypress', me._autohider);
          me._autohider = null;
          me.forceHide = null;
        };
        me._autohider = function(e) {
          if (me._autoignore) return;
          if (options.clickable && (me[0] == e.target || _.include($(e.target).parents(), me[0]))) return;
          if (options.onHide && !options.onHide(e)) return;
          me.forceHide(e);
        };
        $(document).bind('click', this._autohider);
        $(document).bind('keypress', this._autohider);
      }
    },

    draggable : function(options) {
      options || (options = {});
      this.each(function() {
        var el    = this;
        var ghost = null, xOff = null, yOff = null;

        var stopEvent = function(e) {
          e.stopPropagation();
          e.preventDefault();
          return false;
        };

        var checkEvent = function(e) {
          var isNonDraggable = $(e.target).is('input, select, textarea, label, a, canvas, .minibutton, .text_link, .selectable_text, .not_draggable');
          if (isNonDraggable) return true;

          var draggableParent = $(e.target).parents('.is_draggable').andSelf().length;
          if (!draggableParent) return true;

          return stopEvent(e);
        };

        var drag = _.bind(function(e) {
          if (!el._drag) return stopEvent(e);
          if (ghost) {
            ghost.css({top : e.pageY - yOff, left : e.pageX - xOff});
          } else {
            el.style.left = el._drag.left + e.pageX - el._drag.x + 'px';
            el.style.top  = el._drag.top + e.pageY - el._drag.y + 'px';
          }
        }, el);

        var dragEnd = _.bind(function(e) {
          $(document.body).unbind('selectstart', stopEvent);
          $(document.body).unbind('mouseup', dragEnd);
          $(document.body).unbind('mousemove', drag);
          $(ghost || el).removeClass('dragging');
          if (ghost) $(ghost).remove();
          el._drag = null;
          if (options.onDrop) options.onDrop(e);
        }, el);

        var dragStart = _.bind(function(e) {
          if (checkEvent(e)) return true;
          if (options.ghost) {
            xOff  = $(el).width() / 2;
            yOff  = $(el).height() / 2;
            ghost = $(el).clone().css({
              position : 'absolute', cursor : 'copy', 'z-index' : 1000, top : e.pageY - yOff, left : e.pageX - xOff
            }).appendTo(document.body);
          }
          $(ghost || el).addClass('dragging');
          el._drag = {
            left : parseInt(el.style.left, 10) || 0,
            top  : parseInt(el.style.top, 10) || 0,
            x    : e.pageX,
            y    : e.pageY
          };
          $(document.body).bind('selectstart', stopEvent);
          $(document.body).bind('mouseup', dragEnd);
          $(document.body).bind('mousemove', drag);
        }, el);

        $(el).bind(options.ghost ? 'dragstart' : 'mousedown', dragStart);
      });
    },

    // TODO: share common bits of this with the annotation_editor.
    selectable : function(options) {
      var doc  = $(document);
      var edge = 17;
      var selection = $('.selection');

      $(this).bind('mousedown', _.bind(function(e) {
        if (options.ignore && $(e.target).closest(options.ignore).length) return;
        var off = this.offset();
        if ((e.pageX > off.left + this.outerWidth() - edge) ||
            (e.pageY > off.top + this.outerHeight() - edge)) return;
        e.preventDefault();
        if (dc.app.searchBox) dc.app.searchBox.blur();
        var targets = $(options.select);
        var scrTop  = this.scrollTop(), scrLeft = this.scrollLeft();
        var ox = e.pageX, oy = e.pageY;
        var coords = function(e) {
          return {
            left    : Math.min(ox, e.pageX),
            top     : Math.min(oy, e.pageY),
            width   : Math.abs(e.pageX - ox),
            height  : Math.abs(e.pageY - oy)
          };
        };
        var selectTargets = function(e) {
          if (e.pageX == ox && e.pageY == oy) return;
          var pos = coords(e);
          var x1 = pos.left + scrLeft, y1 = pos.top + scrTop, x2 = x1 + pos.width, y2 = y1 + pos.height;
          var hits = [];
          for (var i=0; i<targets.length; i++) {
            var el = $(targets[i]);
            pos = el.offset();
            var ex1 = pos.left + scrLeft, ey1 = pos.top + scrTop, ex2 = ex1 + el.outerWidth(), ey2 = ey1 + el.outerHeight();
            if (!(x1 > ex2 || x2 < ex1 || y1 > ey2 || y2 < ey1)) hits.push(el);
          }
          if (options.onSelect) options.onSelect(hits);
        };
        var drag = _.bind(function(e) {
          e.stopPropagation();
          e.preventDefault();
          selection.show().css(coords(e));
          selectTargets(e);
        }, this);
        var dragEnd = _.bind(function(e) {
          e.stopPropagation();
          e.preventDefault();
          doc.unbind('mouseup', dragEnd).unbind('mousemove', drag);
          selectTargets(e);
          selection.hide();
        }, this);
        doc.bind('mouseup', dragEnd).bind('mousemove', drag);
      }, this));
    },

    // jQuery's default text() method doesn't play nice with contentEditable
    // elements, which insert divs or paras instead of newline characters.
    // Convert them properly.
    textWithNewlines: function( text ) {
      var ret = "";
      $.each( text || this, function(){
        $.each( this.childNodes, function() {
          if ( this.nodeType != 8 ) {
            var tagName = this.tagName && this.tagName.toLowerCase();
            var isBreak = tagName == 'div' || tagName == 'p';
            if (isBreak && !_.include(["\n", ''], ret.charAt(ret.length - 1))) ret += "\n";
            ret += this.nodeType != 1 ? this.nodeValue : $.fn.textWithNewlines([this]);
            if (isBreak) ret += "\n";
          }
        });
      });
      return ret;
    },

    placeholder: function(opts) {
      var defaults = {
        message: '...',
        className: 'placeholder'
      };
      var options = $.extend({}, defaults, opts);

      var setPlaceholder = function($input) {
        $input.val($input.attr('placeholder') || options.message);
        $input.addClass(options.className);
      };

      return this.each(function() {
        var $this = $(this);

        if ($this.attr('type') == 'search') return;

        $this.bind('blur', function() {
          if ($this.val() == '') {
            setPlaceholder($this);
          }
        }).bind('focus', function() {
          if ($this.val() == ($this.attr('placeholder') || options.message)) {
            $this.val('');
          }
          $this.removeClass(options.className);
        });
        _.defer(function(){
          $this.keyup().blur();
        });
      });
    }

  });

  // Add mousewheel support:
  var types = ['DOMMouseScroll', 'mousewheel'];

  var mouseWheelHandler = function(event) {
      var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;
      event = $.event.fix(event || window.event);
      event.type = "mousewheel";
      if (event.wheelDelta) {
        delta = event.wheelDelta / 5;
      } else if (event.detail) {
        delta = -event.detail * 5;
      }
      args.unshift(event, delta);
      return $.event.handle.apply(this, args);
  };

  $.event.special.mousewheel = {
    setup: function() {
      if (this.addEventListener) {
        for (var i=types.length; i;) this.addEventListener(types[--i], mouseWheelHandler, false);
      } else {
        this.onmousewheel = mouseWheelHandler;
      }
    },
    teardown: function() {
      if (this.removeEventListener) {
        for (var i=types.length; i;) this.removeEventListener(types[--i], mouseWheelHandler, false);
      } else {
        this.onmousewheel = null;
      }
    }
  };

})(jQuery);<|endoftext|>


<commit_before>import { FS } from 'grind-support'
import { encodeHTML } from 'entities'

const acorn = require('acorn')
acorn.walk = require('acorn/dist/walk').simple

const astring = require('astring')

export class Stone {

	context = {
		escape: value => {
			if(value.isNil) {
				return ''
			}

			return encodeHTML(value.toString())
		}

	}

	async compile(template) {
		let contents = (await FS.readFile(template)).toString().trim()

		const expressions = [ ]
		let match = null

		while((match = contents.match(/@([a-z0-9_]+)(\s*\()?/))) {
			if(match.index > 0) {
				expressions.push({
					type: 'string',
					contents: contents.substring(0, match.index)
				})

				contents = contents.substring(match.index)
			}

			let args = null

			if(match[2]) {
				let openCount = -1
				let index = 0
				let startIndex = 0
				let lastIndex = 0

				while(openCount !== 0 && (index = indexOfParenthesis(contents, index)) >= 0) {
					const parenthesis = contents.substring(index, index + 1)

					if(parenthesis === ')') {
						openCount--
					} else if(openCount === -1) {
						startIndex = index
						openCount = 1
					} else {
						openCount++
					}

					lastIndex = index
					index++
				}

				args = contents.substring(startIndex + 1, lastIndex)
				contents = contents.substring(lastIndex + 1)
			} else {
				contents = contents.substring(match[0].length)
			}

			const method = `compile${match[1][0].toUpperCase()}${match[1].substring(1)}`
			const result = this[method](args)

			if(!result.isNil) {
				expressions.push({
					type: 'code',
					contents: result
				})
			}
		}

		contents = contents.trim()

		if(contents.length > 0) {
			expressions.push({
				type: 'string',
				contents: contents
			})
		}

		let code = ''

		for(const { type, contents } of expressions) {
			if(type === 'code') {
				code += `${contents}\n`
			} else {
				code += `output += \`${sanetizeString(contents)}\`;\n`
			}
		}

		code = `template = function(_) {\nlet output = '';\n${this._contextualize(code)}\nreturn output;\n}`

		return eval(code)
	}

	render(template, context) {
		return this.compile(template).then(template => template({ ...this.context, ...context }))
	}

	_contextualize(code) {
		const tree = acorn.parse(code)
		const scopes = [
			{
				locals: new Set,
				end: Number.MAX_VALUE
			}
		]

		let scope = scopes[0]

		const checkScope = fromNode => {
			while(fromNode.start >= scope.end && scopes.length > 1) {
				scopes.pop()
				scope = scopes[scopes.length - 1]
			}
		}

		acorn.walk(tree, {
			Statement: node => {
				checkScope(node)
			},

			BlockStatement: node => {
				checkScope(node)

				scope = {
					locals: new Set(scope.locals),
					node: node,
					end: node.end
				}

				scopes.push(scope)
			},

			VariableDeclarator: node => {
				checkScope(node)
				scope.locals.add(node.id.name)
			},

			Identifier: node => {
				checkScope(node)

				if(scope.locals.has(node.name)) {
					return
				}

				node.name = `_.${node.name}`
			}
		})

		return astring(tree)
	}

	compileIf(args) {
		return `if(${args}){`
	}

	compileElseif(args) {
		return `} else if(${args}){`
	}

	compileEndif() {
		return '}'
	}

}

/**
 * [indexOfParenthesis description]
 * @param  {string} string    [description]
 * @param  {number} fromIndex [description]
 * @return {number}           [description]
 */
function indexOfParenthesis(string, fromIndex) {
	const open = string.indexOf('(', fromIndex)
	const close = string.indexOf(')', fromIndex)

	if(open === -1) {
		return close
	} else if(close === -1)  {
		return open
	}

	return Math.min(open, close)
}

function sanetizeString(string) {
	string = string.replace(/\{\{\s*(.+?)\s*\}\}/g, '${escape($1)}')
	string = string.replace(/\{!!\s*(.+?)\s*!!\}/g, '${$1}')
	string = string.replace(/[\n]/g, '\\n')
	string = string.replace(/[\r]/g, '\\r')
	string = string.replace(/[\t]/g, '\\t')

	return string
}
<commit_msg>Added a bunch more control flow functions<commit_after>import { FS } from 'grind-support'
import { encodeHTML } from 'entities'

const acorn = require('acorn')
acorn.walk = require('acorn/dist/walk').simple

const astring = require('astring')

export class Stone {

	context = {
		escape: value => {
			if(value.isNil) {
				return ''
			}

			return encodeHTML(value.toString())
		}

	}

	async compile(template) {
		let contents = (await FS.readFile(template)).toString().trim()

		const expressions = [ ]
		let match = null

		while((match = contents.match(/@([a-z0-9_]+)(\s*\()?/))) {
			if(match.index > 0) {
				expressions.push({
					type: 'string',
					contents: contents.substring(0, match.index)
				})

				contents = contents.substring(match.index)
			}

			let args = null

			if(match[2]) {
				let openCount = -1
				let index = 0
				let startIndex = 0
				let lastIndex = 0

				while(openCount !== 0 && (index = indexOfParenthesis(contents, index)) >= 0) {
					const parenthesis = contents.substring(index, index + 1)

					if(parenthesis === ')') {
						openCount--
					} else if(openCount === -1) {
						startIndex = index
						openCount = 1
					} else {
						openCount++
					}

					lastIndex = index
					index++
				}

				args = contents.substring(startIndex + 1, lastIndex)
				contents = contents.substring(lastIndex + 1)
			} else {
				contents = contents.substring(match[0].length)
			}

			const method = `compile${match[1][0].toUpperCase()}${match[1].substring(1)}`
			const result = this[method](args)

			if(!result.isNil) {
				expressions.push({
					type: 'code',
					contents: result
				})
			}
		}

		contents = contents.trim()

		if(contents.length > 0) {
			expressions.push({
				type: 'string',
				contents: contents
			})
		}

		let code = ''

		for(const { type, contents } of expressions) {
			if(type === 'code') {
				code += `${contents}\n`
			} else {
				code += `output += \`${sanetizeString(contents)}\`;\n`
			}
		}

		code = `template = function(_) {\nlet output = '';\n${this._contextualize(code)}\nreturn output;\n}`

		return eval(code)
	}

	render(template, context) {
		return this.compile(template).then(template => template({ ...this.context, ...context }))
	}

	_contextualize(code) {
		const tree = acorn.parse(code)
		const scopes = [
			{
				locals: new Set,
				end: Number.MAX_VALUE
			}
		]

		let scope = scopes[0]

		const checkScope = fromNode => {
			while(fromNode.start >= scope.end && scopes.length > 1) {
				scopes.pop()
				scope = scopes[scopes.length - 1]
			}
		}

		acorn.walk(tree, {
			Statement: node => {
				checkScope(node)
			},

			BlockStatement: node => {
				checkScope(node)

				scope = {
					locals: new Set(scope.locals),
					node: node,
					end: node.end
				}

				scopes.push(scope)
			},

			VariableDeclarator: node => {
				checkScope(node)
				scope.locals.add(node.id.name)
			},

			Identifier: node => {
				checkScope(node)

				if(scope.locals.has(node.name)) {
					return
				}

				node.name = `_.${node.name}`
			}
		})

		return astring(tree)
	}

	compileIf(args) {
		return `if(${args}) {`
	}

	compileElseif(args) {
		return `} else if(${args}) {`
	}

	compileElse() {
		return '} else {'
	}

	compileEndif() {
		return this.compileEnd()
	}

	compileFor(args) {
		return `for(${args}) {`
	}

	compileEndfor() {
		return this.compileEnd()
	}

	compileWhile(args) {
		return `while(${args}) {`
	}

	compileEndwhile() {
		return this.compileEnd()
	}

	compileEnd() {
		return '}'
	}

}

/**
 * [indexOfParenthesis description]
 * @param  {string} string    [description]
 * @param  {number} fromIndex [description]
 * @return {number}           [description]
 */
function indexOfParenthesis(string, fromIndex) {
	const open = string.indexOf('(', fromIndex)
	const close = string.indexOf(')', fromIndex)

	if(open === -1) {
		return close
	} else if(close === -1)  {
		return open
	}

	return Math.min(open, close)
}

function sanetizeString(string) {
	string = string.replace(/\{\{\s*(.+?)\s*\}\}/g, '${escape($1)}')
	string = string.replace(/\{!!\s*(.+?)\s*!!\}/g, '${$1}')
	string = string.replace(/[\n]/g, '\\n')
	string = string.replace(/[\r]/g, '\\r')
	string = string.replace(/[\t]/g, '\\t')

	return string
}
<|endoftext|>


<commit_before>"use strict";

module.exports = WirelessTag;

var request = require('request'),
    util = require('util'),
    EventEmitter = require('events'),
    u = require('./util.js'),
    WirelessTagSensor = require('./sensor.js'),
    WirelessTagPlatform = require('./platform.js');

const roTagProps = ["uuid","slaveId","tagType","alive"];
const rwTagProps = ["name",
                    ["updateInterval","postBackInterval"]];

const MAX_UPDATE_RETRIES = 2;

// the following are in milliseconds
const RETRY_UPDATE_DELAY = 5000;         // time before retrying update
const MIN_UPDATE_LOOP_WAIT = 1500;       // minimum wait time between loops
const MAX_UPDATE_LOOP_WAIT = 30 * 60000; // maximum wait time between loops
const DEFAULT_UPDATE_PADDING = 2000; // time to add to calculated update time

/*
 * The cloud instance of a Wireless Tag. One Wireless Tag manager can
 * manage multiple Wireless Tags.
 */
function WirelessTag(tagManager, tagData) {
    EventEmitter.call(this);
    this.wirelessTagManager = tagManager;
    this.errorHandler =
        tagManager ? tagManager.errorHandler : u.defaultHandler;
    this.callAPI = WirelessTagPlatform.callAPI;
    u.setObjProperties(this, roTagProps, rwTagProps);
    this.data = tagData || {};
    this.on('data', this.discoverSensors.bind(this));
    this.on('data', this.bounceUpdateLoop.bind(this));
}
util.inherits(WirelessTag, EventEmitter);

WirelessTag.prototype.sensorCapabilities = function() {
    var capabilities = [];
    for (let propName in this) {
        if (propName.startsWith("has") && propName.endsWith("Sensor")) {
            let propValue = this[propName];
            if ((('function' === typeof propValue) && propValue.call(this))
                || (('function' !== typeof propValue) && propValue)) {
                capabilities.push(propName.
                                  replace(/has(\w+)Sensor/,'$1').
                                  toLowerCase());
            }
        }
    }
    return capabilities;
}

WirelessTag.prototype.eachSensor = function(action) {
    if ('function' !== typeof action) return;
    var capabilities = this.sensorCapabilities();
    for (let cap of capabilities) {
        let sensor = this[cap + "Sensor"];
        if (sensor) action(sensor);
    }
    return capabilities;
}

WirelessTag.prototype.hasMotionSensor = function() {
    return (this.tagType == 12 || this.tagType == 13 || this.tagType == 21);
}
WirelessTag.prototype.hasLightSensor = function() {
    return (this.tagType == 26);
}
WirelessTag.prototype.hasMoistureSensor = function() {
    return (this.tagType == 32 || this.tagType == 33);
}
WirelessTag.prototype.hasWaterSensor = function() {
    return (this.tagType == 32 || this.tagType == 33);
}
WirelessTag.prototype.hasReedSensor = function() {
    return (this.tagType == 52 || this.tagType == 53);
}
WirelessTag.prototype.hasPIRSensor = function() {
    return (this.tagType == 72);
}
WirelessTag.prototype.hasEventSensor = function() {
    return (this.hasMotionSensor()
            || this.hasLightSensor()
            || this.hasReedSensor()
            || this.hasPIRSensor());
}
WirelessTag.prototype.hasHumiditySensor = function() {
    return this.isHTU();
}
WirelessTag.prototype.hasTempSensor = function() {
    return !(this.tagType == 82 || this.tagType == 92);
}
WirelessTag.prototype.hasCurrentSensor = function() {
    return this.tagType == 42;
}
WirelessTag.prototype.canBeep = function() {
    return this.tagType == 13
        || this.tagType == 12
        || this.tagType == 21
        || this.tagType==26;
}
WirelessTag.prototype.canPlayback = function() {
    return (this.tagType == 21);
}
WirelessTag.prototype.isHTU = function() {
    return this.tagType == 13 || this.tagType == 21 // motion && type != 12
        || this.tagType == 52                       // reed && type != 53
        || this.tagType == 26                       // ambient light
        || this.tagType == 72                       // PIR
        || this.isKumostat()
    ;
}
WirelessTag.prototype.isKumostat = function() {
    return (this.tagType == 62);
}
WirelessTag.prototype.isNest = function() {
    return (this.thermostat != null && this.data.thermostat.nest_id != null);
}
WirelessTag.prototype.isWeMo = function() {
    return (this.tagType == 82);
}
WirelessTag.prototype.isWeMoLED = function() {
    return (this.isWeMo() && (this.data.cap > 0));
}
WirelessTag.prototype.isCamera = function() {
    return (this.tagType == 92);
}

WirelessTag.prototype.lastUpdated = function() {
    return new Date(u.FILETIMEtoDate(this.data.lastComm));
}

WirelessTag.prototype.discoverSensors = function(tag) {
    if (! tag) tag = this;
    var capabilities = tag.sensorCapabilities();
    for (let cap of capabilities) {
        let propName = cap + "Sensor";
        let sensor = tag[propName];
        if (sensor) continue;
        sensor = new WirelessTagSensor(tag, cap);
        Object.defineProperty(tag, propName, {
            enumerable: true,
            value: sensor
        });
        tag.emit('discover', sensor);
    }
}

WirelessTag.prototype.update = function(tag, retry, callback)  {
    if ((tag === undefined) || (tag === true) || (tag === false)) {
        retry = tag;
        tag = this;
    }
    if (retry) {
        if (tag._updateAttempts === undefined) {
            tag._updateAttempts = 0;
        } else {
            tag._updateAttempts++;
        }
    }
    var req = tag.callAPI(
        '/ethClient.asmx/GetTagForSlaveId',
        { slaveid: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            if (tag.data.lastComm == result.lastComm) {
                // data not updated by tag manager, retry if requested
                if (retry) {
                    if (this._updateAttempts >= MAX_UPDATE_RETRIES) {
                        return Promise.reject("no update from tag", tag.slaveId,
                                              "after", MAX_UPDATE_RETRIES,
                                              "attempts");
                    } else {
                        console.warn("## retrying update for tag", tag.slaveId);
                        return new Promise((resolve,reject) => {
                            let action = () => {
                                tag.update(retry, callback).then(
                                    (result) => { resolve(result); },
                                    (error) => { reject(error); }
                                );
                            };
                            setTimeout(action, RETRY_UPDATE_DELAY);
                        });
                    }
                }
            }
            tag._updateAttempts = 0;
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
}

WirelessTag.prototype.liveUpdate = function(tag, callback)  {
    if (! tag) tag = this;
    var req = tag.callAPI(
        '/ethClient.asmx/RequestImmediatePostback',
        { id: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
}

WirelessTag.prototype.startUpdateLoop = function(minWait) {
    let padding = DEFAULT_UPDATE_PADDING;
    if (minWait === undefined) {
        minWait = MIN_UPDATE_LOOP_WAIT;
    } else if (minWait > MAX_UPDATE_LOOP_WAIT) {
        minWait = MAX_UPDATE_LOOP_WAIT;
    }
    if (this._updateTimer) return this._updateTimer;
    this._updateTimer = true; // placeholder to avoid race conditions
    let action = () => {
        this._updateTimer = null;
        this.update().then(
            (success) => {
                return this.startUpdateLoop();
            },
            (error) => {
                console.error(error);
                return this.startUpdateLoop(minWait * 2);
            }
        );
    }
    let remainingTime =
        (this.updateInterval * 1000) -
        (Date.now() - this.lastUpdated().getTime());
    if (remainingTime < minWait) {
        remainingTime = minWait;
    } else {
        remainingTime += padding;
    }
    // ensure that updates weren't cancelled since we entered here
    if (this._updateTimer) {
        this._updateTimer = setTimeout(action, remainingTime);
    }
    return this._updateTimer;
}

WirelessTag.prototype.stopUpdateLoop = function() {
    let timer = this._updateTimer;
    this._updateTimer = null;   // avoid race conditions
    if (timer && timer !== true) {
        clearTimeout(timer);
    }
}

WirelessTag.prototype.bounceUpdateLoop = function(tag) {
    if (! tag) tag = this;
    let timer = tag._updateTimer;
    if (timer && timer !== true) {
        console.warn("## bouncing update timer loop for tag", tag.slaveId);
        tag.stopUpdateLoop();
        tag.startUpdateLoop();
    }
}
<commit_msg>Adds tag.rev and tag.version()<commit_after>"use strict";

module.exports = WirelessTag;

var request = require('request'),
    util = require('util'),
    EventEmitter = require('events'),
    u = require('./util.js'),
    WirelessTagSensor = require('./sensor.js'),
    WirelessTagPlatform = require('./platform.js');

const roTagProps = ["uuid","slaveId","tagType","alive","rev"];
const rwTagProps = ["name",
                    ["updateInterval","postBackInterval"]];

const MAX_UPDATE_RETRIES = 2;

// the following are in milliseconds
const RETRY_UPDATE_DELAY = 5000;         // time before retrying update
const MIN_UPDATE_LOOP_WAIT = 1500;       // minimum wait time between loops
const MAX_UPDATE_LOOP_WAIT = 30 * 60000; // maximum wait time between loops
const DEFAULT_UPDATE_PADDING = 2000; // time to add to calculated update time

/*
 * The cloud instance of a Wireless Tag. One Wireless Tag manager can
 * manage multiple Wireless Tags.
 */
function WirelessTag(tagManager, tagData) {
    EventEmitter.call(this);
    this.wirelessTagManager = tagManager;
    this.errorHandler =
        tagManager ? tagManager.errorHandler : u.defaultHandler;
    this.callAPI = WirelessTagPlatform.callAPI;
    u.setObjProperties(this, roTagProps, rwTagProps);
    this.data = tagData || {};
    this.on('data', this.discoverSensors.bind(this));
    this.on('data', this.bounceUpdateLoop.bind(this));
}
util.inherits(WirelessTag, EventEmitter);

WirelessTag.prototype.sensorCapabilities = function() {
    var capabilities = [];
    for (let propName in this) {
        if (propName.startsWith("has") && propName.endsWith("Sensor")) {
            let propValue = this[propName];
            if ((('function' === typeof propValue) && propValue.call(this))
                || (('function' !== typeof propValue) && propValue)) {
                capabilities.push(propName.
                                  replace(/has(\w+)Sensor/,'$1').
                                  toLowerCase());
            }
        }
    }
    return capabilities;
}

WirelessTag.prototype.eachSensor = function(action) {
    if ('function' !== typeof action) return;
    var capabilities = this.sensorCapabilities();
    for (let cap of capabilities) {
        let sensor = this[cap + "Sensor"];
        if (sensor) action(sensor);
    }
    return capabilities;
}

WirelessTag.prototype.version = function() {
    switch (this.data.version1) {
    case 2:
	switch (this.rev) {
        case 14: return "v2.1";
	case 15: return "v2.2";
	case 31: return "v2.3";
	default: return "v2.0";
        }
    case 3:
	return "v3.0";
    case 4:
	return "v4.0";
    default:
        if (this.tagType === 12) {
            switch (this.rev) {
            case 0: return "v1.1";
            case 1: return "v1.2";
            case 11: return "v1.3";
            case 12: return "v1.4";
            case 13: return "v1.5";
            }
        }
    }
    return "";
}

WirelessTag.prototype.hasMotionSensor = function() {
    return (this.tagType == 12 || this.tagType == 13 || this.tagType == 21);
}
WirelessTag.prototype.hasLightSensor = function() {
    return (this.tagType == 26);
}
WirelessTag.prototype.hasMoistureSensor = function() {
    return (this.tagType == 32 || this.tagType == 33);
}
WirelessTag.prototype.hasWaterSensor = function() {
    return (this.tagType == 32 || this.tagType == 33);
}
WirelessTag.prototype.hasReedSensor = function() {
    return (this.tagType == 52 || this.tagType == 53);
}
WirelessTag.prototype.hasPIRSensor = function() {
    return (this.tagType == 72);
}
WirelessTag.prototype.hasEventSensor = function() {
    return (this.hasMotionSensor()
            || this.hasLightSensor()
            || this.hasReedSensor()
            || this.hasPIRSensor());
}
WirelessTag.prototype.hasHumiditySensor = function() {
    return this.isHTU();
}
WirelessTag.prototype.hasTempSensor = function() {
    return !(this.tagType == 82 || this.tagType == 92);
}
WirelessTag.prototype.hasCurrentSensor = function() {
    return this.tagType == 42;
}
WirelessTag.prototype.canBeep = function() {
    return this.tagType == 13
        || this.tagType == 12
        || this.tagType == 21
        || this.tagType==26;
}
WirelessTag.prototype.canPlayback = function() {
    return (this.tagType == 21);
}
WirelessTag.prototype.isHTU = function() {
    return this.tagType == 13 || this.tagType == 21 // motion && type != 12
        || this.tagType == 52                       // reed && type != 53
        || this.tagType == 26                       // ambient light
        || this.tagType == 72                       // PIR
        || this.isKumostat()
    ;
}
WirelessTag.prototype.isKumostat = function() {
    return (this.tagType == 62);
}
WirelessTag.prototype.isNest = function() {
    return (this.thermostat != null && this.data.thermostat.nest_id != null);
}
WirelessTag.prototype.isWeMo = function() {
    return (this.tagType == 82);
}
WirelessTag.prototype.isWeMoLED = function() {
    return (this.isWeMo() && (this.data.cap > 0));
}
WirelessTag.prototype.isCamera = function() {
    return (this.tagType == 92);
}

WirelessTag.prototype.lastUpdated = function() {
    return new Date(u.FILETIMEtoDate(this.data.lastComm));
}

WirelessTag.prototype.discoverSensors = function(tag) {
    if (! tag) tag = this;
    var capabilities = tag.sensorCapabilities();
    for (let cap of capabilities) {
        let propName = cap + "Sensor";
        let sensor = tag[propName];
        if (sensor) continue;
        sensor = new WirelessTagSensor(tag, cap);
        Object.defineProperty(tag, propName, {
            enumerable: true,
            value: sensor
        });
        tag.emit('discover', sensor);
    }
}

WirelessTag.prototype.update = function(tag, retry, callback)  {
    if ((tag === undefined) || (tag === true) || (tag === false)) {
        retry = tag;
        tag = this;
    }
    if (retry) {
        if (tag._updateAttempts === undefined) {
            tag._updateAttempts = 0;
        } else {
            tag._updateAttempts++;
        }
    }
    var req = tag.callAPI(
        '/ethClient.asmx/GetTagForSlaveId',
        { slaveid: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            if (tag.data.lastComm == result.lastComm) {
                // data not updated by tag manager, retry if requested
                if (retry) {
                    if (this._updateAttempts >= MAX_UPDATE_RETRIES) {
                        return Promise.reject("no update from tag", tag.slaveId,
                                              "after", MAX_UPDATE_RETRIES,
                                              "attempts");
                    } else {
                        console.warn("## retrying update for tag", tag.slaveId);
                        return new Promise((resolve,reject) => {
                            let action = () => {
                                tag.update(retry, callback).then(
                                    (result) => { resolve(result); },
                                    (error) => { reject(error); }
                                );
                            };
                            setTimeout(action, RETRY_UPDATE_DELAY);
                        });
                    }
                }
            }
            tag._updateAttempts = 0;
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
}

WirelessTag.prototype.liveUpdate = function(tag, callback)  {
    if (! tag) tag = this;
    var req = tag.callAPI(
        '/ethClient.asmx/RequestImmediatePostback',
        { id: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
}

WirelessTag.prototype.startUpdateLoop = function(minWait) {
    let padding = DEFAULT_UPDATE_PADDING;
    if (minWait === undefined) {
        minWait = MIN_UPDATE_LOOP_WAIT;
    } else if (minWait > MAX_UPDATE_LOOP_WAIT) {
        minWait = MAX_UPDATE_LOOP_WAIT;
    }
    if (this._updateTimer) return this._updateTimer;
    this._updateTimer = true; // placeholder to avoid race conditions
    let action = () => {
        this._updateTimer = null;
        this.update().then(
            (success) => {
                return this.startUpdateLoop();
            },
            (error) => {
                console.error(error);
                return this.startUpdateLoop(minWait * 2);
            }
        );
    }
    let remainingTime =
        (this.updateInterval * 1000) -
        (Date.now() - this.lastUpdated().getTime());
    if (remainingTime < minWait) {
        remainingTime = minWait;
    } else {
        remainingTime += padding;
    }
    // ensure that updates weren't cancelled since we entered here
    if (this._updateTimer) {
        this._updateTimer = setTimeout(action, remainingTime);
    }
    return this._updateTimer;
}

WirelessTag.prototype.stopUpdateLoop = function() {
    let timer = this._updateTimer;
    this._updateTimer = null;   // avoid race conditions
    if (timer && timer !== true) {
        clearTimeout(timer);
    }
}

WirelessTag.prototype.bounceUpdateLoop = function(tag) {
    if (! tag) tag = this;
    let timer = tag._updateTimer;
    if (timer && timer !== true) {
        console.warn("## bouncing update timer loop for tag", tag.slaveId);
        tag.stopUpdateLoop();
        tag.startUpdateLoop();
    }
}
<|endoftext|>


<commit_before><commit_msg>Update index.js<commit_after><|endoftext|>