<commit_before>import './module.js';

import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style.js';
import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import Modify from 'ol/interaction/Modify';
import Polygon from 'ol/geom/Polygon';
import Draw from 'ol/interaction/Draw';
import Overlay from 'ol/Overlay';
import { TOUCH as hasTouch } from 'ol/has';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { transform } from 'ol/proj';
import MultiPoint from 'ol/geom/MultiPoint.js';
import MultiLineString from 'ol/geom/MultiLineString';

import {getArea as getSphereArea, getDistance} from 'ol/sphere';

angular.module('anol.measure')
/**
 * @ngdoc directive
 * @name anol.measure.directive:anolLineMeasure
 *
 * @requires anol.map.MapService
 * @requires anol.map.ControlsSerivce
 * @requries anol.map.LayersService
 *
 * @param {string} anolMeasure Type of measurement. Supported values are *line* and *area*. Default: *line*
 * @param {boolean} geodesic Use geodesic measure method
 * @param {Style} style Style for drawed measures
 * @param {string} tooltipPlacement Position of tooltip
 * @param {number} tooltipDelay Time in milisecounds to wait before display tooltip
 * @param {boolean} tooltipEnable Enable tooltips. Default true for non-touch screens, default false for touchscreens
 * @param {string} templateUrl Url to template to use instead of default one
 * @param {boolean} addToMap Create control and add to map when placed inside map. Default: true
 * @param {function} activate Pass name of function to activate control from outer scope
 * @param {function} decativate Pass name of function to deactivate control from outer scope
 * @param {function} measureResultCallback Given function is called when measure result is available
 * @param {function} activatedCallback Given function is called when control is activated
 * @param {function} deactivatedCallback Given function is called when control is deactivated
 *
 * @description
 * Point, Line or area measurement
 */
    .directive('anolMeasure', ['$templateRequest', '$compile', '$timeout', 'ControlsService', 'LayersService', 'MapService',
        function($templateRequest, $compile, $timeout, ControlsService, LayersService, MapService) {
            // create a sphere whose radius is equal to the semi-major axis of the WGS84 ellipsoid
            // var wgs84Sphere = new ol.Sphere(6378137);
            var measureStyle = function(feature, labelSegments) {
                var geometry = feature.getGeometry();
                var styles = [
                        new Style({
                         fill: new Fill({
                            color: 'rgba(255, 255, 255, 0.2)'
                        }),
                        stroke: new Stroke({
                            color: 'rgba(0, 0, 0, 0.5)',
                            lineDash: [10, 10],
                            width: 2,
                            opacity: 0.5
                        }),
                        image: new CircleStyle({
                            radius: 5,
                            stroke: new Stroke({
                                color: 'rgba(0, 0, 0, 0.7)'
                            })
                        })
                    })
                ];

                function lengthAsString(start, end) {
                    var geometry = new LineString([start, end])
                    var projection = MapService.getMap().getView().getProjection();

                    var length;
                    // TODO load from Service
                    var geodesic = false;

                    if (geodesic) {
                        var coordinates = geometry.getCoordinates();
                        length = 0;
                        for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                            var c1 = transform(coordinates[i], projection.getCode(), 'EPSG:4326');
                            var c2 = transform(coordinates[i + 1], projection.getCode(), 'EPSG:4326');
                            length += getDistance(c1, c2);
                        }
                    } else {
                        length = Math.round(geometry.getLength() * 100) / 100;
                    }

                    var output;
                    if (length > 1000) {
                        output = anol.helper.round((length / 1000), 3) + ' ' + 'km';
                    } else {
                        output = anol.helper.round(length, 2)  + ' ' + 'm';
                    }
                    return output;
                }

                function layoutSegments(start, end) {
                      var newXY = [(end[0] + start[0]) / 2, (end[1] + start[1]) / 2];
                      // text
                      styles.push(new Style({
                        geometry: new Point(newXY),
                        text: new Text({
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({
                                color: '#000'
                            }),
                            stroke: new Stroke({
                                color: '#fff',
                                width: 2
                            }),
                            text: lengthAsString(start, end)
                        })
                      }));
                      // points
                      styles.push(new Style({
                        geometry: new MultiPoint([start, end]),
                        image: new CircleStyle({
                            radius: 5,
                            fill: new Fill({
                              color: 'grey'
                            })
                        })
                      }));
                    }

                if (labelSegments) {
                    var geometryType = feature.getGeometry().getType();
                    if (geometryType === 'LineString') {
                        geometry.forEachSegment(function(start, end) {
                            layoutSegments(start, end);
                        });
                    }
                    if (geometryType === 'Polygon') {
                        var coordinates = geometry.getCoordinates();
                        var coords = coordinates[0];
                        angular.forEach(coords, function(coord, idx) {
                            if (idx !== coords.length - 1){
                                var start = coord;
                                var end = coords[idx + 1];
                                layoutSegments(start, end);
                            }
                        })
                    }
                }
                return styles;
            };

            var calculateCoordinate = function(geometry) {
                return geometry.getCoordinates();
            };

            var calculateLength = function(geometry, projection, geodesic) {
                if(geometry.getType() !== 'LineString') {
                    return 0;
                }
                var length;
                if (geodesic) {
                    var coordinates = geometry.getCoordinates();
                    length = 0;
                    for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                        var c1 = transform(coordinates[i], projection.getCode(), 'EPSG:4326');
                        var c2 = transform(coordinates[i + 1], projection.getCode(), 'EPSG:4326');
                        length += getDistance(c1, c2);
                    }
                } else {
                    length = Math.round(geometry.getLength() * 100) / 100;
                }
                return length;
            };

            var calculateArea = function(geometry, projection, geodesic) {
                if(geometry.getType() !== 'Polygon') {
                    return 0.0;
                }
                var area;
                if (geodesic) {
                    area = getSphereArea(geometry);
                } else {
                    area = geometry.getArea();
                }
                return area;
            };

            var formatCoordinateResult = function(geometry, projection, geodesic, labelSegments) {
                var coord = transform(geometry.getCoordinates(),
                    projection.getCode(),
                    'EPSG:4326');
                var output = '';
                output += coord[0] + ' lat | ';
                output += coord[1] + ' lon';
                return output;
            };

            var formatLineResult = function(geometry, projection, geodesic, labelSegments) {
                var length = calculateLength(geometry, projection, geodesic);
                var output;
                if (length > 1000) {
                    output = anol.helper.round((length / 1000), 3) + ' ' + 'km';
                } else {
                    output = anol.helper.round(length, 2)  + ' ' + 'm';
                }
                return output;
            };

            var formatAreaResult = function(geometry, projection, geodesic, labelSegments) {
                var area = calculateArea(geometry, projection, geodesic);
                if (area === 0) {
                    return '';
                }

                var output;
                if (area > 100000) {
                    output = anol.helper.round((area / 10000), 3) +
                     ' ' + 'ha';
                } else {
                    output = anol.helper.round(area, 3) +
                     ' ' + 'm2';
                }

                if (labelSegments) {
                    // calculate line length for area and add to label
                    var length = 0;
                    var coordinates = geometry.getCoordinates();
                    var coords = coordinates[0];
                    angular.forEach(coords, function(coord, idx) {
                        if (idx !== coords.length - 1){
                            length += calculateLength(new LineString([coord, coords[idx + 1]]));
                        }
                    })
                    if (length > 1000) {
                        output += '
' +  anol.helper.round((length / 1000), 3) + ' ' + 'km';
                    } else {
                        output += '
' +  anol.helper.round(length, 2)  + ' ' + 'm';
                    }
                }
                return output;
            };

            var handlePointMeasure = function(geometry, coordinate) {
                return new Point(coordinate);
            };

            var handleLineMeasure = function(geometry, coordinate) {
                switch(geometry.getType()) {
                case 'Point':
                    return new LineString([
                        geometry.getCoordinates(),
                        coordinate
                    ]);
                case 'LineString':
                    var coords = geometry.getCoordinates();
                    coords.push(coordinate);
                    return new LineString(coords);
                }
            };

            var handleAreaMeasure = function(geometry, coordinate) {
                var coords;
                switch(geometry.getType()) {
                case 'Point':
                    return new LineString([
                        geometry.getCoordinates(),
                        coordinate
                    ]);
                case 'LineString':
                    coords = geometry.getCoordinates();
                    coords.push(coordinate);
                    coords.push(coords[0]);
                    return new Polygon([coords]);
                case 'Polygon':
                    coords = geometry.getCoordinates()[0];
                    coords.splice(coords.length - 1, 0, coordinate);
                    return new Polygon([coords]);
                }
            };

            var createMeasureOverlay = function() {
                var element = angular.element('

');
                element.addClass('anol-overlay');
                element.addClass('anol-measure-overlay');
                var overlay = new Overlay({
                    element: element[0],
                    offset: [0, -15],
                    positioning: 'bottom-center'
                });
                return overlay;
            };

            var createModifyInteraction = function(measureSource, measureType, measureOverlay, measureResultCallback, projection, geodesic, labelSegments) {
                var modify = new Modify({
                    features: measureSource.getFeaturesCollection()
                });
                modify.on('modifyend', function() {
                    var resultFormatter, resultCalculator;
                    switch(measureType) {
                    case 'point':
                        resultCalculator = calculateCoordinate;
                        resultFormatter = formatCoordinateResult;
                        break;
                    case 'line':
                        resultCalculator = calculateLength;
                        resultFormatter = formatLineResult;
                        break;
                    case 'area':
                        resultCalculator = calculateArea;
                        resultFormatter = formatAreaResult;
                        break;
                    }
                    var geometry = measureSource.getFeatures()[0].getGeometry();

                    if(angular.isFunction(measureResultCallback)) {
                        measureResultCallback({
                            type: measureType,
                            value: resultCalculator(geometry, projection, geodesic)
                        });
                        return;
                    }

                    measureOverlay.getElement().innerHTML = resultFormatter(geometry, projection, geodesic, labelSegments);
                    measureOverlay.setPosition(geometry.getLastCoordinate());
                });
                return modify;
            };

            var createDrawInteraction = function(measureSource, measureType, measureOverlay, measureResultCallback, projection, geodesic, labelSegments) {
                var draw = new Draw({
                    type: 'Point',
                    style: new Style({})
                });

                draw.on('drawstart',
                    function(evt) {
                        var sketch = evt.feature;

                        /** @type {ol.Coordinate|undefined} */
                        var coord = evt.coordinate;
                        measureOverlay.setPosition(coord);

                        var geometryCreator;
                        var resultCalculator;
                        var resultFormatter;
                        switch(measureType) {
                        case 'point':
                            geometryCreator = handlePointMeasure;
                            resultCalculator = calculateCoordinate;
                            resultFormatter = formatCoordinateResult;
                            break;
                        case 'line':
                            geometryCreator = handleLineMeasure;
                            resultCalculator = calculateLength;
                            resultFormatter = formatLineResult;
                            break;
                        case 'area':
                            geometryCreator = handleAreaMeasure;
                            resultCalculator = calculateArea;
                            resultFormatter = formatAreaResult;
                            break;
                        }
                        var features = measureSource.getFeatures();
                        if(features.length === 0) {
                            measureSource.addFeature(sketch);
                            if(angular.isFunction(measureResultCallback)) {
                                measureResultCallback({
                                    type: measureType,
                                    value: resultCalculator(sketch.getGeometry(), projection, geodesic)
                                });
                            }
                            return;
                        }

                        var newGeometry = geometryCreator(features[0].getGeometry(),
                            sketch.getGeometry().getCoordinates());
                        features[0].setGeometry(newGeometry);

                        if(angular.isFunction(measureResultCallback)) {
                            measureResultCallback({
                                type: measureType,
                                value: resultCalculator(newGeometry, projection, geodesic)
                            });
                            return;
                        }

                        measureOverlay.getElement().innerHTML = resultFormatter(newGeometry, projection, geodesic, labelSegments);
                        measureOverlay.setPosition(newGeometry.getLastCoordinate());
                    }, this
                );
                return draw;
            };

            return {
                restrict: 'A',
                require: '?^anolMap',
                replace: true,
                scope: {
                    measureType: '@anolMeasure',
                    geodesic: '=',
                    style: '=?',
                    tooltipPlacement: '@',
                    tooltipDelay: '@',
                    tooltipEnable: '@',
                    addToMap: '@?',
                    activate: '=?',
                    deactivate: '=?',
                    measureResultCallback: '=?',
                    activatedCallback: '=?',
                    deactivatedCallback: '=?',
                    labelSegments: '=',
                    title: '@'
                },
                template: function() {
                    return require('./templates/measure.html');
                },
                link: function(scope, element, attrs, AnolMapController) {
                    //attribute defaults
                    scope.tooltipPlacement = angular.isDefined(scope.tooltipPlacement) ?
                        scope.tooltipPlacement : 'right';
                    scope.tooltipDelay = angular.isDefined(scope.tooltipDelay) ?
                        scope.tooltipDelay : 500;
                    scope.tooltipEnable = angular.isDefined(scope.tooltipEnable) ?
                        scope.tooltipEnable : !hasTouch;
                    scope.geodesic = scope.geodesic === true || scope.geodesic === 'true';
                    scope.labelSegments = angular.isDefined(scope.labelSegments) ?
                        scope.labelSegments : false;

                    var control;

                    // create layer to draw in
                    var measureSource = new VectorSource({
                        useSpatialIndex: false
                    });
                    var _measureLayer = new VectorLayer({
                        source: measureSource,
                        style: scope.style || function(feature) {
                            return measureStyle(feature, scope.labelSegments)
                        },
                        zIndex: 2000
                    });

                    var layerOptions = {
                        title: scope.measureType + 'MeasureLayer',
                        name: scope.measureType + 'MeasureLayer',
                        displayInLayerswitcher: false,
                        olLayer: _measureLayer
                    };

                    var map = MapService.getMap();

                    var measureOverlay = createMeasureOverlay();

                    var draw = createDrawInteraction(measureSource,
                        scope.measureType,
                        measureOverlay,
                        scope.measureResultCallback,
                        map.getView().getProjection(),
                        scope.geodesic,
                        scope.labelSegments
                    );

                    var modify = createModifyInteraction(measureSource,
                        scope.measureType,
                        measureOverlay,
                        scope.measureResultCallback,
                        map.getView().getProjection(),
                        scope.geodesic,
                        scope.labelSegments
                    );

                    scope.measure = function() {
                        if(control.active) {
                            control.deactivate();
                        } else {
                            control.activate();
                        }
                    };

                    var deactivate = function() {
                        map.removeInteraction(draw);
                        map.removeInteraction(modify);
                        measureSource.clear();
                        map.removeOverlay(measureOverlay);
                        measureOverlay.getElement().innerHTML = '';
                        if(angular.isFunction(scope.deactivatedCallback)) {
                            scope.deactivatedCallback();
                        }
                    };

                    var activate = function() {
                        map.addInteraction(draw);
                        map.addInteraction(modify);
                        map.addOverlay(measureOverlay);
                        if(angular.isFunction(scope.activatedCallback)) {
                            scope.activatedCallback();
                        }
                    };

                    scope.deactivate = function() {
                        control.deactivate();
                    };

                    scope.activate = function() {
                        control.activate();
                    };

                    LayersService.addSystemLayer(new anol.layer.Layer(layerOptions), 0);

                    if(AnolMapController === null || scope.addToMap === false || scope.addToMap === 'false') {
                        control = new anol.control.Control({
                            exclusive: true,
                            olControl: null
                        });
                    } else {
                        element.addClass('ol-control');
                        element.addClass('anol-measure-' + scope.measureType);
                        control = new anol.control.Control({
                            element: element,
                            exclusive: true
                        });
                    }

                    control.onDeactivate(deactivate);
                    control.onActivate(activate);
                    ControlsService.addControl(control);
                }
            };
        }]);
