<commit_before>define(['jquery', 
    'jquery-ui',
    'underscore',
    'backbone',
    'bootstrap',
    'arches', 
    'views/map',
    'openlayers', 
    'knockout',
    'map/resource-layer-model',
    'utils',
    'resource-types',], 
    function($, jqui, _, Backbone, bootstrap, arches, MapView, ol, ko, ResourceLayerModel, utils, resourceTypes) {
        var geoJSON = new ol.format.GeoJSON();
        return Backbone.View.extend({

            events: {
                'click .layer-zoom': 'layerZoom',
                'click .cluster-item-link': 'clusterItemClick'
            },

            initialize: function(options) { 
                var self = this;

                //  Handle show/hide toggle ourselves
                $('#map-tools-btn').on('click', function(evt) {
                    if($(evt.currentTarget).hasClass('open')){
                        self.disableDrawingTools();
                    }
                    else {
                        self.enableDrawingTools();
                    }
                    $(evt.currentTarget).toggleClass('open');
                    return false;
                });
                $('#map-tools-dropdown').on('click', 'a', function(evt) {
                    if($(evt.target).attr('name') === 'map-tools'){
                        self.togglefilter(evt);
                    }
                    return false;
                });

                //  suppress default bahavior of the bootstrap menu to auto close
                $('#map-tools-btn').on('hide.bs.dropdown', false);            
                $('#map-tools-btn').on('show.bs.dropdown', false);


                this.expanded = ko.observable(false);
                this.expanded.subscribe(function(status){
                    this.toggleFilterSection($('#map-filter'), status);
                }, this);

                this.query = {
                    filter: {
                        geometry:{
                            type: ko.observable(''),
                            coordinates: ko.observable([])
                        },
                        buffer: {
                            width: ko.observable('0'),
                            unit: ko.observable('ft')
                        },
                        inverted: ko.observable(false)
                    },
                    changed: ko.pureComputed(function(){
                        return (ko.toJSON(this.query.filter.geometry.coordinates()) + 
                            ko.toJSON(this.query.filter.buffer.width()) + 
                            ko.toJSON(this.query.filter.inverted()));
                    }, this).extend({ rateLimit: 200 })
                }

                ko.applyBindings(this.query.filter, $('#map-tools-dropdown')[0]); 

                this.query.filter.buffer.width.subscribe(function(){
                    self.applyBuffer();
                });

                this.query.filter.geometry.type.subscribe(function(type){
                    var enabled = type !== '';
                    this.trigger('enabled', enabled, this.query.filter.inverted());
                    return enabled;
                }, this);


                this.vectorLayer = new ResourceLayerModel({}, function(features){
                    self.vectorLayerLoaded = true;
                    if (self.highlightOnLoad) {
                        self.highlightOnLoad = false;
                        _.defer(function () { self.highlightFeatures(self.highlightOnLoad.resultsarray, self.highlightOnLoad.entityIdArray) });
                    }
                    self.trigger('vectorlayerloaded', features);
                    if (!self.cancelFitBaseLayer){
                        setTimeout(function() {
                              self.zoomToExtent(self.vectorLayer.getSource().getExtent());
                        }, 500);                        
                    }
                }).layer();
                this.map = new MapView({
                    el: $('#map'),
                    overlays: [this.vectorLayer]
                });


                this.bufferFeatureOverlay = new ol.FeatureOverlay({
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: 'rgba(123, 123, 255, 0.5)'
                        }),
                        stroke: new ol.style.Stroke({
                            color: '#ff6633',
                            width: 2,
                            lineDash: [4,4]
                        })
                    })
                }); 
                this.bufferFeatureOverlay.setMap(this.map.map);                   
                
                this.drawingFeatureOverlay = new ol.FeatureOverlay({
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: 'rgba(255, 255, 255, 0.2)'
                        }),
                        stroke: new ol.style.Stroke({
                            color: '#ffcc33',
                            width: 2
                        })
                    })
                });
                this.drawingFeatureOverlay.setMap(this.map.map);
                
                ko.applyBindings(this.map, $('#basemaps-panel')[0]);

                var hideAllPanels = function(){
                    $("#basemaps-panel").addClass("hidden");

                    //Update state of remaining buttons
                    $("#inventory-basemaps")
                        .removeClass("arches-map-tools-pressed")
                        .addClass("arches-map-tools")
                        .css("border-bottom-left-radius", "1px");
                };

                //Inventory-basemaps button opens basemap panel
                $("#inventory-basemaps").click(function (){
                    if ($(this).hasClass('arches-map-tools-pressed')) {
                        hideAllPanels();
                    } else {
                        $("#basemaps-panel").removeClass("hidden");

                        //Update state of current button and adjust position
                        $("#inventory-basemaps")
                            .addClass("arches-map-tools-pressed")
                            .removeClass("arches-map-tools")
                            .css("border-bottom-left-radius", "5px");
                    }
                });

                $(".basemap").click(function (){ 
                    var basemap = $(this).attr('id');
                    _.each(self.map.baseLayers, function(baseLayer){ 
                        baseLayer.layer.setVisible(baseLayer.id == basemap);
                    });
                    hideAllPanels();
                });

                //Close Button
                $(".close").click(function (){ 
                    hideAllPanels();
                });

                
                var mouseoverFeatureTooltip = $('#feature_tooltip');
                var currentMousePx = null;
                var archesFeaturesCache = {};

                var showMouseoverFeatureTooltip = function(feature) {
                    var mapheight = self.map.$el.height();
                    var mapwidth = self.map.$el.width();
                    if (currentMousePx) {
                        mouseoverFeatureTooltip.find('#tooltip-text').html(feature.get('primaryname'));
                        if(currentMousePx[0] < mapwidth*0.33){
                            mouseoverFeatureTooltip.removeClass('left')
                                .addClass('right');
                        }
                        if(currentMousePx[0] > mapwidth*0.66){
                            mouseoverFeatureTooltip.removeClass('right')
                                .addClass('left');
                        }
                        if(mouseoverFeatureTooltip.hasClass('left')){
                            mouseoverFeatureTooltip.css({
                                left: (currentMousePx[0] - mouseoverFeatureTooltip.width()+20) + 'px',
                                top: (currentMousePx[1] - mouseoverFeatureTooltip.height()/2) + 'px'
                            });
                        }
                        if(mouseoverFeatureTooltip.hasClass('right')){
                            mouseoverFeatureTooltip.css({
                                left: (currentMousePx[0] + 45) + 'px',
                                top: (currentMousePx[1] - mouseoverFeatureTooltip.height()/2) + 'px'
                            });
                        }
                        mouseoverFeatureTooltip.show();
                    }
                };

                self.map.on('mousePositionChanged', function (mousePosition, pixels, feature) {
                    var cursorStyle = "";
                    currentMousePx = pixels;

                    if (feature && (feature.get('arches_marker') || feature.get('arches_cluster'))) {
                        cursorStyle = "pointer";
                        if (feature.get('arches_marker') || feature.get('features').length === 1) {
                            if (feature.get('features')) {
                                feature = feature.get('features')[0];
                            }
                            var fullFeature = archesFeaturesCache[feature.getId()];
                            if (fullFeature && fullFeature != 'loading') {
                                showMouseoverFeatureTooltip(fullFeature);
                            } else if (fullFeature != 'loading') {
                                archesFeaturesCache[feature.getId()] = 'loading';
                                $.ajax({
                                    url: arches.urls.map_markers + 'all?entityid=' + feature.getId(),
                                    success: function(response) {
                                        fullFeature = geoJSON.readFeature(response.features[0]);
                                        var geom = fullFeature.getGeometry();
                                        geom.transform(ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));

                                        fullFeature.set('select_feature', true);
                                        fullFeature.set('entityid', fullFeature.getId());

                                        archesFeaturesCache[feature.getId()] = fullFeature;
                                        showMouseoverFeatureTooltip(fullFeature);
                                    }
                                });
                            }
                        }
                    } else {
                        mouseoverFeatureTooltip.hide();
                    }
                    self.map.$el.css("cursor", cursorStyle);
                });

                var clusterFeaturesCache = {};

                var selectFeatureOverlay = new ol.FeatureOverlay({
                    style: function(feature, resolution) {
                        var isSelectFeature = _.contains(feature.getKeys(), 'select_feature');
                        var fillOpacity = isSelectFeature ? 0.3 : 0;
                        var strokeOpacity = isSelectFeature ? 0.9 : 0;
                        return [new ol.style.Style({
                            fill: new ol.style.Fill({
                                color: 'rgba(0, 255, 255, ' + fillOpacity + ')'
                            }),
                            stroke: new ol.style.Stroke({
                                color: 'rgba(0, 255, 255, ' + strokeOpacity + ')',
                                width: 3
                            }),
                            image: new ol.style.Circle({
                                radius: 10,
                                fill: new ol.style.Fill({
                                    color: 'rgba(0, 255, 255, ' + fillOpacity + ')'
                                }),
                                stroke: new ol.style.Stroke({
                                    color: 'rgba(0, 255, 255, ' + strokeOpacity + ')',
                                    width: 3
                                })
                            })
                        })];
                    }
                });
                selectFeatureOverlay.setMap(self.map.map);
                
                $('.resource-info-closer').click(function() {
                    $('#resource-info').hide();
                    selectFeatureOverlay.getFeatures().clear();
                    $('.resource-info-closer')[0].blur();
                });

                $('.cluster-info-closer').click(function() {
                    $('#cluster-info').hide();
                    $('.cluster-info-closer')[0].blur();
                });

                this.viewModel = {
                    selectedResource: ko.observable({}),
                    clusterFeatures: ko.observableArray()
                };
                ko.applyBindings(this.viewModel, $('#resource-info')[0]);
                ko.applyBindings(this.viewModel, $('#cluster-info')[0]);

                var showFeaturePopup = function(feature) {
                    var resourceData = {
                        id: feature.getId(),
                        reportLink: arches.urls.reports + feature.getId()
                    };
                    var typeInfo = resourceTypes[feature.get('entitytypeid')];
                    $('#cluster-info').hide();
                    if (typeInfo) {
                        resourceData.typeName = resourceTypes[feature.get('entitytypeid')].name;
                        resourceData.typeIcon = resourceTypes[feature.get('entitytypeid')].icon;
                    }
                    _.each(feature.getKeys(), function (key) {
                        resourceData[key] = feature.get(key);
                    });
                    
                    selectFeatureOverlay.getFeatures().clear();
                    selectFeatureOverlay.getFeatures().push(feature);
                    self.viewModel.selectedResource(resourceData);
                    $('#resource-info').show();
                };

                this.showFeaturePopup = showFeaturePopup;

                var showClusterPopup = function(feature) {
                    var ids = [];
                    _.each(feature.get('features'), function(childFeature) {
                        ids.push(childFeature.getId());
                    });
                    var featureIds = ids.join(',');
                    var completeFeatures = clusterFeaturesCache[featureIds];

                    self.viewModel.clusterFeatures.removeAll();
                    $('#resource-info').hide();
                    $('#cluster-info').show();

                    if (completeFeatures) {
                        self.viewModel.clusterFeatures.push.apply(self.viewModel.clusterFeatures, completeFeatures);
                    } else {
                        $.ajax({
                            url: arches.urls.map_markers + 'all?entityid=' + featureIds,
                            success: function(response) {
                                clusterFeaturesCache[featureIds] = response.features;
                                self.viewModel.clusterFeatures.push.apply(self.viewModel.clusterFeatures, response.features);
                            }
                        });
                    }
                };

                var currentZoom;
                this.map.on('viewChanged', function (zoom, extent) {
                    currentZoom = zoom;
                });

                this.map.on('mapClicked', function(e, clickFeature) {
                    selectFeatureOverlay.getFeatures().clear();
                    $('#resource-info').hide();
                    if (clickFeature) {
                        var keys = clickFeature.getKeys();
                        var isCluster = _.contains(keys, "features");
                        var isArchesFeature = (_.contains(keys, 'arches_cluster') || _.contains(keys, 'arches_marker'));
                        if (isCluster && clickFeature.get('features').length > 1) {
                            if (currentZoom !== arches.mapDefaults.maxZoom) {
                                var extent = clickFeature.getGeometry().getExtent();
                                _.each(clickFeature.get("features"), function (feature) {
                                    if (_.contains(keys, 'extent')) {
                                        featureExtent = ol.extent.applyTransform(feature.get('extent'), ol.proj.getTransform('EPSG:4326', 'EPSG:3857'));
                                    } else {
                                        featureExtent = feature.getGeometry().getExtent();
                                    }
                                    extent = ol.extent.extend(extent, featureExtent);
                                });
                                self.map.map.getView().fitExtent(extent, (self.map.map.getSize()));
                            } else {
                                showClusterPopup(clickFeature);
                            }
                        } else {
                            if (isCluster) {
                                clickFeature = clickFeature.get('features')[0];
                                keys = clickFeature.getKeys();
                            }
                            if (!_.contains(keys, 'select_feature')) {
                                if (isArchesFeature) {
                                    if (archesFeaturesCache[clickFeature.getId()] && archesFeaturesCache[clickFeature.getId()] !== 'loading'){
                                        showFeaturePopup(archesFeaturesCache[clickFeature.getId()]);
                                    } else {
                                        $('.map-loading').show();
                                        archesFeaturesCache[clickFeature.getId()] = 'loading';
                                        $.ajax({
                                            url: arches.urls.map_markers + 'all?entityid=' + clickFeature.getId(),
                                            success: function(response) {
                                                var feature = geoJSON.readFeature(response.features[0]);
                                                var geom = feature.getGeometry();
                                                geom.transform(ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));

                                                feature.set('select_feature', true);
                                                feature.set('entityid', feature.getId());

                                                archesFeaturesCache[clickFeature.getId()] = feature;
                                                $('.map-loading').hide();
                                                showFeaturePopup(feature);
                                            }
                                        });
                                    }
                                }
                            }
                        }
                    }
                });

                var iconUnicode = '\uf060';
                this.resultLayer = new ResourceLayerModel({entitytypeid: null, vectorColor: '#C4171D'}).layer();
                this.map.map.addLayer(this.resultLayer);
                var styleFactory = function (color, zIndex) {
                    var rgb = utils.hexToRgb(color);
                    return [new ol.style.Style({
                        text: new ol.style.Text({
                            text: iconUnicode,
                            font: 'normal 42px octicons',
                            offsetX: 5,
                            offsetY: ((42/2)*-1)-5,
                            fill: new ol.style.Fill({
                                color: 'rgba(126,126,126,0.3)',
                            })
                        }),
                        zIndex: zIndex
                    }), new ol.style.Style({
                        text: new ol.style.Text({
                            text: iconUnicode,
                            font: 'normal 42px octicons',
                            offsetY: (42/2)*-1,
                            stroke: new ol.style.Stroke({
                                color: 'white',
                                width: 3
                            }),
                            fill: new ol.style.Fill({
                                color: 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',0.9)',
                            })
                        }),
                        zIndex: zIndex
                    })];
                };
                var normalStyle = styleFactory('#C4171D', 1000000);
                var highlightStyle = styleFactory('#4CAE4C', 9999999);

                this.currentPageLayer = new ol.layer.Vector({
                    source: new ol.source.GeoJSON(),
                    style: function(feature) {
                        if(feature.get('highlight')) {
                            return highlightStyle;
                        } else {
                            return normalStyle;
                        }
                    }
                });
                this.map.map.addLayer(this.currentPageLayer);
            },

            zoomToResource: function(resourceid){
                this.cancelFitBaseLayer = true;
                var feature = this.resultLayer.getSource().getFeatureById(resourceid);
                if(feature.getGeometry().getGeometries().length > 1){
                    var extent = feature.getGeometry().getExtent();
                    var minX = extent[0];
                    var minY = extent[1];
                    var maxX = extent[2];
                    var maxY = extent[3];
                    var polygon = new ol.geom.Polygon([[[minX, minY], [maxX, minY], [maxX, maxY], [minX, maxY], [minX, minY]]]);
                    //polygon.transform('EPSG:3857', 'EPSG:4326');
                    this.map.map.getView().fitGeometry(polygon, this.map.map.getSize(), {maxZoom:16}); 
                    //this.zoomToExtent(feature.getGeometry().getExtent());
                }else{
                    this.map.map.getView().fitGeometry(feature.getGeometry().getGeometries()[0], this.map.map.getSize(), {maxZoom:16});                    
                }
            },
            
            zoomToExtent: function(extent){
                var size = this.map.map.getSize();
                var view = this.map.map.getView()
                view.fitExtent(
                    extent,
                    size
                );
            },

            highlightFeatures: function(resultsarray, entityIdArray){
                var resultFeatures = [];
                var currentPageFeatures = [];
                var self = this;

                if (this.vectorLayerLoaded) {
                    var previousSelections = this.resultLayer.vectorSource.getFeatures().concat(this.currentPageLayer.getSource().getFeatures());
                    this.resultLayer.vectorSource.clear();
                    this.currentPageLayer.getSource().clear();
                    self.vectorLayer.vectorSource.addFeatures(previousSelections);

                    _.each(entityIdArray, function(entityid) {
                        var feature = self.vectorLayer.vectorSource.getFeatureById(entityid);
                        if (feature) {
                            self.vectorLayer.vectorSource.removeFeature(feature);
                            if (!feature.get('arches_marker')) {
                                feature.set('arches_marker', true);
                            }
                            if (_.find(resultsarray.results.hits.hits, function(hit){ return hit['_id'] === feature.getId() })) {
                                currentPageFeatures.push(feature);
                            } else {
                                resultFeatures.push(feature);
                            }
                        }
                    });
                    self.currentPageLayer.getSource().addFeatures(currentPageFeatures);
                    self.resultLayer.vectorSource.addFeatures(resultFeatures);
                } else {
                    this.highlightOnLoad = {
                        resultsarray: resultsarray,
                        entityIdArray: entityIdArray
                    };
                }
            },

            selectFeatureById: function(resourceid){
                this.unselectAllFeatures();
                var feature = this.currentPageLayer.getSource().getFeatureById(resourceid);
                if(feature){
                    feature.set('highlight', true);<commit_msg>reducing number of calls to render map layers by only adding or removing features once per layer for each page load<commit_after>define(['jquery', 
    'jquery-ui',
    'underscore',
    'backbone',
    'bootstrap',
    'arches', 
    'views/map',
    'openlayers', 
    'knockout',
    'map/resource-layer-model',
    'utils',
    'resource-types',], 
    function($, jqui, _, Backbone, bootstrap, arches, MapView, ol, ko, ResourceLayerModel, utils, resourceTypes) {
        var geoJSON = new ol.format.GeoJSON();
        return Backbone.View.extend({

            events: {
                'click .layer-zoom': 'layerZoom',
                'click .cluster-item-link': 'clusterItemClick'
            },

            initialize: function(options) { 
                var self = this;

                //  Handle show/hide toggle ourselves
                $('#map-tools-btn').on('click', function(evt) {
                    if($(evt.currentTarget).hasClass('open')){
                        self.disableDrawingTools();
                    }
                    else {
                        self.enableDrawingTools();
                    }
                    $(evt.currentTarget).toggleClass('open');
                    return false;
                });
                $('#map-tools-dropdown').on('click', 'a', function(evt) {
                    if($(evt.target).attr('name') === 'map-tools'){
                        self.togglefilter(evt);
                    }
                    return false;
                });

                //  suppress default bahavior of the bootstrap menu to auto close
                $('#map-tools-btn').on('hide.bs.dropdown', false);            
                $('#map-tools-btn').on('show.bs.dropdown', false);


                this.expanded = ko.observable(false);
                this.expanded.subscribe(function(status){
                    this.toggleFilterSection($('#map-filter'), status);
                }, this);

                this.query = {
                    filter: {
                        geometry:{
                            type: ko.observable(''),
                            coordinates: ko.observable([])
                        },
                        buffer: {
                            width: ko.observable('0'),
                            unit: ko.observable('ft')
                        },
                        inverted: ko.observable(false)
                    },
                    changed: ko.pureComputed(function(){
                        return (ko.toJSON(this.query.filter.geometry.coordinates()) + 
                            ko.toJSON(this.query.filter.buffer.width()) + 
                            ko.toJSON(this.query.filter.inverted()));
                    }, this).extend({ rateLimit: 200 })
                }

                ko.applyBindings(this.query.filter, $('#map-tools-dropdown')[0]); 

                this.query.filter.buffer.width.subscribe(function(){
                    self.applyBuffer();
                });

                this.query.filter.geometry.type.subscribe(function(type){
                    var enabled = type !== '';
                    this.trigger('enabled', enabled, this.query.filter.inverted());
                    return enabled;
                }, this);


                this.vectorLayer = new ResourceLayerModel({}, function(features){
                    self.resourceFeatures = features;
                    if (self.highlightOnLoad) {
                        self.highlightOnLoad = false;
                        _.defer(function () { self.highlightFeatures(self.highlightOnLoad.resultsarray, self.highlightOnLoad.entityIdArray) });
                    }
                    self.trigger('vectorlayerloaded', features);
                    if (!self.cancelFitBaseLayer){
                        setTimeout(function() {
                              self.zoomToExtent(self.vectorLayer.getSource().getExtent());
                        }, 500);                        
                    }
                }).layer();
                this.map = new MapView({
                    el: $('#map'),
                    overlays: [this.vectorLayer]
                });


                this.bufferFeatureOverlay = new ol.FeatureOverlay({
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: 'rgba(123, 123, 255, 0.5)'
                        }),
                        stroke: new ol.style.Stroke({
                            color: '#ff6633',
                            width: 2,
                            lineDash: [4,4]
                        })
                    })
                }); 
                this.bufferFeatureOverlay.setMap(this.map.map);                   
                
                this.drawingFeatureOverlay = new ol.FeatureOverlay({
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: 'rgba(255, 255, 255, 0.2)'
                        }),
                        stroke: new ol.style.Stroke({
                            color: '#ffcc33',
                            width: 2
                        })
                    })
                });
                this.drawingFeatureOverlay.setMap(this.map.map);
                
                ko.applyBindings(this.map, $('#basemaps-panel')[0]);

                var hideAllPanels = function(){
                    $("#basemaps-panel").addClass("hidden");

                    //Update state of remaining buttons
                    $("#inventory-basemaps")
                        .removeClass("arches-map-tools-pressed")
                        .addClass("arches-map-tools")
                        .css("border-bottom-left-radius", "1px");
                };

                //Inventory-basemaps button opens basemap panel
                $("#inventory-basemaps").click(function (){
                    if ($(this).hasClass('arches-map-tools-pressed')) {
                        hideAllPanels();
                    } else {
                        $("#basemaps-panel").removeClass("hidden");

                        //Update state of current button and adjust position
                        $("#inventory-basemaps")
                            .addClass("arches-map-tools-pressed")
                            .removeClass("arches-map-tools")
                            .css("border-bottom-left-radius", "5px");
                    }
                });

                $(".basemap").click(function (){ 
                    var basemap = $(this).attr('id');
                    _.each(self.map.baseLayers, function(baseLayer){ 
                        baseLayer.layer.setVisible(baseLayer.id == basemap);
                    });
                    hideAllPanels();
                });

                //Close Button
                $(".close").click(function (){ 
                    hideAllPanels();
                });

                
                var mouseoverFeatureTooltip = $('#feature_tooltip');
                var currentMousePx = null;
                var archesFeaturesCache = {};

                var showMouseoverFeatureTooltip = function(feature) {
                    var mapheight = self.map.$el.height();
                    var mapwidth = self.map.$el.width();
                    if (currentMousePx) {
                        mouseoverFeatureTooltip.find('#tooltip-text').html(feature.get('primaryname'));
                        if(currentMousePx[0] < mapwidth*0.33){
                            mouseoverFeatureTooltip.removeClass('left')
                                .addClass('right');
                        }
                        if(currentMousePx[0] > mapwidth*0.66){
                            mouseoverFeatureTooltip.removeClass('right')
                                .addClass('left');
                        }
                        if(mouseoverFeatureTooltip.hasClass('left')){
                            mouseoverFeatureTooltip.css({
                                left: (currentMousePx[0] - mouseoverFeatureTooltip.width()+20) + 'px',
                                top: (currentMousePx[1] - mouseoverFeatureTooltip.height()/2) + 'px'
                            });
                        }
                        if(mouseoverFeatureTooltip.hasClass('right')){
                            mouseoverFeatureTooltip.css({
                                left: (currentMousePx[0] + 45) + 'px',
                                top: (currentMousePx[1] - mouseoverFeatureTooltip.height()/2) + 'px'
                            });
                        }
                        mouseoverFeatureTooltip.show();
                    }
                };

                self.map.on('mousePositionChanged', function (mousePosition, pixels, feature) {
                    var cursorStyle = "";
                    currentMousePx = pixels;

                    if (feature && (feature.get('arches_marker') || feature.get('arches_cluster'))) {
                        cursorStyle = "pointer";
                        if (feature.get('arches_marker') || feature.get('features').length === 1) {
                            if (feature.get('features')) {
                                feature = feature.get('features')[0];
                            }
                            var fullFeature = archesFeaturesCache[feature.getId()];
                            if (fullFeature && fullFeature != 'loading') {
                                showMouseoverFeatureTooltip(fullFeature);
                            } else if (fullFeature != 'loading') {
                                archesFeaturesCache[feature.getId()] = 'loading';
                                $.ajax({
                                    url: arches.urls.map_markers + 'all?entityid=' + feature.getId(),
                                    success: function(response) {
                                        fullFeature = geoJSON.readFeature(response.features[0]);
                                        var geom = fullFeature.getGeometry();
                                        geom.transform(ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));

                                        fullFeature.set('select_feature', true);
                                        fullFeature.set('entityid', fullFeature.getId());

                                        archesFeaturesCache[feature.getId()] = fullFeature;
                                        showMouseoverFeatureTooltip(fullFeature);
                                    }
                                });
                            }
                        }
                    } else {
                        mouseoverFeatureTooltip.hide();
                    }
                    self.map.$el.css("cursor", cursorStyle);
                });

                var clusterFeaturesCache = {};

                var selectFeatureOverlay = new ol.FeatureOverlay({
                    style: function(feature, resolution) {
                        var isSelectFeature = _.contains(feature.getKeys(), 'select_feature');
                        var fillOpacity = isSelectFeature ? 0.3 : 0;
                        var strokeOpacity = isSelectFeature ? 0.9 : 0;
                        return [new ol.style.Style({
                            fill: new ol.style.Fill({
                                color: 'rgba(0, 255, 255, ' + fillOpacity + ')'
                            }),
                            stroke: new ol.style.Stroke({
                                color: 'rgba(0, 255, 255, ' + strokeOpacity + ')',
                                width: 3
                            }),
                            image: new ol.style.Circle({
                                radius: 10,
                                fill: new ol.style.Fill({
                                    color: 'rgba(0, 255, 255, ' + fillOpacity + ')'
                                }),
                                stroke: new ol.style.Stroke({
                                    color: 'rgba(0, 255, 255, ' + strokeOpacity + ')',
                                    width: 3
                                })
                            })
                        })];
                    }
                });
                selectFeatureOverlay.setMap(self.map.map);
                
                $('.resource-info-closer').click(function() {
                    $('#resource-info').hide();
                    selectFeatureOverlay.getFeatures().clear();
                    $('.resource-info-closer')[0].blur();
                });

                $('.cluster-info-closer').click(function() {
                    $('#cluster-info').hide();
                    $('.cluster-info-closer')[0].blur();
                });

                this.viewModel = {
                    selectedResource: ko.observable({}),
                    clusterFeatures: ko.observableArray()
                };
                ko.applyBindings(this.viewModel, $('#resource-info')[0]);
                ko.applyBindings(this.viewModel, $('#cluster-info')[0]);

                var showFeaturePopup = function(feature) {
                    var resourceData = {
                        id: feature.getId(),
                        reportLink: arches.urls.reports + feature.getId()
                    };
                    var typeInfo = resourceTypes[feature.get('entitytypeid')];
                    $('#cluster-info').hide();
                    if (typeInfo) {
                        resourceData.typeName = resourceTypes[feature.get('entitytypeid')].name;
                        resourceData.typeIcon = resourceTypes[feature.get('entitytypeid')].icon;
                    }
                    _.each(feature.getKeys(), function (key) {
                        resourceData[key] = feature.get(key);
                    });
                    
                    selectFeatureOverlay.getFeatures().clear();
                    selectFeatureOverlay.getFeatures().push(feature);
                    self.viewModel.selectedResource(resourceData);
                    $('#resource-info').show();
                };

                this.showFeaturePopup = showFeaturePopup;

                var showClusterPopup = function(feature) {
                    var ids = [];
                    _.each(feature.get('features'), function(childFeature) {
                        ids.push(childFeature.getId());
                    });
                    var featureIds = ids.join(',');
                    var completeFeatures = clusterFeaturesCache[featureIds];

                    self.viewModel.clusterFeatures.removeAll();
                    $('#resource-info').hide();
                    $('#cluster-info').show();

                    if (completeFeatures) {
                        self.viewModel.clusterFeatures.push.apply(self.viewModel.clusterFeatures, completeFeatures);
                    } else {
                        $.ajax({
                            url: arches.urls.map_markers + 'all?entityid=' + featureIds,
                            success: function(response) {
                                clusterFeaturesCache[featureIds] = response.features;
                                self.viewModel.clusterFeatures.push.apply(self.viewModel.clusterFeatures, response.features);
                            }
                        });
                    }
                };

                var currentZoom;
                this.map.on('viewChanged', function (zoom, extent) {
                    currentZoom = zoom;
                });

                this.map.on('mapClicked', function(e, clickFeature) {
                    selectFeatureOverlay.getFeatures().clear();
                    $('#resource-info').hide();
                    if (clickFeature) {
                        var keys = clickFeature.getKeys();
                        var isCluster = _.contains(keys, "features");
                        var isArchesFeature = (_.contains(keys, 'arches_cluster') || _.contains(keys, 'arches_marker'));
                        if (isCluster && clickFeature.get('features').length > 1) {
                            if (currentZoom !== arches.mapDefaults.maxZoom) {
                                var extent = clickFeature.getGeometry().getExtent();
                                _.each(clickFeature.get("features"), function (feature) {
                                    if (_.contains(keys, 'extent')) {
                                        featureExtent = ol.extent.applyTransform(feature.get('extent'), ol.proj.getTransform('EPSG:4326', 'EPSG:3857'));
                                    } else {
                                        featureExtent = feature.getGeometry().getExtent();
                                    }
                                    extent = ol.extent.extend(extent, featureExtent);
                                });
                                self.map.map.getView().fitExtent(extent, (self.map.map.getSize()));
                            } else {
                                showClusterPopup(clickFeature);
                            }
                        } else {
                            if (isCluster) {
                                clickFeature = clickFeature.get('features')[0];
                                keys = clickFeature.getKeys();
                            }
                            if (!_.contains(keys, 'select_feature')) {
                                if (isArchesFeature) {
                                    if (archesFeaturesCache[clickFeature.getId()] && archesFeaturesCache[clickFeature.getId()] !== 'loading'){
                                        showFeaturePopup(archesFeaturesCache[clickFeature.getId()]);
                                    } else {
                                        $('.map-loading').show();
                                        archesFeaturesCache[clickFeature.getId()] = 'loading';
                                        $.ajax({
                                            url: arches.urls.map_markers + 'all?entityid=' + clickFeature.getId(),
                                            success: function(response) {
                                                var feature = geoJSON.readFeature(response.features[0]);
                                                var geom = feature.getGeometry();
                                                geom.transform(ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));

                                                feature.set('select_feature', true);
                                                feature.set('entityid', feature.getId());

                                                archesFeaturesCache[clickFeature.getId()] = feature;
                                                $('.map-loading').hide();
                                                showFeaturePopup(feature);
                                            }
                                        });
                                    }
                                }
                            }
                        }
                    }
                });

                var iconUnicode = '\uf060';
                this.resultLayer = new ResourceLayerModel({entitytypeid: null, vectorColor: '#C4171D'}).layer();
                this.map.map.addLayer(this.resultLayer);
                var styleFactory = function (color, zIndex) {
                    var rgb = utils.hexToRgb(color);
                    return [new ol.style.Style({
                        text: new ol.style.Text({
                            text: iconUnicode,
                            font: 'normal 42px octicons',
                            offsetX: 5,
                            offsetY: ((42/2)*-1)-5,
                            fill: new ol.style.Fill({
                                color: 'rgba(126,126,126,0.3)',
                            })
                        }),
                        zIndex: zIndex
                    }), new ol.style.Style({
                        text: new ol.style.Text({
                            text: iconUnicode,
                            font: 'normal 42px octicons',
                            offsetY: (42/2)*-1,
                            stroke: new ol.style.Stroke({
                                color: 'white',
                                width: 3
                            }),
                            fill: new ol.style.Fill({
                                color: 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',0.9)',
                            })
                        }),
                        zIndex: zIndex
                    })];
                };
                var normalStyle = styleFactory('#C4171D', 1000000);
                var highlightStyle = styleFactory('#4CAE4C', 9999999);

                this.currentPageLayer = new ol.layer.Vector({
                    source: new ol.source.GeoJSON(),
                    style: function(feature) {
                        if(feature.get('highlight')) {
                            return highlightStyle;
                        } else {
                            return normalStyle;
                        }
                    }
                });
                this.map.map.addLayer(this.currentPageLayer);
            },

            zoomToResource: function(resourceid){
                this.cancelFitBaseLayer = true;
                var feature = this.resultLayer.getSource().getFeatureById(resourceid);
                if(feature.getGeometry().getGeometries().length > 1){
                    var extent = feature.getGeometry().getExtent();
                    var minX = extent[0];
                    var minY = extent[1];
                    var maxX = extent[2];
                    var maxY = extent[3];
                    var polygon = new ol.geom.Polygon([[[minX, minY], [maxX, minY], [maxX, maxY], [minX, maxY], [minX, minY]]]);
                    //polygon.transform('EPSG:3857', 'EPSG:4326');
                    this.map.map.getView().fitGeometry(polygon, this.map.map.getSize(), {maxZoom:16}); 
                    //this.zoomToExtent(feature.getGeometry().getExtent());
                }else{
                    this.map.map.getView().fitGeometry(feature.getGeometry().getGeometries()[0], this.map.map.getSize(), {maxZoom:16});                    
                }
            },
            
            zoomToExtent: function(extent){
                var size = this.map.map.getSize();
                var view = this.map.map.getView()
                view.fitExtent(
                    extent,
                    size
                );
            },

            highlightFeatures: function(resultsarray, entityIdArray){
                var resultFeatures = [];
                var currentPageFeatures = [];
                var nonResultFeatures = [];
                var self = this;

                if (this.resourceFeatures) {
                    _.each(this.resourceFeatures, function (feature) {
                        if (_.find(resultsarray.results.hits.hits, function(hit){ return hit['_id'] === feature.getId() })) {
                            if (!feature.get('arches_marker')) {
                                feature.set('arches_marker', true);
                            }
                            currentPageFeatures.push(feature);
                        } else if (_.contains(entityIdArray,feature.getId())) {
                            resultFeatures.push(feature);
                        } else {
                            nonResultFeatures.push(feature);
                        }
                    });

                    self.vectorLayer.vectorSource.clear();
                    this.resultLayer.vectorSource.clear();
                    this.currentPageLayer.getSource().clear();
                    self.currentPageLayer.getSource().addFeatures(currentPageFeatures);
                    self.resultLayer.vectorSource.addFeatures(resultFeatures);
                    self.vectorLayer.vectorSource.addFeatures(nonResultFeatures);
                } else {
                    this.highlightOnLoad = {
                        resultsarray: resultsarray,
                        entityIdArray: entityIdArray
                    };
                }
            },

            selectFeatureById: function(resourceid){
                this.unselectAllFeatures();
                var feature = this.currentPageLayer.getSource().getFeatureById(resourceid);
                if(feature){
                    feature.set('highlight', true);
{'fraction_non_alphanumeric': 0.11039954889640728, 'fraction_numerical': 0.008297083937489931, 'mean_word_length': 0.98628, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 106, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3740091', 'n_tokens_mistral': 11470, 'n_tokens_neox': 10510, 'n_words': 1650}