<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

module.exports = {
    id: "android",
    initialize:function() {
        var channel = require("cordova/channel"),
            cordova = require('cordova'),
            exec = require('cordova/exec');

        // Inject a listener for the backbutton on the document.
        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
        backButtonChannel.onHasSubscribersChange = function() {
            // If we just attached the first handler or detached the last handler,
            // let native know we need to override the back button.
            exec(null, null, "App", "overrideBackbutton", [this.numHandlers == 1]);
        };

        // Add hardware MENU and SEARCH button handlers
        cordova.addDocumentEventHandler('menubutton');
        cordova.addDocumentEventHandler('searchbutton');

        // Figure out if we need to shim-in localStorage and WebSQL
        // support from the native side.
        var storage = require('cordova/plugin/android/storage');

        // First patch WebSQL if necessary
        if (typeof window.openDatabase == 'undefined') {
            // Not defined, create an openDatabase function for all to use!
            window.openDatabase = storage.openDatabase;
        } else {
            // Defined, but some Android devices will throw a SECURITY_ERR -
            // so we wrap the whole thing in a try-catch and shim in our own
            // if the device has Android bug 16175.
            var originalOpenDatabase = window.openDatabase;
            window.openDatabase = function(name, version, desc, size) {
                var db = null;
                try {
                    db = originalOpenDatabase(name, version, desc, size);
                }
                catch (ex) {
                    if (ex.code === 18) {
                        db = null;
                    } else {
                        throw ex;
                    }
                }

                if (db === null) {
                    return storage.openDatabase(name, version, desc, size);
                }
                else {
                    return db;
                }

            };
        }

        // Patch localStorage if necessary
        if (typeof window.localStorage == 'undefined' || window.localStorage === null) {
            window.localStorage = new storage.CupcakeLocalStorage();
        }

        // Let native code know we are all done on the JS side.
        // Native code will then un-hide the WebView.
        channel.join(function() {
            exec(null, null, "App", "show", []);
        }, [channel.onCordovaReady]);
    },
    clobbers: {
        navigator: {
            children: {
                app:{
                    path: "cordova/plugin/android/app"
                }
            }
        },
        File: { // exists natively on Android WebView, override
            path: "cordova/plugin/File"
        },
        FileReader: { // exists natively on Android WebView, override
            path: "cordova/plugin/FileReader"
        },
        FileError: { //exists natively on Android WebView on Android 4.x
            path: "cordova/plugin/FileError"
        },
        MediaError: { // exists natively on Android WebView on Android 4.x
            path: "cordova/plugin/MediaError"
        }
    },
    merges: {
        device: {
            path: 'cordova/plugin/android/device'
        },
        navigator: {
            children: {
                notification: {
                    path: 'cordova/plugin/android/notification'
                }
            }
        }
    }
};
<commit_msg>CB-1508: Implement InAppBrowser feature for Android<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

module.exports = {
    id: "android",
    initialize:function() {
        var channel = require("cordova/channel"),
            cordova = require('cordova'),
            exec = require('cordova/exec');

        // Inject a listener for the backbutton on the document.
        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
        backButtonChannel.onHasSubscribersChange = function() {
            // If we just attached the first handler or detached the last handler,
            // let native know we need to override the back button.
            exec(null, null, "App", "overrideBackbutton", [this.numHandlers == 1]);
        };

        // Add hardware MENU and SEARCH button handlers
        cordova.addDocumentEventHandler('menubutton');
        cordova.addDocumentEventHandler('searchbutton');

        // Figure out if we need to shim-in localStorage and WebSQL
        // support from the native side.
        var storage = require('cordova/plugin/android/storage');

        // First patch WebSQL if necessary
        if (typeof window.openDatabase == 'undefined') {
            // Not defined, create an openDatabase function for all to use!
            window.openDatabase = storage.openDatabase;
        } else {
            // Defined, but some Android devices will throw a SECURITY_ERR -
            // so we wrap the whole thing in a try-catch and shim in our own
            // if the device has Android bug 16175.
            var originalOpenDatabase = window.openDatabase;
            window.openDatabase = function(name, version, desc, size) {
                var db = null;
                try {
                    db = originalOpenDatabase(name, version, desc, size);
                }
                catch (ex) {
                    if (ex.code === 18) {
                        db = null;
                    } else {
                        throw ex;
                    }
                }

                if (db === null) {
                    return storage.openDatabase(name, version, desc, size);
                }
                else {
                    return db;
                }

            };
        }

        // Patch localStorage if necessary
        if (typeof window.localStorage == 'undefined' || window.localStorage === null) {
            window.localStorage = new storage.CupcakeLocalStorage();
        }

        // Let native code know we are all done on the JS side.
        // Native code will then un-hide the WebView.
        channel.join(function() {
            exec(null, null, "App", "show", []);
        }, [channel.onCordovaReady]);
    },
    clobbers: {
        navigator: {
            children: {
                app:{
                    path: "cordova/plugin/android/app"
                }
            }
        },
        File: { // exists natively on Android WebView, override
            path: "cordova/plugin/File"
        },
        FileReader: { // exists natively on Android WebView, override
            path: "cordova/plugin/FileReader"
        },
        FileError: { //exists natively on Android WebView on Android 4.x
            path: "cordova/plugin/FileError"
        },
        MediaError: { // exists natively on Android WebView on Android 4.x
            path: "cordova/plugin/MediaError"
        },
        open: {
            path: "cordova/plugin/InAppBrowser"
        }
    },
    merges: {
        device: {
            path: 'cordova/plugin/android/device'
        },
        navigator: {
            children: {
                notification: {
                    path: 'cordova/plugin/android/notification'
                }
            }
        }
    }
};
<|endoftext|>