<commit_msg>add conditon to support mesausre only on left click<commit_after>import './module.js';

import {Circle as CircleStyle, Fill, Stroke, Style, Text} from 'ol/style.js';
import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import Modify from 'ol/interaction/Modify';
import Polygon from 'ol/geom/Polygon';
import Draw from 'ol/interaction/Draw';
import Overlay from 'ol/Overlay';
import { TOUCH as hasTouch } from 'ol/has';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { transform } from 'ol/proj';
import MultiPoint from 'ol/geom/MultiPoint.js';
import MultiLineString from 'ol/geom/MultiLineString';

import {getArea as getSphereArea, getDistance} from 'ol/sphere';

angular.module('anol.measure')
/**
 * @ngdoc directive
 * @name anol.measure.directive:anolLineMeasure
 *
 * @requires anol.map.MapService
 * @requires anol.map.ControlsSerivce
 * @requries anol.map.LayersService
 *
 * @param {string} anolMeasure Type of measurement. Supported values are *line* and *area*. Default: *line*
 * @param {boolean} geodesic Use geodesic measure method
 * @param {Style} style Style for drawed measures
 * @param {string} tooltipPlacement Position of tooltip
 * @param {number} tooltipDelay Time in milisecounds to wait before display tooltip
 * @param {boolean} tooltipEnable Enable tooltips. Default true for non-touch screens, default false for touchscreens
 * @param {string} templateUrl Url to template to use instead of default one
 * @param {boolean} addToMap Create control and add to map when placed inside map. Default: true
 * @param {function} activate Pass name of function to activate control from outer scope
 * @param {function} decativate Pass name of function to deactivate control from outer scope
 * @param {function} measureResultCallback Given function is called when measure result is available
 * @param {function} activatedCallback Given function is called when control is activated
 * @param {function} deactivatedCallback Given function is called when control is deactivated
 *
 * @description
 * Point, Line or area measurement
 */
    .directive('anolMeasure', ['$templateRequest', '$compile', '$timeout', 'ControlsService', 'LayersService', 'MapService',
        function($templateRequest, $compile, $timeout, ControlsService, LayersService, MapService) {
            // create a sphere whose radius is equal to the semi-major axis of the WGS84 ellipsoid
            // var wgs84Sphere = new ol.Sphere(6378137);
            var measureStyle = function(feature, labelSegments) {
                var geometry = feature.getGeometry();
                var styles = [
                        new Style({
                         fill: new Fill({
                            color: 'rgba(255, 255, 255, 0.2)'
                        }),
                        stroke: new Stroke({
                            color: 'rgba(0, 0, 0, 0.5)',
                            lineDash: [10, 10],
                            width: 2,
                            opacity: 0.5
                        }),
                        image: new CircleStyle({
                            radius: 5,
                            stroke: new Stroke({
                                color: 'rgba(0, 0, 0, 0.7)'
                            })
                        })
                    })
                ];

                function lengthAsString(start, end) {
                    var geometry = new LineString([start, end])
                    var projection = MapService.getMap().getView().getProjection();

                    var length;
                    // TODO load from Service
                    var geodesic = false;

                    if (geodesic) {
                        var coordinates = geometry.getCoordinates();
                        length = 0;
                        for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                            var c1 = transform(coordinates[i], projection.getCode(), 'EPSG:4326');
                            var c2 = transform(coordinates[i + 1], projection.getCode(), 'EPSG:4326');
                            length += getDistance(c1, c2);
                        }
                    } else {
                        length = Math.round(geometry.getLength() * 100) / 100;
                    }

                    var output;
                    if (length > 1000) {
                        output = anol.helper.round((length / 1000), 3) + ' ' + 'km';
                    } else {
                        output = anol.helper.round(length, 2)  + ' ' + 'm';
                    }
                    return output;
                }

                function layoutSegments(start, end) {
                      var newXY = [(end[0] + start[0]) / 2, (end[1] + start[1]) / 2];
                      // text
                      styles.push(new Style({
                        geometry: new Point(newXY),
                        text: new Text({
                            font: '14px Calibri,sans-serif',
                            fill: new Fill({
                                color: '#000'
                            }),
                            stroke: new Stroke({
                                color: '#fff',
                                width: 2
                            }),
                            text: lengthAsString(start, end)
                        })
                      }));
                      // points
                      styles.push(new Style({
                        geometry: new MultiPoint([start, end]),
                        image: new CircleStyle({
                            radius: 5,
                            fill: new Fill({
                              color: 'grey'
                            })
                        })
                      }));
                    }

                if (labelSegments) {
                    var geometryType = feature.getGeometry().getType();
                    if (geometryType === 'LineString') {
                        geometry.forEachSegment(function(start, end) {
                            layoutSegments(start, end);
                        });
                    }
                    if (geometryType === 'Polygon') {
                        var coordinates = geometry.getCoordinates();
                        var coords = coordinates[0];
                        angular.forEach(coords, function(coord, idx) {
                            if (idx !== coords.length - 1){
                                var start = coord;
                                var end = coords[idx + 1];
                                layoutSegments(start, end);
                            }
                        })
                    }
                }
                return styles;
            };

            var calculateCoordinate = function(geometry) {
                return geometry.getCoordinates();
            };

            var calculateLength = function(geometry, projection, geodesic) {
                if(geometry.getType() !== 'LineString') {
                    return 0;
                }
                var length;
                if (geodesic) {
                    var coordinates = geometry.getCoordinates();
                    length = 0;
                    for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                        var c1 = transform(coordinates[i], projection.getCode(), 'EPSG:4326');
                        var c2 = transform(coordinates[i + 1], projection.getCode(), 'EPSG:4326');
                        length += getDistance(c1, c2);
                    }
                } else {
                    length = Math.round(geometry.getLength() * 100) / 100;
                }
                return length;
            };

            var calculateArea = function(geometry, projection, geodesic) {
                if(geometry.getType() !== 'Polygon') {
                    return 0.0;
                }
                var area;
                if (geodesic) {
                    area = getSphereArea(geometry);
                } else {
                    area = geometry.getArea();
                }
                return area;
            };

            var formatCoordinateResult = function(geometry, projection, geodesic, labelSegments) {
                var coord = transform(geometry.getCoordinates(),
                    projection.getCode(),
                    'EPSG:4326');
                var output = '';
                output += coord[0] + ' lat | ';
                output += coord[1] + ' lon';
                return output;
            };

            var formatLineResult = function(geometry, projection, geodesic, labelSegments) {
                var length = calculateLength(geometry, projection, geodesic);
                var output;
                if (length > 1000) {
                    output = anol.helper.round((length / 1000), 3) + ' ' + 'km';
                } else {
                    output = anol.helper.round(length, 2)  + ' ' + 'm';
                }
                return output;
            };

            var formatAreaResult = function(geometry, projection, geodesic, labelSegments) {
                var area = calculateArea(geometry, projection, geodesic);
                if (area === 0) {
                    return '';
                }

                var output;
                if (area > 100000) {
                    output = anol.helper.round((area / 10000), 3) +
                     ' ' + 'ha';
                } else {
                    output = anol.helper.round(area, 3) +
                     ' ' + 'm2';
                }

                if (labelSegments) {
                    // calculate line length for area and add to label
                    var length = 0;
                    var coordinates = geometry.getCoordinates();
                    var coords = coordinates[0];
                    angular.forEach(coords, function(coord, idx) {
                        if (idx !== coords.length - 1){
                            length += calculateLength(new LineString([coord, coords[idx + 1]]));
                        }
                    })
                    if (length > 1000) {
                        output += '
' +  anol.helper.round((length / 1000), 3) + ' ' + 'km';
                    } else {
                        output += '
' +  anol.helper.round(length, 2)  + ' ' + 'm';
                    }
                }
                return output;
            };

            var handlePointMeasure = function(geometry, coordinate) {
                return new Point(coordinate);
            };

            var handleLineMeasure = function(geometry, coordinate) {
                switch(geometry.getType()) {
                case 'Point':
                    return new LineString([
                        geometry.getCoordinates(),
                        coordinate
                    ]);
                case 'LineString':
                    var coords = geometry.getCoordinates();
                    coords.push(coordinate);
                    return new LineString(coords);
                }
            };

            var handleAreaMeasure = function(geometry, coordinate) {
                var coords;
                switch(geometry.getType()) {
                case 'Point':
                    return new LineString([
                        geometry.getCoordinates(),
                        coordinate
                    ]);
                case 'LineString':
                    coords = geometry.getCoordinates();
                    coords.push(coordinate);
                    coords.push(coords[0]);
                    return new Polygon([coords]);
                case 'Polygon':
                    coords = geometry.getCoordinates()[0];
                    coords.splice(coords.length - 1, 0, coordinate);
                    return new Polygon([coords]);
                }
            };

            var createMeasureOverlay = function() {
                var element = angular.element('

');
                element.addClass('anol-overlay');
                element.addClass('anol-measure-overlay');
                var overlay = new Overlay({
                    element: element[0],
                    offset: [0, -15],
                    positioning: 'bottom-center'
                });
                return overlay;
            };

            var createModifyInteraction = function(measureSource, measureType, measureOverlay, measureResultCallback, projection, geodesic, labelSegments) {
                var modify = new Modify({
                    features: measureSource.getFeaturesCollection(),
                    condition: function(e) {
                        if (e.pointerEvent.buttons === 1) {
                            return true;
                        }  else {
                            return false;
                        }
                    }
                });
                modify.on('modifyend', function() {
                    var resultFormatter, resultCalculator;
                    switch(measureType) {
                    case 'point':
                        resultCalculator = calculateCoordinate;
                        resultFormatter = formatCoordinateResult;
                        break;
                    case 'line':
                        resultCalculator = calculateLength;
                        resultFormatter = formatLineResult;
                        break;
                    case 'area':
                        resultCalculator = calculateArea;
                        resultFormatter = formatAreaResult;
                        break;
                    }
                    var geometry = measureSource.getFeatures()[0].getGeometry();

                    if(angular.isFunction(measureResultCallback)) {
                        measureResultCallback({
                            type: measureType,
                            value: resultCalculator(geometry, projection, geodesic)
                        });
                        return;
                    }

                    measureOverlay.getElement().innerHTML = resultFormatter(geometry, projection, geodesic, labelSegments);
                    measureOverlay.setPosition(geometry.getLastCoordinate());
                });
                return modify;
            };

            var createDrawInteraction = function(measureSource, measureType, measureOverlay, measureResultCallback, projection, geodesic, labelSegments) {
                var draw = new Draw({
                    type: 'Point',
                    condition: function(e) {
                        if (e.pointerEvent.buttons === 1) {
                            return true;
                        }  else {
                            return false;
                        }
                    },
                    style: new Style({})
                });

                draw.on('drawstart',
                    function(evt) {
                        var sketch = evt.feature;

                        /** @type {ol.Coordinate|undefined} */
                        var coord = evt.coordinate;
                        measureOverlay.setPosition(coord);

                        var geometryCreator;
                        var resultCalculator;
                        var resultFormatter;
                        switch(measureType) {
                        case 'point':
                            geometryCreator = handlePointMeasure;
                            resultCalculator = calculateCoordinate;
                            resultFormatter = formatCoordinateResult;
                            break;
                        case 'line':
                            geometryCreator = handleLineMeasure;
                            resultCalculator = calculateLength;
                            resultFormatter = formatLineResult;
                            break;
                        case 'area':
                            geometryCreator = handleAreaMeasure;
                            resultCalculator = calculateArea;
                            resultFormatter = formatAreaResult;
                            break;
                        }
                        var features = measureSource.getFeatures();
                        if(features.length === 0) {
                            measureSource.addFeature(sketch);
                            if(angular.isFunction(measureResultCallback)) {
                                measureResultCallback({
                                    type: measureType,
                                    value: resultCalculator(sketch.getGeometry(), projection, geodesic)
                                });
                            }
                            return;
                        }

                        var newGeometry = geometryCreator(features[0].getGeometry(),
                            sketch.getGeometry().getCoordinates());
                        features[0].setGeometry(newGeometry);

                        if(angular.isFunction(measureResultCallback)) {
                            measureResultCallback({
                                type: measureType,
                                value: resultCalculator(newGeometry, projection, geodesic)
                            });
                            return;
                        }

                        measureOverlay.getElement().innerHTML = resultFormatter(newGeometry, projection, geodesic, labelSegments);
                        measureOverlay.setPosition(newGeometry.getLastCoordinate());
                    }, this
                );
                return draw;
            };

            return {
                restrict: 'A',
                require: '?^anolMap',
                replace: true,
                scope: {
                    measureType: '@anolMeasure',
                    geodesic: '=',
                    style: '=?',
                    tooltipPlacement: '@',
                    tooltipDelay: '@',
                    tooltipEnable: '@',
                    addToMap: '@?',
                    activate: '=?',
                    deactivate: '=?',
                    measureResultCallback: '=?',
                    activatedCallback: '=?',
                    deactivatedCallback: '=?',
                    labelSegments: '=',
                    title: '@'
                },
                template: function() {
                    return require('./templates/measure.html');
                },
                link: function(scope, element, attrs, AnolMapController) {
                    //attribute defaults
                    scope.tooltipPlacement = angular.isDefined(scope.tooltipPlacement) ?
                        scope.tooltipPlacement : 'right';
                    scope.tooltipDelay = angular.isDefined(scope.tooltipDelay) ?
                        scope.tooltipDelay : 500;
                    scope.tooltipEnable = angular.isDefined(scope.tooltipEnable) ?
                        scope.tooltipEnable : !hasTouch;
                    scope.geodesic = scope.geodesic === true || scope.geodesic === 'true';
                    scope.labelSegments = angular.isDefined(scope.labelSegments) ?
                        scope.labelSegments : false;

                    var control;

                    // create layer to draw in
                    var measureSource = new VectorSource({
                        useSpatialIndex: false
                    });
                    var _measureLayer = new VectorLayer({
                        source: measureSource,
                        style: scope.style || function(feature) {
                            return measureStyle(feature, scope.labelSegments)
                        },
                        zIndex: 2000
                    });

                    var layerOptions = {
                        title: scope.measureType + 'MeasureLayer',
                        name: scope.measureType + 'MeasureLayer',
                        displayInLayerswitcher: false,
                        olLayer: _measureLayer
                    };

                    var map = MapService.getMap();

                    var measureOverlay = createMeasureOverlay();

                    var draw = createDrawInteraction(measureSource,
                        scope.measureType,
                        measureOverlay,
                        scope.measureResultCallback,
                        map.getView().getProjection(),
                        scope.geodesic,
                        scope.labelSegments
                    );

                    var modify = createModifyInteraction(measureSource,
                        scope.measureType,
                        measureOverlay,
                        scope.measureResultCallback,
                        map.getView().getProjection(),
                        scope.geodesic,
                        scope.labelSegments
                    );

                    scope.measure = function() {
                        if(control.active) {
                            control.deactivate();
                        } else {
                            control.activate();
                        }
                    };

                    var deactivate = function() {
                        map.removeInteraction(draw);
                        map.removeInteraction(modify);
                        measureSource.clear();
                        map.removeOverlay(measureOverlay);
                        measureOverlay.getElement().innerHTML = '';
                        if(angular.isFunction(scope.deactivatedCallback)) {
                            scope.deactivatedCallback();
                        }
                    };

                    var activate = function() {
                        map.addInteraction(draw);
                        map.addInteraction(modify);
                        map.addOverlay(measureOverlay);
                        if(angular.isFunction(scope.activatedCallback)) {
                            scope.activatedCallback();
                        }
                    };

                    scope.deactivate = function() {
                        control.deactivate();
                    };

                    scope.activate = function() {
                        control.activate();
                    };

                    LayersService.addSystemLayer(new anol.layer.Layer(layerOptions), 0);

                    if(AnolMapController === null || scope.addToMap === false || scope.addToMap === 'false') {
                        control = new anol.control.Control({
                            exclusive: true,
                            olControl: null
                        });
                    } else {
                        element.addClass('ol-control');
                        element.addClass('anol-measure-' + scope.measureType);
                        control = new anol.control.Control({
                            element: element,
                            exclusive: true
                        });
                    }

                    control.onDeactivate(deactivate);
                    control.onActivate(activate);
                    ControlsService.addControl(control);
                }
            };
        }]);
<|endoftext|>


<commit_before>var canvas = document.querySelector("#gamevas");
canvas.width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
canvas.height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
var ctx = canvas.getContext("2d");
var player, enemies, goal, held = 0, difficulty = 1, ticks = 0;
var screen = { x: 0, y: 0, width: canvas.width, height: canvas.height};
engn.makeDPad();
var levelSwitch = function (text, newlevel) {
  difficulty = newlevel;
  engn.keys.reset();
  setGame();
};
var renderThing = function (thing) {
  ctx.fillStyle = thing.color;
  ctx.fillRect(thing.x, thing.y, thing.width, thing.height);
};
var loop = new engn.Loop(60);
setGame();
loop.onUpdate(gameUpdate).onRender(gameRender).unfreeze();
<commit_msg>Replace spurts with boost powerups.<commit_after>var canvas = document.querySelector("#gamevas");
canvas.width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
canvas.height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
var ctx = canvas.getContext("2d");
var player, enemies, boosts, goal, held = 0, difficulty = 1, ticks = 0;
var screen = { x: 0, y: 0, width: canvas.width, height: canvas.height};
engn.makeDPad();
var levelSwitch = function (text, newlevel) {
  difficulty = newlevel;
  engn.keys.reset();
  setGame();
};
var renderRect = function (thing) {
  ctx.fillStyle = thing.color;
  ctx.fillRect(thing.x, thing.y, thing.width, thing.height);
};
var renderCircle = function (thing) {
  ctx.fillStyle = thing.color;
  ctx.beginPath();
  ctx.ellipse(thing.x - thing.width / 2, thing.y - thing.height / 2,
    thing.width / 2, thing.height / 2, 0, 0, Math.PI * 2);
  ctx.fill();
};
var loop = new engn.Loop(60);
setGame();
loop.onUpdate(gameUpdate).onRender(gameRender).unfreeze();
<|endoftext|>