<commit_before>var _      = require('lodash');
var async  = require('async');
var fs     = require('fs');
var jazz   = require('jazz');
var yamljs = require('yaml-js');

function load(file, cb) {

  var params = {};
  var tests  = [];

  var data = yamljs.load(fs.readFileSync(file).toString());
  data.forEach(function (item) {
    if (item.params) {
      params = _.extend(params, item.params);
    } else {
      tests.push(item);
    }
  });

  function iter(test, cb) {

    test.file = file;

    jazz.compile(test.command).process(params, function (data) {
      test.command = data;
      cb();
    });
  }

  async.each(tests, iter, function (err, results) {
    cb(err, tests);
  });
}

exports.load = load;<commit_msg>Add test.load doco.<commit_after>var _      = require('lodash');
var async  = require('async');
var fs     = require('fs');
var jazz   = require('jazz');
var yamljs = require('yaml-js');

/**
 * Load a test YAML file.
 *
 * @param {String} file: test file
 * @param {Function} cb: standard cb(err, result) callback
 */
function load(file, cb) {

  var params = {};
  var tests  = [];

  var data = yamljs.load(fs.readFileSync(file).toString());
  data.forEach(function (item) {
    if (item.params) {
      params = _.extend(params, item.params);
    } else {
      tests.push(item);
    }
  });

  function iter(test, cb) {

    test.file = file;

    jazz.compile(test.command).process(params, function (data) {
      test.command = data;
      cb();
    });
  }

  async.each(tests, iter, function (err, results) {
    cb(err, tests);
  });
}

exports.load = load;<|endoftext|>


<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) {
                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 length;

                    // map.getView().getProjection(),
                    // var projection =
                    var projection = MapService.getMap().getView().getProjection();
                    var geodesic = true;

                    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 > 100) {
                        output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km';
                    } else {
                        output = (Math.round(length * 100) / 100) + ' ' + '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'
                            })
                        })
                      }));
                    }

                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) {
                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) {
                var length = calculateLength(geometry, projection, geodesic);
                var output;
                if (length > 100) {
                    output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km';
                } else {
                    output = (Math.round(length * 100) / 100) + ' ' + 'm';
                }
                return output;
            };

            var formatAreaResult = function(geometry, projection, geodesic) {
                var area = calculateArea(geometry, projection, geodesic);
                var output;
                if (area > 10000) {
                    output = (Math.round(area / 10000 * 100) / 100) +
                     ' ' + 'ha';
                } else {
                    output = Math.round(area) +
                     ' ' + 'm2';
                }

                // 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 > 100) {
                    output += '
' + (Math.round(length / 1000 * 100) / 100) + ' ' + 'km';
                } else {
                    output += '
' + (Math.round(length * 100) / 100) + ' ' + '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) {
                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);
                    measureOverlay.setPosition(geometry.getLastCoordinate());
                });
                return modify;
            };

            var createDrawInteraction = function(measureSource, measureType, measureOverlay, measureResultCallback, projection, geodesic) {
                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);
                        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 || measureStyle,
                        zIndex: 2000
                    });

                    var layerOptions = {
                        title: 'lineMeasureLayer',
                        name: 'lineMeasureLayer',
                        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
                    );

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

                    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>measure: make label segments optional<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 length;

                    // map.getView().getProjection(),
                    // var projection =
                    var projection = MapService.getMap().getView().getProjection();
                    var geodesic = true;

                    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 > 100) {
                        output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km';
                    } else {
                        output = (Math.round(length * 100) / 100) + ' ' + '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) {
                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) {
                var length = calculateLength(geometry, projection, geodesic);
                var output;
                if (length > 100) {
                    output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km';
                } else {
                    output = (Math.round(length * 100) / 100) + ' ' + 'm';
                }
                return output;
            };

            var formatAreaResult = function(geometry, projection, geodesic) {
                var area = calculateArea(geometry, projection, geodesic);
                var output;
                if (area > 10000) {
                    output = (Math.round(area / 10000 * 100) / 100) +
                     ' ' + 'ha';
                } else {
                    output = Math.round(area) +
                     ' ' + 'm2';
                }

                // 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 > 100) {
                    output += '
' + (Math.round(length / 1000 * 100) / 100) + ' ' + 'km';
                } else {
                    output += '
' + (Math.round(length * 100) / 100) + ' ' + '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) {
                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);
                    measureOverlay.setPosition(geometry.getLastCoordinate());
                });
                return modify;
            };

            var createDrawInteraction = function(measureSource, measureType, measureOverlay, measureResultCallback, projection, geodesic) {
                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);
                        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: 'lineMeasureLayer',
                        name: 'lineMeasureLayer',
                        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
                    );

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

                    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 config   = require(__dirname + '/config.js');