<commit_before>
  var audio_context;
  var recorder;

  function startUserMedia(stream) {
    var input = audio_context.createMediaStreamSource(stream);
    recorder = new Recorder(input);
  }

  function startRecording(button) {
    recorder && recorder.record();
    //button.disabled = true;
    //button.nextElementSibling.disabled = false;
  }

  function stopRecording(button) {
    recorder && recorder.stop();
    //button.disabled = true;
    //button.previousElementSibling.disabled = false;
    // create WAV download link using audio data blob
    createDownloadLink();

    recorder.clear();
  }

  var success=function(data){
    data=$.parseJSON(data);
    console.log(data);
  };

  function createDownloadLink() {
    recorder && recorder.exportWAV(function(blob) {
      debug=true;
      if(debug){
        download='prova';}
      else{
          download = "RAP_"+new Date().toISOString() + '.wav';
          saveAs(blob, download);}
      queryParam=$.param({'namefile':download});

      event.preventDefault();
      var url = 'http://127.0.0.1?'+queryParam;
      $.get(url, success);

      //CALL FUNCTION
    });
  }

  window.onload = function init() {
    try {
      // webkit shim
      window.AudioContext = window.AudioContext || window.webkitAudioContext;
      navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
      window.URL = window.URL || window.webkitURL;

      audio_context = new AudioContext;

    } catch (e) {
      alert('No web audio support in this browser!');
    }

    navigator.getUserMedia({audio: true}, startUserMedia, function(e) {

    });
  };

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Recorder = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";

module.exports = require("./recorder").Recorder;

},{"./recorder":2}],2:[function(require,module,exports){
'use strict';

var _createClass = (function () {
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
        }
    }return function (Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
    };
})();

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.Recorder = undefined;

var _inlineWorker = require('inline-worker');

var _inlineWorker2 = _interopRequireDefault(_inlineWorker);

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : { default: obj };
}

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

var Recorder = exports.Recorder = (function () {
    function Recorder(source, cfg) {
        var _this = this;

        _classCallCheck(this, Recorder);

        this.config = {
            bufferLen: 4096,
            numChannels: 2,
            mimeType: 'audio/wav'
        };
        this.recording = false;
        this.callbacks = {
            getBuffer: [],
            exportWAV: []
        };

        Object.assign(this.config, cfg);
        this.context = source.context;
        this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, this.config.bufferLen, this.config.numChannels, this.config.numChannels);

        this.node.onaudioprocess = function (e) {
            if (!_this.recording) return;

            var buffer = [];
            for (var channel = 0; channel < _this.config.numChannels; channel++) {
                buffer.push(e.inputBuffer.getChannelData(channel));
            }
            _this.worker.postMessage({
                command: 'record',
                buffer: buffer
            });
        };

        source.connect(this.node);
        this.node.connect(this.context.destination); //this should not be necessary

        var self = {};
        this.worker = new _inlineWorker2.default(function () {
            var recLength = 0,
                recBuffers = [],
                sampleRate = undefined,
                numChannels = undefined;

            self.onmessage = function (e) {
                switch (e.data.command) {
                    case 'init':
                        init(e.data.config);
                        break;
                    case 'record':
                        record(e.data.buffer);
                        break;
                    case 'exportWAV':
                        exportWAV(e.data.type);
                        break;
                    case 'getBuffer':
                        getBuffer();
                        break;
                    case 'clear':
                        clear();
                        break;
                }
            };

            function init(config) {
                sampleRate = config.sampleRate;
                numChannels = config.numChannels;
                initBuffers();
            }

            function record(inputBuffer) {
                for (var channel = 0; channel < numChannels; channel++) {
                    recBuffers[channel].push(inputBuffer[channel]);
                }
                recLength += inputBuffer[0].length;
            }

            function exportWAV(type) {
                var buffers = [];
                for (var channel = 0; channel < numChannels; channel++) {
                    buffers.push(mergeBuffers(recBuffers[channel], recLength));
                }
                var interleaved = undefined;
                if (numChannels === 2) {
                    interleaved = interleave(buffers[0], buffers[1]);
                } else {
                    interleaved = buffers[0];
                }
                var dataview = encodeWAV(interleaved);
                var audioBlob = new Blob([dataview], { type: type });

                self.postMessage({ command: 'exportWAV', data: audioBlob });
            }

            function getBuffer() {
                var buffers = [];
                for (var channel = 0; channel < numChannels; channel++) {
                    buffers.push(mergeBuffers(recBuffers[channel], recLength));
                }
                self.postMessage({ command: 'getBuffer', data: buffers });
            }

            function clear() {
                recLength = 0;
                recBuffers = [];
                initBuffers();
            }

            function initBuffers() {
                for (var channel = 0; channel < numChannels; channel++) {
                    recBuffers[channel] = [];
                }
            }

            function mergeBuffers(recBuffers, recLength) {
                var result = new Float32Array(recLength);
                var offset = 0;
                for (var i = 0; i < recBuffers.length; i++) {
                    result.set(recBuffers[i], offset);
                    offset += recBuffers[i].length;
                }
                return result;
            }

            function interleave(inputL, inputR) {
                var length = inputL.length + inputR.length;
                var result = new Float32Array(length);

                var index = 0,
                    inputIndex = 0;

                while (index < length) {
                    result[index++] = inputL[inputIndex];
                    result[index++] = inputR[inputIndex];
                    inputIndex++;
                }
                return result;
            }

            function floatTo16BitPCM(output, offset, input) {
                for (var i = 0; i < input.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, input[i]));
                    output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }

            function writeString(view, offset, string) {
                for (var i = 0; i < string.length; i++) {
                    view.setUint8(offset + i, string.charCodeAt(i));
                }
            }

            function encodeWAV(samples) {
                var buffer = new ArrayBuffer(44 + samples.length * 2);
                var view = new DataView(buffer);

                /* RIFF identifier */
                writeString(view, 0, 'RIFF');
                /* RIFF chunk length */
                view.setUint32(4, 36 + samples.length * 2, true);
                /* RIFF type */
                writeString(view, 8, 'WAVE');
                /* format chunk identifier */
                writeString(view, 12, 'fmt ');
                /* format chunk length */
                view.setUint32(16, 16, true);
                /* sample format (raw) */
                view.setUint16(20, 1, true);
                /* channel count */
                view.setUint16(22, numChannels, true);
                /* sample rate */
                view.setUint32(24, sampleRate, true);
                /* byte rate (sample rate * block align) */
                view.setUint32(28, sampleRate * 4, true);
                /* block align (channel count * bytes per sample) */
                view.setUint16(32, numChannels * 2, true);
                /* bits per sample */
                view.setUint16(34, 16, true);
                /* data chunk identifier */
                writeString(view, 36, 'data');
                /* data chunk length */
                view.setUint32(40, samples.length * 2, true);

                floatTo16BitPCM(view, 44, samples);

                return view;
            }
        }, self);

        this.worker.postMessage({
            command: 'init',
            config: {
                sampleRate: this.context.sampleRate,
                numChannels: this.config.numChannels
            }
        });

        this.worker.onmessage = function (e) {
            var cb = _this.callbacks[e.data.command].pop();
            if (typeof cb == 'function') {
                cb(e.data.data);
            }
        };
    }

    _createClass(Recorder, [{
        key: 'record',
        value: function record() {
            this.recording = true;
        }
    }, {
        key: 'stop',
        value: function stop() {
            this.recording = false;
        }
    }, {
        key: 'clear',
        value: function clear() {
            this.worker.postMessage({ command: 'clear' });
        }
    }, {
        key: 'getBuffer',
        value: function getBuffer(cb) {
            cb = cb || this.config.callback;
            if (!cb) throw new Error('Callback not set');

            this.callbacks.getBuffer.push(cb);

            this.worker.postMessage({ command: 'getBuffer' });
        }
    }, {
        key: 'exportWAV',
        value: function exportWAV(cb, mimeType) {
            mimeType = mimeType || this.config.mimeType;
            cb = cb || this.config.callback;
            if (!cb) throw new Error('Callback not set');

            this.callbacks.exportWAV.push(cb);

            this.worker.postMessage({
                command: 'exportWAV',
                type: mimeType
            });
        }
    }], [{
        key: 'forceDownload',
        value: function forceDownload(blob, filename) {
            var url = (window.URL || window.webkitURL).createObjectURL(blob);
            var link = window.document.createElement('a');
            link.href = url;
            link.download = filename || 'output.wav';
            var click = document.createEvent("Event");
            click.initEvent("click", true, true);
            link.dispatchEvent(click);
        }
    }]);

    return Recorder;
})();