var imdb     = require('imdb-api');
var tomatoes = require('tomatoes');
var tomatoe  = tomatoes(config.rottenTomatoesApiKey);  // API Key

var catchAllInfos = function(titles, callback) {
    // start adding Movie-Info from rottentomatoes to each item
    // this is beeing done in timesteps to prevent having more
    // queries to the tomatoes-api than allowed
    var counter = 0;
    (function getInfo(i){
        setTimeout(function(){
          getTomatoesFromTitle(Object.keys(titles)[i-1], function(err, info, title){
              if (err !== undefined) {
                  console.log(err);
                  titles[title].info = {};
              }
              else {
                titles[title].info = info[getRightInfo(info[key].year, info[key])];
              }
              counter++;
              if (counter === Object.keys(titles).length) {
                  callback(titles);
              }
          });
          if (--i) getInfo(i);
        }, 1000/config.tomatoesMaxQueriesPerSecond);  // Reduce queries/second
    })(Object.keys(titles).length);
};
exports.catchAllInfos = catchAllInfos;

var getRightInfo = function(year, info) {
    for (var key in info) {
        if (year === info[key].year)
            return key;
    }
    return 0;
};
exports.getRightInfo = getRightInfo;

var getTomatoesFromTitle = function(title, callback) {
    tomatoe.search(title, function(err, info) {
        callback(err, info, title);
    });
};
exports.getTomatoesFromTitle = getTomatoesFromTitle;

var getTomatoesFromString = function(string, callback) {
    var title = getFilmTitleFromString(string);
    if (title) {
        tomatoe.search(title, function(err, results) {
            callback(err, results);
        });
    }
    else {
        callback(false);
    }
};
exports.getTomatoesFromString = getTomatoesFromString;

var yearFromString = function(title) {
    title = title.replace(/\./g, " "); // for "Fathers.Day.2011.DC.BDRiP" strings
    var year = title.match(/(.+)\s((19|20)\d{2})\s?/i);
    if (year !== null && year[2] !== undefined)
        return year[2];
    return false;
};
exports.yearFromString = yearFromString;

var getFilmTitleFromString = function(string) {
    string = string.replace(/\./g, " "); // for "Fathers.Day.2011.DC.BDRiP" strings
    string = string.replace(/\’/g, "");
    var title = string.match(/(.+)\s(19|20)\d{2}\s?/i); // everything til the first 4-digit year followed by space or nothing
    if (title !== null)
        return title[1];
    return string;
};
exports.getFilmTitleFromString = getFilmTitleFromString;

exports.getResFromString = function(string) {
    var results = string.match(/720p|1080p|480p|hdtv/i);
    if (results !== null)
        return results[0];
    return false;
};

exports.getImdbFromTitle = function(title, callback) {
    imdb.getReq({ name: title}, function(err, info) {
        callback(err, info);
    });
};

exports.getImdbFromString = function(string, callback) {
    var title = getFilmTitleFromString(string);
    if (title) {
        imdb.getReq({ name: title}, function(err, info) {
            callback(err, info);
        });
    }
    else {
        callback(false)
    }
};<commit_msg>added getRightInfo<commit_after>var config   = require(__dirname + '/config.js');
var imdb     = require('imdb-api');
var tomatoes = require('tomatoes');
var tomatoe  = tomatoes(config.rottenTomatoesApiKey);  // API Key

var catchAllInfos = function(titles, callback) {
    // start adding Movie-Info from rottentomatoes to each item
    // this is beeing done in timesteps to prevent having more
    // queries to the tomatoes-api than allowed
    var counter = 0;
    (function getInfo(i){
        setTimeout(function(){
          getTomatoesFromTitle(Object.keys(titles)[i-1], function(err, info, title){
              if (err !== undefined) {
                  console.log(err);
                  titles[title].info = {};
              }
              else {
                titles[title].info = info[getRightInfo(titles[title].year, info)];
              }
              counter++;
              if (counter === Object.keys(titles).length) {
                  callback(titles);
              }
          });
          if (--i) getInfo(i);
        }, 1000/config.tomatoesMaxQueriesPerSecond);  // Reduce queries/second
    })(Object.keys(titles).length);
};
exports.catchAllInfos = catchAllInfos;

var getRightInfo = function(year, info) {
    for (var key in info) {
        if (year === info[key].year)
            return key;
    }
    return 0;
};
exports.getRightInfo = getRightInfo;

var getTomatoesFromTitle = function(title, callback) {
    tomatoe.search(title, function(err, info) {
        callback(err, info, title);
    });
};
exports.getTomatoesFromTitle = getTomatoesFromTitle;

var getTomatoesFromString = function(string, callback) {
    var title = getFilmTitleFromString(string);
    if (title) {
        tomatoe.search(title, function(err, results) {
            callback(err, results);
        });
    }
    else {
        callback(false);
    }
};
exports.getTomatoesFromString = getTomatoesFromString;

var yearFromString = function(title) {
    title = title.replace(/\./g, " "); // for "Fathers.Day.2011.DC.BDRiP" strings
    var year = title.match(/(.+)\s((19|20)\d{2})\s?/i);
    if (year !== null && year[2] !== undefined)
        return year[2];
    return false;
};
exports.yearFromString = yearFromString;

var getFilmTitleFromString = function(string) {
    string = string.replace(/\./g, " "); // for "Fathers.Day.2011.DC.BDRiP" strings
    string = string.replace(/\’/g, "");
    var title = string.match(/(.+)\s(19|20)\d{2}\s?/i); // everything til the first 4-digit year followed by space or nothing
    if (title !== null)
        return title[1];
    return string;
};
exports.getFilmTitleFromString = getFilmTitleFromString;

exports.getResFromString = function(string) {
    var results = string.match(/720p|1080p|480p|hdtv/i);
    if (results !== null)
        return results[0];
    return false;
};

exports.getImdbFromTitle = function(title, callback) {
    imdb.getReq({ name: title}, function(err, info) {
        callback(err, info);
    });
};

exports.getImdbFromString = function(string, callback) {
    var title = getFilmTitleFromString(string);
    if (title) {
        imdb.getReq({ name: title}, function(err, info) {
            callback(err, info);
        });
    }
    else {
        callback(false)
    }
};<|endoftext|>


<commit_before>'use strict'

const {BrowserWindow, MenuItem, webContents} = require('electron')
const EventEmitter = require('events').EventEmitter
const v8Util = process.atomBinding('v8_util')
const bindings = process.atomBinding('menu')

// Automatically generated radio menu item's group id.
var nextGroupId = 0

// Search between separators to find a radio menu item and return its group id,
// otherwise generate a group id.
var generateGroupId = function (items, pos) {
  var i, item, j, k, ref1, ref2, ref3
  if (pos > 0) {
    for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
      item = items[i]
      if (item.type === 'radio') {
        return item.groupId
      }
      if (item.type === 'separator') {
        break
      }
    }
  } else if (pos < items.length) {
    for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
      item = items[i]
      if (item.type === 'radio') {
        return item.groupId
      }
      if (item.type === 'separator') {
        break
      }
    }
  }
  return ++nextGroupId
}

// Returns the index of item according to |id|.
var indexOfItemById = function (items, id) {
  var i, item, j, len
  for (i = j = 0, len = items.length; j < len; i = ++j) {
    item = items[i]
    if (item.id === id) {
      return i
    }
  }
  return -1
}

// Returns the index of where to insert the item according to |position|.
var indexToInsertByPosition = function (items, position) {
  var insertIndex
  if (!position) {
    return items.length
  }
  const [query, id] = position.split('=')
  insertIndex = indexOfItemById(items, id)
  if (insertIndex === -1 && query !== 'endof') {
    console.warn("Item with id '" + id + "' is not found")
    return items.length
  }
  switch (query) {
    case 'after':
      insertIndex++
      break
    case 'endof':

      // If the |id| doesn't exist, then create a new group with the |id|.
      if (insertIndex === -1) {
        items.push({
          id: id,
          type: 'separator'
        })
        insertIndex = items.length - 1
      }

      // Find the end of the group.
      insertIndex++
      while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
        insertIndex++
      }
  }
  return insertIndex
}