exports.default = Recorder;

},{"inline-worker":3}],3:[function(require,module,exports){
"use strict";

module.exports = require("./inline-worker");
},{"./inline-worker":4}],4:[function(require,module,exports){
(function (global){
"use strict";

var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };

var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker);

var InlineWorker = (function () {
  function InlineWorker(func, self) {
    var _this = this;

    _classCallCheck(this, InlineWorker);

    if (WORKER_ENABLED) {
      var functionBody = func.toString().trim().match(/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/)[1];
      var url = global.URL.createObjectURL(new global.Blob([functionBody], { type: "text/javascript" }));

      return new global.Worker(url);
    }

    this.self = self;
    this.self.postMessage = function (data) {
      setTimeout(function () {
        _this.onmessage({ data: data });
      }, 0);
    };

    setTimeout(function () {
      func.call(self);
    }, 0);
  }

  _createClass(InlineWorker, {
    postMessage: {
      value: function postMessage(data) {
        var _this = this;

        setTimeout(function () {
          _this.self.onmessage({ data: data });
        }, 0);
      }
    }
  });

  return InlineWorker;
})();

module.exports = InlineWorker;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[1])(1)
});<commit_msg>taken off the debug<commit_after>
  var audio_context;
  var recorder;

  function startUserMedia(stream) {
    var input = audio_context.createMediaStreamSource(stream);
    recorder = new Recorder(input);
  }

  function startRecording(button) {
    recorder && recorder.record();
    //button.disabled = true;
    //button.nextElementSibling.disabled = false;
  }

  function stopRecording(button) {
    recorder && recorder.stop();
    //button.disabled = true;
    //button.previousElementSibling.disabled = false;
    // create WAV download link using audio data blob
    createDownloadLink();

    recorder.clear();
  }

  var success=function(data){
    data=$.parseJSON(data);
    console.log(data);
  };

  function createDownloadLink() {
    recorder && recorder.exportWAV(function(blob) {
      debug=false;
      if(debug){
        download='prova';}
      else{
          download = "RAP_"+new Date().toISOString() + '.wav';
          saveAs(blob, download);}
      queryParam=$.param({'namefile':download});

      event.preventDefault();
      var url = 'http://127.0.0.1?'+queryParam;
      var url = 'http://10.22.12.169?'+queryParam;

      $.get(url, success);

      //CALL FUNCTION
    });
  }

  window.onload = function init() {
    try {
      // webkit shim
      window.AudioContext = window.AudioContext || window.webkitAudioContext;
      navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
      window.URL = window.URL || window.webkitURL;

      audio_context = new AudioContext;

    } catch (e) {
      alert('No web audio support in this browser!');
    }

    navigator.getUserMedia({audio: true}, startUserMedia, function(e) {

    });
  };

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Recorder = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";

module.exports = require("./recorder").Recorder;

},{"./recorder":2}],2:[function(require,module,exports){
'use strict';

var _createClass = (function () {
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
        }
    }return function (Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
    };
})();

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.Recorder = undefined;

var _inlineWorker = require('inline-worker');

var _inlineWorker2 = _interopRequireDefault(_inlineWorker);

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : { default: obj };
}

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

var Recorder = exports.Recorder = (function () {
    function Recorder(source, cfg) {
        var _this = this;

        _classCallCheck(this, Recorder);

        this.config = {
            bufferLen: 4096,
            numChannels: 2,
            mimeType: 'audio/wav'
        };
        this.recording = false;
        this.callbacks = {
            getBuffer: [],
            exportWAV: []
        };

        Object.assign(this.config, cfg);
        this.context = source.context;
        this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, this.config.bufferLen, this.config.numChannels, this.config.numChannels);

        this.node.onaudioprocess = function (e) {
            if (!_this.recording) return;

            var buffer = [];
            for (var channel = 0; channel < _this.config.numChannels; channel++) {
                buffer.push(e.inputBuffer.getChannelData(channel));
            }
            _this.worker.postMessage({
                command: 'record',
                buffer: buffer
            });
        };

        source.connect(this.node);
        this.node.connect(this.context.destination); //this should not be necessary

        var self = {};
        this.worker = new _inlineWorker2.default(function () {
            var recLength = 0,
                recBuffers = [],
                sampleRate = undefined,
                numChannels = undefined;

            self.onmessage = function (e) {
                switch (e.data.command) {
                    case 'init':
                        init(e.data.config);
                        break;
                    case 'record':
                        record(e.data.buffer);
                        break;
                    case 'exportWAV':
                        exportWAV(e.data.type);
                        break;
                    case 'getBuffer':
                        getBuffer();
                        break;
                    case 'clear':
                        clear();
                        break;
                }
            };

            function init(config) {
                sampleRate = config.sampleRate;
                numChannels = config.numChannels;
                initBuffers();
            }

            function record(inputBuffer) {
                for (var channel = 0; channel < numChannels; channel++) {
                    recBuffers[channel].push(inputBuffer[channel]);
                }
                recLength += inputBuffer[0].length;
            }

            function exportWAV(type) {
                var buffers = [];
                for (var channel = 0; channel < numChannels; channel++) {
                    buffers.push(mergeBuffers(recBuffers[channel], recLength));
                }
                var interleaved = undefined;
                if (numChannels === 2) {
                    interleaved = interleave(buffers[0], buffers[1]);
                } else {
                    interleaved = buffers[0];
                }
                var dataview = encodeWAV(interleaved);
                var audioBlob = new Blob([dataview], { type: type });

                self.postMessage({ command: 'exportWAV', data: audioBlob });
            }

            function getBuffer() {
                var buffers = [];
                for (var channel = 0; channel < numChannels; channel++) {
                    buffers.push(mergeBuffers(recBuffers[channel], recLength));
                }
                self.postMessage({ command: 'getBuffer', data: buffers });
            }

            function clear() {
                recLength = 0;
                recBuffers = [];
                initBuffers();
            }

            function initBuffers() {
                for (var channel = 0; channel < numChannels; channel++) {
                    recBuffers[channel] = [];
                }
            }

            function mergeBuffers(recBuffers, recLength) {
                var result = new Float32Array(recLength);
                var offset = 0;
                for (var i = 0; i < recBuffers.length; i++) {
                    result.set(recBuffers[i], offset);
                    offset += recBuffers[i].length;
                }
                return result;
            }

            function interleave(inputL, inputR) {
                var length = inputL.length + inputR.length;
                var result = new Float32Array(length);

                var index = 0,
                    inputIndex = 0;

                while (index < length) {
                    result[index++] = inputL[inputIndex];
                    result[index++] = inputR[inputIndex];
                    inputIndex++;
                }
                return result;
            }

            function floatTo16BitPCM(output, offset, input) {
                for (var i = 0; i < input.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, input[i]));
                    output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }

            function writeString(view, offset, string) {
                for (var i = 0; i < string.length; i++) {
                    view.setUint8(offset + i, string.charCodeAt(i));
                }
            }

            function encodeWAV(samples) {
                var buffer = new ArrayBuffer(44 + samples.length * 2);
                var view = new DataView(buffer);

                /* RIFF identifier */
                writeString(view, 0, 'RIFF');
                /* RIFF chunk length */
                view.setUint32(4, 36 + samples.length * 2, true);
                /* RIFF type */
                writeString(view, 8, 'WAVE');
                /* format chunk identifier */
                writeString(view, 12, 'fmt ');
                /* format chunk length */
                view.setUint32(16, 16, true);
                /* sample format (raw) */
                view.setUint16(20, 1, true);
                /* channel count */
                view.setUint16(22, numChannels, true);
                /* sample rate */
                view.setUint32(24, sampleRate, true);
                /* byte rate (sample rate * block align) */
                view.setUint32(28, sampleRate * 4, true);
                /* block align (channel count * bytes per sample) */
                view.setUint16(32, numChannels * 2, true);
                /* bits per sample */
                view.setUint16(34, 16, true);
                /* data chunk identifier */
                writeString(view, 36, 'data');
                /* data chunk length */
                view.setUint32(40, samples.length * 2, true);

                floatTo16BitPCM(view, 44, samples);

                return view;
            }
        }, self);

        this.worker.postMessage({
            command: 'init',
            config: {
                sampleRate: this.context.sampleRate,
                numChannels: this.config.numChannels
            }
        });

        this.worker.onmessage = function (e) {
            var cb = _this.callbacks[e.data.command].pop();
            if (typeof cb == 'function') {
                cb(e.data.data);
            }
        };
    }

    _createClass(Recorder, [{
        key: 'record',
        value: function record() {
            this.recording = true;
        }
    }, {
        key: 'stop',
        value: function stop() {
            this.recording = false;
        }
    }, {
        key: 'clear',
        value: function clear() {
            this.worker.postMessage({ command: 'clear' });
        }
    }, {
        key: 'getBuffer',
        value: function getBuffer(cb) {
            cb = cb || this.config.callback;
            if (!cb) throw new Error('Callback not set');

            this.callbacks.getBuffer.push(cb);

            this.worker.postMessage({ command: 'getBuffer' });
        }
    }, {
        key: 'exportWAV',
        value: function exportWAV(cb, mimeType) {
            mimeType = mimeType || this.config.mimeType;
            cb = cb || this.config.callback;
            if (!cb) throw new Error('Callback not set');

            this.callbacks.exportWAV.push(cb);

            this.worker.postMessage({
                command: 'exportWAV',
                type: mimeType
            });
        }
    }], [{
        key: 'forceDownload',
        value: function forceDownload(blob, filename) {
            var url = (window.URL || window.webkitURL).createObjectURL(blob);
            var link = window.document.createElement('a');
            link.href = url;
            link.download = filename || 'output.wav';
            var click = document.createEvent("Event");
            click.initEvent("click", true, true);
            link.dispatchEvent(click);
        }
    }]);

    return Recorder;
})();