const Menu = bindings.Menu

Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)

Menu.prototype._init = function () {
  this.commandsMap = {}
  this.groupsMap = {}
  this.items = []
  this.delegate = {
    isCommandIdChecked: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.checked : undefined
    },
    isCommandIdEnabled: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.enabled : undefined
    },
    isCommandIdVisible: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.visible : undefined
    },
    getAcceleratorForCommandId: (commandId, useDefaultAccelerator) => {
      const command = this.commandsMap[commandId]
      if (command == null) return
      if (command.accelerator != null) return command.accelerator
      if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
    },
    getIconForCommandId: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.icon : undefined
    },
    executeCommand: (event, commandId) => {
      const command = this.commandsMap[commandId]
      if (command == null) return
      command.click(event, BrowserWindow.getFocusedWindow(), webContents.getFocusedWebContents())
    },
    menuWillShow: () => {
      // Make sure radio groups have at least one menu item seleted.
      var checked, group, id, j, len, radioItem, ref1
      ref1 = this.groupsMap
      for (id in ref1) {
        group = ref1[id]
        checked = false
        for (j = 0, len = group.length; j < len; j++) {
          radioItem = group[j]
          if (!radioItem.checked) {
            continue
          }
          checked = true
          break
        }
        if (!checked) {
          v8Util.setHiddenValue(group[0], 'checked', true)
        }
      }
    }
  }
}

Menu.prototype.popup = function (window, x, y, positioningItem) {
  let asyncPopup

  // menu.popup(x, y, positioningItem)
  if (window != null && (typeof window !== 'object' || window.constructor !== BrowserWindow)) {
    // Shift.
    positioningItem = y
    y = x
    x = window
    window = null
  }

  // menu.popup(window, {})
  if (x != null && typeof x === 'object') {
    const options = x
    x = options.x
    y = options.y
    positioningItem = options.positioningItem
    asyncPopup = options.async
  }

  // Default to showing in focused window.
  if (window == null) window = BrowserWindow.getFocusedWindow()

  // Default to showing under mouse location.
  if (typeof x !== 'number') x = -1
  if (typeof y !== 'number') y = -1

  // Default to not highlighting any item.
  if (typeof positioningItem !== 'number') positioningItem = -1

  // Default to synchronous for backwards compatibility.
  if (typeof asyncPopup !== 'boolean') asyncPopup = false

  this.popupAt(window, x, y, positioningItem, asyncPopup)
}

Menu.prototype.closePopup = function (window) {
  if (window == null || window.constructor !== BrowserWindow) {
    window = BrowserWindow.getFocusedWindow()
  }
  if (window != null) {
    this.closePopupAt(window.id)
  }
}

Menu.prototype.append = function (item) {
  return this.insert(this.getItemCount(), item)
}

Menu.prototype.insert = function (pos, item) {
  var base, name
  if ((item != null ? item.constructor : void 0) !== MenuItem) {
    throw new TypeError('Invalid item')
  }
  switch (item.type) {
    case 'normal':
      this.insertItem(pos, item.commandId, item.label)
      break
    case 'checkbox':
      this.insertCheckItem(pos, item.commandId, item.label)
      break
    case 'separator':
      this.insertSeparator(pos)
      break
    case 'submenu':
      this.insertSubMenu(pos, item.commandId, item.label, item.submenu)
      break
    case 'radio':
      // Grouping radio menu items.
      item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
      if ((base = this.groupsMap)[name = item.groupId] == null) {
        base[name] = []
      }
      this.groupsMap[item.groupId].push(item)

      // Setting a radio menu item should flip other items in the group.
      v8Util.setHiddenValue(item, 'checked', item.checked)
      Object.defineProperty(item, 'checked', {
        enumerable: true,
        get: function () {
          return v8Util.getHiddenValue(item, 'checked')
        },
        set: () => {
          var j, len, otherItem, ref1
          ref1 = this.groupsMap[item.groupId]
          for (j = 0, len = ref1.length; j < len; j++) {
            otherItem = ref1[j]
            if (otherItem !== item) {
              v8Util.setHiddenValue(otherItem, 'checked', false)
            }
          }
          return v8Util.setHiddenValue(item, 'checked', true)
        }
      })
      this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
  }
  if (item.sublabel != null) {
    this.setSublabel(pos, item.sublabel)
  }
  if (item.icon != null) {
    this.setIcon(pos, item.icon)
  }
  if (item.role != null) {
    this.setRole(pos, item.role)
  }

  // Make menu accessable to items.
  item.overrideReadOnlyProperty('menu', this)

  // Remember the items.
  this.items.splice(pos, 0, item)
  this.commandsMap[item.commandId] = item
}

// Force menuWillShow to be called
Menu.prototype._callMenuWillShow = function () {
  if (this.delegate != null) {
    this.delegate.menuWillShow()
  }
  this.items.forEach(function (item) {
    if (item.submenu != null) {
      item.submenu._callMenuWillShow()
    }
  })
}

var applicationMenu = null

Menu.setApplicationMenu = function (menu) {
  if (!(menu === null || menu.constructor === Menu)) {
    throw new TypeError('Invalid menu')
  }

  // Keep a reference.
  applicationMenu = menu
  if (process.platform === 'darwin') {
    if (menu === null) {
      return
    }
    menu._callMenuWillShow()
    bindings.setApplicationMenu(menu)
  } else {
    BrowserWindow.getAllWindows().forEach(function (window) {
      window.setMenu(menu)
    })
  }
}

Menu.getApplicationMenu = function () {
  return applicationMenu
}

Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder

Menu.buildFromTemplate = function (template) {
  var insertIndex, item, j, k, len, len1, menu, menuItem, positionedTemplate
  if (!Array.isArray(template)) {
    throw new TypeError('Invalid template for Menu')
  }
  positionedTemplate = []
  insertIndex = 0
  for (j = 0, len = template.length; j < len; j++) {
    item = template[j]
    if (item.position) {
      insertIndex = indexToInsertByPosition(positionedTemplate, item.position)
    } else {
      // If no |position| is specified, insert after last item.
      insertIndex++
    }
    positionedTemplate.splice(insertIndex, 0, item)
  }
  menu = new Menu()
  for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
    item = positionedTemplate[k]
    if (typeof item !== 'object') {
      throw new TypeError('Invalid template for MenuItem')
    }
    menuItem = new MenuItem(item)
    menu.append(menuItem)
  }
  return menu
}

module.exports = Menu
<commit_msg>add first pass at getMenuItemById<commit_after>'use strict'

const {BrowserWindow, MenuItem, webContents} = require('electron')
const EventEmitter = require('events').EventEmitter
const v8Util = process.atomBinding('v8_util')
const bindings = process.atomBinding('menu')

// Automatically generated radio menu item's group id.
var nextGroupId = 0

// Search between separators to find a radio menu item and return its group id,
// otherwise generate a group id.
var generateGroupId = function (items, pos) {
  var i, item, j, k, ref1, ref2, ref3
  if (pos > 0) {
    for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
      item = items[i]
      if (item.type === 'radio') {
        return item.groupId
      }
      if (item.type === 'separator') {
        break
      }
    }
  } else if (pos < items.length) {
    for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
      item = items[i]
      if (item.type === 'radio') {
        return item.groupId
      }
      if (item.type === 'separator') {
        break
      }
    }
  }
  return ++nextGroupId
}

// Returns the index of item according to |id|.
var indexOfItemById = function (items, id) {
  var i, item, j, len
  for (i = j = 0, len = items.length; j < len; i = ++j) {
    item = items[i]
    if (item.id === id) {
      return i
    }
  }
  return -1
}

// Returns the index of where to insert the item according to |position|.
var indexToInsertByPosition = function (items, position) {
  var insertIndex
  if (!position) {
    return items.length
  }
  const [query, id] = position.split('=')
  insertIndex = indexOfItemById(items, id)
  if (insertIndex === -1 && query !== 'endof') {
    console.warn("Item with id '" + id + "' is not found")
    return items.length
  }
  switch (query) {
    case 'after':
      insertIndex++
      break
    case 'endof':

      // If the |id| doesn't exist, then create a new group with the |id|.
      if (insertIndex === -1) {
        items.push({
          id: id,
          type: 'separator'
        })
        insertIndex = items.length - 1
      }

      // Find the end of the group.
      insertIndex++
      while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
        insertIndex++
      }
  }
  return insertIndex
}

const Menu = bindings.Menu

Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)