exports.default = Recorder;

},{"inline-worker":3}],3:[function(require,module,exports){
"use strict";

module.exports = require("./inline-worker");
},{"./inline-worker":4}],4:[function(require,module,exports){
(function (global){
"use strict";

var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };

var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker);

var InlineWorker = (function () {
  function InlineWorker(func, self) {
    var _this = this;

    _classCallCheck(this, InlineWorker);

    if (WORKER_ENABLED) {
      var functionBody = func.toString().trim().match(/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/)[1];
      var url = global.URL.createObjectURL(new global.Blob([functionBody], { type: "text/javascript" }));

      return new global.Worker(url);
    }

    this.self = self;
    this.self.postMessage = function (data) {
      setTimeout(function () {
        _this.onmessage({ data: data });
      }, 0);
    };

    setTimeout(function () {
      func.call(self);
    }, 0);
  }

  _createClass(InlineWorker, {
    postMessage: {
      value: function postMessage(data) {
        var _this = this;

        setTimeout(function () {
          _this.self.onmessage({ data: data });
        }, 0);
      }
    }
  });

  return InlineWorker;
})();

module.exports = InlineWorker;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[1])(1)
});<|endoftext|>


<commit_before>var NwBuilder = require("node-webkit-builder");
var asap = require("pdenodeify");

module.exports = function(options){
	var nwOptions = {};

	// Convert the options to camelcase
	Object.keys(options).forEach(function(optName){
		nwOptions[toCamelcase(optName)] = options[optName];
	});

	// TODO fix this
	//nwOptions.files = this.filesSrc;

	var nw = new NwBuilder(nwOptions);

	nw.on("log", function(msg){
		console.error(msg);
	});

	var build = nw.build.bind(nw);
	return asap(build)();
};

function toCamelcase(str) {
	return str.replace(/\_+([a-z])/g, function (x, chr) { return chr.toUpperCase(); });
}
<commit_msg>Copy files from the bundlesPath<commit_after>var NwBuilder = require("node-webkit-builder");
var asap = require("pdenodeify");
var path = require("path");

module.exports = function(options, buildResult){
	var nwOptions = {};

	// Convert the options to camelcase
	Object.keys(options).forEach(function(optName){
		nwOptions[toCamelcase(optName)] = options[optName];
	});

	// TODO fix this
	//nwOptions.files = this.filesSrc;

	var nw = new NwBuilder(nwOptions);

	nw.on("log", function(msg){
		console.error(msg);
	});

	// Set the bundlesPath as a file to use.
	var files = nwOptions.files || [];
	if(buildResult && buildResult.bundlesPath) {
		var bundlesPath = buildResult.bundlesPath;
		files.unshift(path.join(bundlesPath, "**/*"));
	}
	nwOptions.files = files;

	var build = nw.build.bind(nw);
	return asap(build)();
};

function toCamelcase(str) {
	return str.replace(/\_+([a-z])/g, function (x, chr) { return chr.toUpperCase(); });
}
<|endoftext|>


<commit_before>// FIXME: make these constants configurable

var RADIUS_SCALE = 10;

var API_URL = "http://localhost:3000/api/v1/";

// If you want to use hard-coded data, change to false
var USE_API = true;

// We obtain a list of meetings attended by a given individual or a
// given organisation / department, and then visualise who they met
// with as bubbles, where the size of the bubble is how often they met
// with them.
var MEETING_SOURCE_TYPE = "organisations"; // (Only used when USE_API is true.)

// You'll need to change this to a valid organisation id within your
// database:
var MEETING_SOURCE_ID = 194;

// This is what the bubbles represent:
var MEETING_TARGET_TYPE = "government-people";