Menu.prototype._init = function () {
  this.commandsMap = {}
  this.groupsMap = {}
  this.items = []
  this.delegate = {
    isCommandIdChecked: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.checked : undefined
    },
    isCommandIdEnabled: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.enabled : undefined
    },
    isCommandIdVisible: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.visible : undefined
    },
    getAcceleratorForCommandId: (commandId, useDefaultAccelerator) => {
      const command = this.commandsMap[commandId]
      if (command == null) return
      if (command.accelerator != null) return command.accelerator
      if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
    },
    getIconForCommandId: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.icon : undefined
    },
    executeCommand: (event, commandId) => {
      const command = this.commandsMap[commandId]
      if (command == null) return
      command.click(event, BrowserWindow.getFocusedWindow(), webContents.getFocusedWebContents())
    },
    menuWillShow: () => {
      // Make sure radio groups have at least one menu item seleted.
      var checked, group, id, j, len, radioItem, ref1
      ref1 = this.groupsMap
      for (id in ref1) {
        group = ref1[id]
        checked = false
        for (j = 0, len = group.length; j < len; j++) {
          radioItem = group[j]
          if (!radioItem.checked) {
            continue
          }
          checked = true
          break
        }
        if (!checked) {
          v8Util.setHiddenValue(group[0], 'checked', true)
        }
      }
    }
  }
}

Menu.prototype.popup = function (window, x, y, positioningItem) {
  let asyncPopup

  // menu.popup(x, y, positioningItem)
  if (window != null && (typeof window !== 'object' || window.constructor !== BrowserWindow)) {
    // Shift.
    positioningItem = y
    y = x
    x = window
    window = null
  }

  // menu.popup(window, {})
  if (x != null && typeof x === 'object') {
    const options = x
    x = options.x
    y = options.y
    positioningItem = options.positioningItem
    asyncPopup = options.async
  }

  // Default to showing in focused window.
  if (window == null) window = BrowserWindow.getFocusedWindow()

  // Default to showing under mouse location.
  if (typeof x !== 'number') x = -1
  if (typeof y !== 'number') y = -1

  // Default to not highlighting any item.
  if (typeof positioningItem !== 'number') positioningItem = -1

  // Default to synchronous for backwards compatibility.
  if (typeof asyncPopup !== 'boolean') asyncPopup = false

  this.popupAt(window, x, y, positioningItem, asyncPopup)
}

Menu.prototype.closePopup = function (window) {
  if (window == null || window.constructor !== BrowserWindow) {
    window = BrowserWindow.getFocusedWindow()
  }
  if (window != null) {
    this.closePopupAt(window.id)
  }
}

Menu.prototype.getMenuItemById = function (items, id) {
  if (items instanceof Menu) {
    items = items.items;
  }
  let found = items.find(item => item.id === id) || false;
  for(let i = 0, length = items.length; !found && i < length; i++) {
    if (items[i].submenu) {
      found = this.getMenuItemById(items[i].submenu, id);
    }
  }
  return found;
};

Menu.prototype.append = function (item) {
  return this.insert(this.getItemCount(), item)
}

Menu.prototype.insert = function (pos, item) {
  var base, name
  if ((item != null ? item.constructor : void 0) !== MenuItem) {
    throw new TypeError('Invalid item')
  }
  switch (item.type) {
    case 'normal':
      this.insertItem(pos, item.commandId, item.label)
      break
    case 'checkbox':
      this.insertCheckItem(pos, item.commandId, item.label)
      break
    case 'separator':
      this.insertSeparator(pos)
      break
    case 'submenu':
      this.insertSubMenu(pos, item.commandId, item.label, item.submenu)
      break
    case 'radio':
      // Grouping radio menu items.
      item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
      if ((base = this.groupsMap)[name = item.groupId] == null) {
        base[name] = []
      }
      this.groupsMap[item.groupId].push(item)

      // Setting a radio menu item should flip other items in the group.
      v8Util.setHiddenValue(item, 'checked', item.checked)
      Object.defineProperty(item, 'checked', {
        enumerable: true,
        get: function () {
          return v8Util.getHiddenValue(item, 'checked')
        },
        set: () => {
          var j, len, otherItem, ref1
          ref1 = this.groupsMap[item.groupId]
          for (j = 0, len = ref1.length; j < len; j++) {
            otherItem = ref1[j]
            if (otherItem !== item) {
              v8Util.setHiddenValue(otherItem, 'checked', false)
            }
          }
          return v8Util.setHiddenValue(item, 'checked', true)
        }
      })
      this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
  }
  if (item.sublabel != null) {
    this.setSublabel(pos, item.sublabel)
  }
  if (item.icon != null) {
    this.setIcon(pos, item.icon)
  }
  if (item.role != null) {
    this.setRole(pos, item.role)
  }

  // Make menu accessable to items.
  item.overrideReadOnlyProperty('menu', this)

  // Remember the items.
  this.items.splice(pos, 0, item)
  this.commandsMap[item.commandId] = item
}

// Force menuWillShow to be called
Menu.prototype._callMenuWillShow = function () {
  if (this.delegate != null) {
    this.delegate.menuWillShow()
  }
  this.items.forEach(function (item) {
    if (item.submenu != null) {
      item.submenu._callMenuWillShow()
    }
  })
}

var applicationMenu = null

Menu.setApplicationMenu = function (menu) {
  if (!(menu === null || menu.constructor === Menu)) {
    throw new TypeError('Invalid menu')
  }

  // Keep a reference.
  applicationMenu = menu
  if (process.platform === 'darwin') {
    if (menu === null) {
      return
    }
    menu._callMenuWillShow()
    bindings.setApplicationMenu(menu)
  } else {
    BrowserWindow.getAllWindows().forEach(function (window) {
      window.setMenu(menu)
    })
  }
}

Menu.getApplicationMenu = function () {
  return applicationMenu
}

Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder

Menu.buildFromTemplate = function (template) {
  var insertIndex, item, j, k, len, len1, menu, menuItem, positionedTemplate
  if (!Array.isArray(template)) {
    throw new TypeError('Invalid template for Menu')
  }
  positionedTemplate = []
  insertIndex = 0
  for (j = 0, len = template.length; j < len; j++) {
    item = template[j]
    if (item.position) {
      insertIndex = indexToInsertByPosition(positionedTemplate, item.position)
    } else {
      // If no |position| is specified, insert after last item.
      insertIndex++
    }
    positionedTemplate.splice(insertIndex, 0, item)
  }
  menu = new Menu()
  for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
    item = positionedTemplate[k]
    if (typeof item !== 'object') {
      throw new TypeError('Invalid template for MenuItem')
    }
    menuItem = new MenuItem(item)
    menu.append(menuItem)
  }
  return menu
}

module.exports = Menu
<|endoftext|>


<commit_before>
let MapConversationAudioSourceController = MapConversationSourceController.createComponent("MapConversationAudioSourceController");

MapConversationAudioSourceController.createViewFragment = function (source) {
  return cloneTemplate("#template-map-conversation-audiosource");
};

MapConversationAudioSourceController.defineMethod("initView", function initView() {
  if (!this.view) return;

  this.quillTranscript = new Quill(this.view.querySelector(".text"), {
    readOnly: true
  });
});

MapConversationAudioSourceController.defineMethod("updateView", function updateView() {
  if (!this.view) return;

  var displayPlayer = function displayPlayer(display) {
    this.querySelector(".plyr").hidden = !display;
  }.bind(this.view);

  displayPlayer(false);
  this.source.getFileStats()
    .then(function (stats) {
      if (this.querySelector("audio").canPlayType(stats.type) != "") {
        displayPlayer(true);
      }
    }.bind(this.view));

  if (this.source.fileHandler.blob) {
    this.view.querySelector("source[data-ica-content='0']").src = this.source.fileHandler.url;

    this.view.querySelector("a[data-ica-content='0']").href = this.source.fileHandler.url;
    this.view.querySelector("a[data-ica-content='0']").textContent = this.source.fileHandler.url;
  }

  this.quillTranscript.setText(this.source.content["1"] || "");
});
<commit_msg>Review and annotate MapConversationAudioSourceController<commit_after>
/**
 * MapConversationAudioSourceController
 * Concrete view controller to display an audio source.
 * @constructor
 */
let MapConversationAudioSourceController = MapConversationSourceController.createComponent("MapConversationAudioSourceController");

MapConversationAudioSourceController.createViewFragment = function () {
  return cloneTemplate("#template-map-conversation-audiosource");
};

// View

MapConversationAudioSourceController.defineMethod("initView", function initView() {
  if (!this.view) return;

  // Transcript
  this.quillTranscript = new Quill(this.view.querySelector(".text"), {
    readOnly: true
  });

});

MapConversationAudioSourceController.defineMethod("updateView", function updateView() {
  if (!this.view) return;

  // Audio player

  let displayPlayer = function displayPlayer(display) {
    this.querySelector(".plyr").hidden = !display;
  }.bind(this.view);

  displayPlayer(false);
  this.source.getFileStats()
    .then(function (stats) {
      if (this.querySelector("audio").canPlayType(stats.type) !== "") {
        displayPlayer(true);
      }
    }.bind(this.view));

  let url = this.source.fileHandler.url;
  if (url) {
    this.view.querySelector("source[data-ica-content='0']").src = url;

    this.view.querySelector("a[data-ica-content='0']").href = url;
    this.view.querySelector("a[data-ica-content='0']").textContent = url;
  }

  // Transcript

  this.quillTranscript.setText(this.source.content["1"] || "");

});

MapConversationAudioSourceController.defineMethod("uninitView", function uninitView() {
  if (!this.view) return;

  // Transcript
  delete this.quillTranscript;

});
<|endoftext|>