var d3 = require('d3');
var d3Chart = {
  create: function(reactComponent, props, state) {
    var svg = d3.select(reactComponent).append('svg');
    this.reactComponent = reactComponent;
    this.svg = svg;
    svg.attr('class', 'd3')
      .attr('width', props.width)
      .attr('height', props.height);

    svg.append('g')
      .attr('class', 'd3-points');

    if (USE_API) {
      var url = API_URL + "organisations/194/meetings?include=" + MEETING_TARGET_TYPE;
      d3.request(url)
        .mimeType("application/json")
        .header("Accept", "application/vnd.api+json")
        .response(function(xhr) { return JSON.parse(xhr.responseText); })
        .get(this.dataLoaded);
    }
    else {
      this.dataLoaded(null, state.data);
    }
  },

  // Return an array of objects, each containing the name of the
  // target and the number of meetings with that target.
  //
  // For example if meetingTargetType is "government-people" then each
  // object will represent a person in government, and a count of the
  // number of meetings which targeted that person.
  countMeetingsByTarget: function(json, meetingTargetType) {
    var data = json["data"];

    var counts = {};
    for (var i = 0; i < data.length; i++) {
      var meeting = data[i];
      var rels = meeting["relationships"];
      var targets = rels[meetingTargetType]["data"];
      // For some reason, the same target can appear multiple times in
      // the relationship data for a given meeting, so we have to
      // de-duplicate here.
      var targetCounts = {};
      for (var j = 0; j < targets.length; j++) {
        var target = targets[j];
        targetCounts[target["id"]] = (targetCounts[target["id"]] || 0) + 1;
      }
      for (var targetId in targetCounts) {
        counts[targetId] = (counts[targetId] || 0) + 1;
      }
    }

    return Object.keys(counts).map(function(targetId, i) {
      return { targetId: targetId, meetingCount: counts[targetId] };
    });
  },

  // The "included" section of the JSON API's response is an array of
  // objects, each representing the target of a meeting.  Transform
  // this into an object keyed by target id, so we can efficiently map
  // target ids to names or any other attributes of the target which
  // we might want to visualise.
  meetingTargets: function(included) {
    var targets = {};
    for (var i = 0; i < included.length; i++) {
      var targetData = included[i];
      var targetId = targetData["id"];
      var targetName = targetData["attributes"]["name"];
      targets[targetId] = {name: targetName};
    }
    return targets;
  },

  dataLoaded: function(error, json) {
    if (error) {
      // FIXME: make this more elegant
      alert("Error fetching data from API: " + error);
    }

    var targets =
        USE_API ?
          d3Chart.meetingTargets(json["included"])
        : json["targets"];

    var meetingCounts =
        USE_API ?
          d3Chart.countMeetingsByTarget(json, MEETING_TARGET_TYPE)
        : json["meetingCounts"];

    d3Chart.lookupTargetNames(meetingCounts, targets);

    d3Chart.getSvgDimensions();
    d3Chart.initBubbleCoordsRadius(meetingCounts, d3Chart.svg_el);

    d3Chart.force = d3Chart.initForceSimulation(meetingCounts);

    d3Chart.update(d3Chart.reactComponent, meetingCounts);
  },

  lookupTargetNames: function(meetingCounts, targets) {
    for (var i = 0; i < meetingCounts.length; i++) {
      var meetingCount = meetingCounts[i];
      var target = targets[meetingCount["targetId"]];
      meetingCount["targetName"] = target["name"];
    }
  },

  getSvgDimensions: function() {
    this.svg_el = this.svg.node();
    this.svg_el.pixelWidth = this.svg.style("width").replace("px", "");
    this.svg_el.pixelHeight = this.svg.style("height").replace("px", "");
    this.svg_el.centreX = this.svg_el.pixelWidth / 2;
    this.svg_el.centreY = this.svg_el.pixelHeight / 2;
  },

  initBubbleCoordsRadius: function(data, svg_el) {
    for (var i = 0; i < data.length; i++) {
      data[i].radius = data[i].meetingCount * RADIUS_SCALE;
      data[i].x = Math.random() * svg_el.pixelWidth;
      data[i].y = Math.random() * svg_el.pixelHeight;
    }
  },

  translate: function(x, y) {
    return "translate(" + x + "," + y + ")";
  },

  positionBubble: function(d, i) {
    var svg_el = d3Chart.svg_el;
    return d3Chart.translate(d.x, d.y);
  },

  bubbleRadius: function(d, i) {
    return d.radius + 2;
  },

  initForceSimulation: function(data) {
    return d3.forceSimulation(data)
      //.force("charge", d3.forceManyBody().strength(-1000))
      .force("centerX", d3.forceX(d3Chart.svg_el.centreX))
      .force("centerY", d3.forceY(d3Chart.svg_el.centreY))
      .force("collide", d3.forceCollide()
                          .radius(d3Chart.bubbleRadius)
                          .iterations(3))
      .force("center", d3.forceCenter(d3Chart.svg_el.centreX,
                                      d3Chart.svg_el.centreY))
      .on("tick", this.tickHandler);
  },

  tickHandler: function() {
    d3Chart.updateBubblePositions();
  },

  updateBubblePositions: function() {
      this.bubbles.attr("transform", this.positionBubble);
  },

  update: function(reactComponent, data) {
    var g = d3.select(reactComponent).select('.d3-points');

    // var xScale = d3.scaleLinear()
    //   .domain([])
    //   .range();

    var tooltip = d3.select(".chart").append("div")
                .attr("class", "tooltip")
                .style("opacity", 0);

    this.bubbles = g.selectAll('g.bubble')
        .data(data)
      .enter().append("g")
        .attr("class", "bubble")
        .on("mouseover", function(d) {
           tooltip.transition()
             .duration(200)
             .style("opacity", 1);
           tooltip.html("" + d.targetName + ""+ "
 " + "Meetings: " + d.meetingCount)
             .style("left", (d3.event.pageX) + "px")
             .style("top", (d3.event.pageY) + "px");
           })
        .on("mousemove", function() {
          tooltip.style("left", (d3.event.pageX ) + "px")
            .style("top", (d3.event.pageY) + "px");
        })
        .on("mouseout", function(d) {
           tooltip.transition()
             .duration(200)
             .style("opacity", 0);
           });

    this.updateBubblePositions();

    this.bubbles.append("circle")
      .attr("r", function(d) {return d.radius;});

    this.bubbles.append("text")
      .text(function(d) {return d.targetName;})
      .style("font-size", function(d) { return Math.min(2*d.radius, (2 * d.radius - 8) / this.getComputedTextLength() * 10) + "px"; })
  }
};

module.exports = d3Chart;
<commit_msg>ui: fix API URL construction to use constants<commit_after>// FIXME: make these constants configurable

var RADIUS_SCALE = 10;

var API_URL = "http://localhost:3000/api/v1/";

// If you want to use hard-coded data, change to false
var USE_API = true;

// We obtain a list of meetings attended by a given individual or a
// given organisation / department, and then visualise who they met
// with as bubbles, where the size of the bubble is how often they met
// with them.
var MEETING_SOURCE_TYPE = "organisations"; // (Only used when USE_API is true.)

// You'll need to change this to a valid organisation id within your
// database:
var MEETING_SOURCE_ID = 194;

// This is what the bubbles represent:
var MEETING_TARGET_TYPE = "government-people";

var d3 = require('d3');
var d3Chart = {
  create: function(reactComponent, props, state) {
    var svg = d3.select(reactComponent).append('svg');
    this.reactComponent = reactComponent;
    this.svg = svg;
    svg.attr('class', 'd3')
      .attr('width', props.width)
      .attr('height', props.height);

    svg.append('g')
      .attr('class', 'd3-points');

    if (USE_API) {
      var url = API_URL + MEETING_SOURCE_TYPE + "/" + MEETING_SOURCE_ID +
          "/meetings?include=" + MEETING_TARGET_TYPE;
      d3.request(url)
        .mimeType("application/json")
        .header("Accept", "application/vnd.api+json")
        .response(function(xhr) { return JSON.parse(xhr.responseText); })
        .get(this.dataLoaded);
    }
    else {
      this.dataLoaded(null, state.data);
    }
  },

  // Return an array of objects, each containing the name of the
  // target and the number of meetings with that target.
  //
  // For example if meetingTargetType is "government-people" then each
  // object will represent a person in government, and a count of the
  // number of meetings which targeted that person.
  countMeetingsByTarget: function(json, meetingTargetType) {
    var data = json["data"];

    var counts = {};
    for (var i = 0; i < data.length; i++) {
      var meeting = data[i];
      var rels = meeting["relationships"];
      var targets = rels[meetingTargetType]["data"];
      // For some reason, the same target can appear multiple times in
      // the relationship data for a given meeting, so we have to
      // de-duplicate here.
      var targetCounts = {};
      for (var j = 0; j < targets.length; j++) {
        var target = targets[j];
        targetCounts[target["id"]] = (targetCounts[target["id"]] || 0) + 1;
      }
      for (var targetId in targetCounts) {
        counts[targetId] = (counts[targetId] || 0) + 1;
      }
    }

    return Object.keys(counts).map(function(targetId, i) {
      return { targetId: targetId, meetingCount: counts[targetId] };
    });
  },

  // The "included" section of the JSON API's response is an array of
  // objects, each representing the target of a meeting.  Transform
  // this into an object keyed by target id, so we can efficiently map
  // target ids to names or any other attributes of the target which
  // we might want to visualise.
  meetingTargets: function(included) {
    var targets = {};
    for (var i = 0; i < included.length; i++) {
      var targetData = included[i];
      var targetId = targetData["id"];
      var targetName = targetData["attributes"]["name"];
      targets[targetId] = {name: targetName};
    }
    return targets;
  },

  dataLoaded: function(error, json) {
    if (error) {
      // FIXME: make this more elegant
      alert("Error fetching data from API: " + error);
    }

    var targets =
        USE_API ?
          d3Chart.meetingTargets(json["included"])
        : json["targets"];

    var meetingCounts =
        USE_API ?
          d3Chart.countMeetingsByTarget(json, MEETING_TARGET_TYPE)
        : json["meetingCounts"];

    d3Chart.lookupTargetNames(meetingCounts, targets);

    d3Chart.getSvgDimensions();
    d3Chart.initBubbleCoordsRadius(meetingCounts, d3Chart.svg_el);

    d3Chart.force = d3Chart.initForceSimulation(meetingCounts);

    d3Chart.update(d3Chart.reactComponent, meetingCounts);
  },

  lookupTargetNames: function(meetingCounts, targets) {
    for (var i = 0; i < meetingCounts.length; i++) {
      var meetingCount = meetingCounts[i];
      var target = targets[meetingCount["targetId"]];
      meetingCount["targetName"] = target["name"];
    }
  },

  getSvgDimensions: function() {
    this.svg_el = this.svg.node();
    this.svg_el.pixelWidth = this.svg.style("width").replace("px", "");
    this.svg_el.pixelHeight = this.svg.style("height").replace("px", "");
    this.svg_el.centreX = this.svg_el.pixelWidth / 2;
    this.svg_el.centreY = this.svg_el.pixelHeight / 2;
  },

  initBubbleCoordsRadius: function(data, svg_el) {
    for (var i = 0; i < data.length; i++) {
      data[i].radius = data[i].meetingCount * RADIUS_SCALE;
      data[i].x = Math.random() * svg_el.pixelWidth;
      data[i].y = Math.random() * svg_el.pixelHeight;
    }
  },

  translate: function(x, y) {
    return "translate(" + x + "," + y + ")";
  },

  positionBubble: function(d, i) {
    var svg_el = d3Chart.svg_el;
    return d3Chart.translate(d.x, d.y);
  },

  bubbleRadius: function(d, i) {
    return d.radius + 2;
  },

  initForceSimulation: function(data) {
    return d3.forceSimulation(data)
      //.force("charge", d3.forceManyBody().strength(-1000))
      .force("centerX", d3.forceX(d3Chart.svg_el.centreX))
      .force("centerY", d3.forceY(d3Chart.svg_el.centreY))
      .force("collide", d3.forceCollide()
                          .radius(d3Chart.bubbleRadius)
                          .iterations(3))
      .force("center", d3.forceCenter(d3Chart.svg_el.centreX,
                                      d3Chart.svg_el.centreY))
      .on("tick", this.tickHandler);
  },

  tickHandler: function() {
    d3Chart.updateBubblePositions();
  },

  updateBubblePositions: function() {
      this.bubbles.attr("transform", this.positionBubble);
  },

  update: function(reactComponent, data) {
    var g = d3.select(reactComponent).select('.d3-points');

    // var xScale = d3.scaleLinear()
    //   .domain([])
    //   .range();

    var tooltip = d3.select(".chart").append("div")
                .attr("class", "tooltip")
                .style("opacity", 0);

    this.bubbles = g.selectAll('g.bubble')
        .data(data)
      .enter().append("g")
        .attr("class", "bubble")
        .on("mouseover", function(d) {
           tooltip.transition()
             .duration(200)
             .style("opacity", 1);
           tooltip.html("" + d.targetName + ""+ "
 " + "Meetings: " + d.meetingCount)
             .style("left", (d3.event.pageX) + "px")
             .style("top", (d3.event.pageY) + "px");
           })
        .on("mousemove", function() {
          tooltip.style("left", (d3.event.pageX ) + "px")
            .style("top", (d3.event.pageY) + "px");
        })
        .on("mouseout", function(d) {
           tooltip.transition()
             .duration(200)
             .style("opacity", 0);
           });

    this.updateBubblePositions();

    this.bubbles.append("circle")
      .attr("r", function(d) {return d.radius;});

    this.bubbles.append("text")
      .text(function(d) {return d.targetName;})
      .style("font-size", function(d) { return Math.min(2*d.radius, (2 * d.radius - 8) / this.getComputedTextLength() * 10) + "px"; })
  }
};

module.exports = d3Chart;
<|endoftext|>