<commit_before>/*
 * Copyright (C) 2012 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * @class
 * @constructor
 * @description
 * The openlayers map object wrapper
 */
Ext.define ("viewer.viewercontroller.openlayers.OpenLayersMap",{
    extend: "viewer.viewercontroller.controller.Map",
    layersLoading : null,
    utils:null,
    /**
     * @constructor
     * @see viewer.viewercontroller.controller.Map#constructor
     */
    constructor: function(config){
        viewer.viewercontroller.openlayers.OpenLayersMap.superclass.constructor.call(this, config);
        this.initConfig(config);
        this.utils = Ext.create("viewer.viewercontroller.openlayers.Utils");
        var maxBounds=null;
        if (config.options.maxExtent){
            maxBounds = this.utils.createBounds(config.options.maxExtent);
        }
        var startBounds;
        if (config.options.startExtent){
            startBounds= this.utils.createBounds(config.options.startExtent);
        }
        //set the Center point
        if (startBounds){
            config.center = startBounds.getCenterLonLat();
        }else if (maxBounds){
            config.center = maxBounds.getCenterLonLat();
        }else{
            this.viewerController.logger.error("No bounds found, can't center viewport");
        }

        config.restrictedExtent = maxBounds;

        //create a click control that handles only single click
        var me=this;
        var singleClickControl = new OpenLayers.Control.Click({
            handlerOptions: {
                single: true,
                "double": false
            },
            autoActivate: true,
            click: function(event){
                var opx = this.map.getLonLatFromPixel(event.xy)
                var options = {
                    x: event.xy.x,
                    y: event.xy.y,
                    coord: {
                        x: opx.lon,
                        y: opx.lat
                    }
                };
                me.fire(viewer.viewercontroller.controller.Event.ON_GET_FEATURE_INFO,options);
            }
        });
        //Overwrite default OpenLayers tools,don't set any mouse controls
        config.controls=[
            new OpenLayers.Control.Attribution(),
            singleClickControl,
            new OpenLayers.Control.Navigation()
        ];
        this.frameworkMap=new OpenLayers.Map(config.domId,config);
        this.frameworkMap.centerLayerContainer();
        /* Zoom to the start extent when the first layer is added
         * because openlayers needs the baselayer to zoom. After zooming, remove the listener.
         */
        if(config.options.startExtent){
            var me = this;
            var handler = function(){
                me.zoomToExtent(config.options.startExtent);
                me.removeListener(viewer.viewercontroller.controller.Event.ON_LAYER_ADDED,handler,handler);
            };
            this.addListener(viewer.viewercontroller.controller.Event.ON_LAYER_ADDED,handler,handler);
        }
        this.layersLoading = 0;
        this.markerLayer=null;
        this.defaultIcon=null;
        this.markers=new Object();
        this.getFeatureInfoControl = null;
        this.addListener(viewer.viewercontroller.controller.Event.ON_LAYER_REMOVED,this.layerRemoved, this);
        return this;
    },

    /**
    *See @link Map.getAllWMSLayers
    */
    getAllWMSLayers : function(){
        var lagen = new Array();
        for(var i = 0 ; i < this.layers.length;i++){
            if(this.layers[i] instanceof OpenLayersWMSLayer){
                lagen.push(this.layers[i]);
            }
        }
        return lagen;
    },

    /**
    *See @link Map.getAllVectorLayers
    */
    getAllVectorLayers : function(){
        var lagen = new Array();
        for(var i = 0 ; i < this.layers.length;i++){
            if(this.layers[i] instanceof OpenLayersVectorLayer){
                lagen.push(this.layers[i]);
            }
        }
        return lagen;
    },

    /**
    *See @link Map.remove
    */
    remove : function(){
        this.getFrameworkMap().destroy();
    },

    /**
    *Add a layer. Also see @link Map.addLayer
    **/
    addLayer : function(layer){
        this.superclass.addLayer.call(this,layer);
        //delete layer.getFrameworkLayer().id;
        var map = this.getFrameworkMap()
        var l = layer.getFrameworkLayer();
        map.addLayer(l);
    },

    /**
    *remove the specific layer. See @link Map.removeLayer
    **/
    removeLayer : function(layer){
        //remove layer from framework
        this.getFrameworkMap().removeLayer(layer.getFrameworkLayer());
        /**
         *Dont call super because we listen to a remove of the layer with a listener
         *at the framework:
         *viewer.viewercontroller.openlayers.OpenLayersMap.superclass.removeLayer.call(this,layer);
         */
    },

    layerRemoved : function (map, options){
        var l = options.layer.getFrameworkLayer();
        for ( var i = 0 ; i < this.layers.length ;i++){
            var frameworkLayer = this.layers[i].getFrameworkLayer();
            if(frameworkLayer.id == l.id){
                this.layers.splice(i,1);
                break;
            }
        }
    },

    setLayerVisible : function (layer, visible){
        this.superclass.setLayerVisible.call(this,layer,visible);
        layer.setVisible(visible);
    },

    /**
    * see @link Map.setLayerIndex
    */
    setLayerIndex : function (layer, newIndex){
        if (!(layer instanceof OpenLayersLayer)){
            throw("OpenLayersMap.setLayerIndex(): Given layer not of type OpenLayersLayer.");
        }
        this.getFrameworkMap().setLayerIndex(layer.getFrameworkLayer(),newIndex);
        return Map.prototype.setLayerIndex(layer,newIndex);
    },

    /**
    * Sets the getfeatureinfo control of this map
    */
    setGetFeatureInfoControl : function (control){
        if( control.type != Tool.GET_FEATURE_INFO){
            throw ("Type of given control not of type GET_FEATURE_INFO, but: " + control.type);
        }
        this.getFeatureInfoControl = control;
    },

    /**
    * Move the viewport to the maxExtent. See @link Map.zoomToMaxExtent
    **/
    zoomToMaxExtent : function (){
        this.getFrameworkMap().zoomToExtent(this.getFrameworkMap().getMaxExtent());
    },
    /**
    * See @link Map.zoomToExtent
    **/
    zoomToExtent : function(extent){
        var bounds=this.utils.createBounds(extent)
        this.getFrameworkMap().zoomToExtent(bounds);
    },

    /**
    * See @link Map.zoomToScale
    * @deprecated, use zoomToResolution because it zooms to a resolution and not a scale
    */
    zoomToScale : function(scale){
        this.getFrameworkMap().zoomToResolution(scale);
    },

    /**
    * See @link Map.zoomToResolution
    */
    zoomToResolution : function(resolution){
        this.getFrameworkMap().zoomTo(this.getFrameworkMap().getZoomForResolution(resolution));
    },

    /**
    * See @link viewer.viewercontroller.controller.Map#moveTo
    */
    moveTo: function(x,y){
        this.getFrameworkMap().panTo(new OpenLayers.LonLat(x,y));
    },

    /**
    * See @link Map.setMaxExtent
    * WARNING: Bug in openlayers: doesn't change the maxextent
    * As workaround add the maxExtent when initing the map
    */
    setMaxExtent : function(extent){
        if (this.getFrameworkMap()!=null){
            this.getFrameworkMap().setOptions({
                maxExtent: Utils.createBounds(extent)
            });
        }
    },

    /**
    * See @link Map.getMaxExtent
    */
    getMaxExtent : function(){
        return Utils.createExtent(this.getFrameworkMap().getMaxExtent());
    },

    /**
    * See @link Map.getExtent
    */
    getExtent : function(){
        var extent = this.utils.createExtent(this.getFrameworkMap().getExtent());
        return extent;
    },

    /*TODO:
        doIdentify : function(x,y){}
        update : function(){}
        removeMarker : function(markerName){}
        */
    /**
    *see @link Map.setMarker
    *TODO: marker icon path...
    */
    setMarker : function(markerName,x,y,type){
        if (this.markerLayer==null){
            this.markerLayer = new OpenLayers.Layer.Markers("Markers");
            this.frameworkMap.addLayer(this.markerLayer);
            var size = new OpenLayers.Size(17,17);
            var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
            this.defaultIcon= new OpenLayers.Icon(contextPath+'/viewer-html/common/openlayers/img/marker.png',size,offset);
        }
        /*According the 'type' load a icon: no types yet only default*/
        var icon= this.defaultIcon.clone();
        if (this.markers[markerName]==undefined){
            this.markers[markerName]= new OpenLayers.Marker(new OpenLayers.LonLat(x,y),icon);
            this.markerLayer.addMarker(this.markers[markerName]);
        }else{
            this.markers[markerName].moveTo(this.frameworkMap.getLayerPxFromLonLat(new OpenLayers.LonLat(x,y)));
        }

    },

    /**
    * see @link Map.removeMarker
    */
    removeMarker : function(markerName){
        if (this.markers[markerName] && this.markerLayer!=null){
            this.markerLayer.removeMarker(this.markers[markerName]);
            this.markers[markerName].destroy();
            delete this.markers[markerName];
        }
    },

    /**
     * @see Ext.util.Observable#addListener
     * @param event the event
     * @param handler the handler
     * @param scope the scope
     * Overwrite the addListener. Register event on the OpenLayers Map (only once)
     * If the event is thrown by the OpenLayers event the given handlers are called.
     */
    addListener : function(event,handler,scope){
        var olSpecificEvent = this.viewerController.mapComponent.getSpecificEventName(event);
        if(olSpecificEvent){
            if(!scope){
                scope = this;
            }
            /* Add event to OpenLayersMap only once, to prevent multiple fired events.
             * count the events for removing the listener again.
             */
            if(this.enabledEvents[olSpecificEvent]){
                this.enabledEvents[olSpecificEvent]++;
            }else{
                this.enabledEvents[olSpecificEvent] = 1;
                this.frameworkMap.events.register(olSpecificEvent, this, this.handleEvent);
            }
        }
        viewer.viewercontroller.openlayers.OpenLayersMap.superclass.addListener.call(this,event,handler,scope);
    },

    /**
     * @see Ext.util.Observable#removeListener
     * @param event the event
     * @param handler the handler
     * @param scope the scope
     * Overwrite the removeListener. Unregister the event on the OpenLayers Map if there
     * are no listeners anymore.
     */
    removeListener : function (event,handler,scope){
        var olSpecificEvent = this.viewerController.mapComponent.getSpecificEventName(event);
        if(olSpecificEvent){
            if(!scope){
                scope = this;
            }
            /* Remove event from OpenLayersMap if the number of events == 0
             * If there are no listeners for the OpenLayers event, remove the listener.
             */
            if(this.enabledEvents[olSpecificEvent]){
                this.enabledEvents[olSpecificEvent]--;
                if (this.enabledEvents[olSpecificEvent] <= 0){
                    this.enabledEvents[olSpecificEvent]=0;
                    this.frameworkMap.events.unregister(olSpecificEvent, this, this.handleEvent);
                }
            }
            viewer.viewercontroller.openlayers.OpenLayersMap.superclass.removeListener.call(this,event,handler,scope);
        }else{
            this.viewerController.logger.warning("Event not listed in OpenLayersMapComponent >"+ event + "<. The application  might not work correctly.");
        }
    },

    /**
     * Handles the events fired by OpenLayers.Map and propagates them to the registered objects.
     *
     */
    handleEvent : function(args){
        var event = args.type;
        var options={};
        var genericEvent = this.viewerController.mapComponent.getGenericEventName(event);
        if (genericEvent==viewer.viewercontroller.controller.Event.ON_LAYER_ADDED){
            options.layer=this.getLayerByOpenLayersId(args.layer.id);
            if (options.layer ==undefined){
                //if no layer found return, dont fire
                return;
            }
        }else if (genericEvent== viewer.viewercontroller.controller.Event.ON_LAYER_VISIBILITY_CHANGED){
            options.layer=this.getLayerByOpenLayersId(args.layer.id);
            if (options.layer ==undefined){
                //if no layer found return, dont fire
                return;
            }
            options.visible=args.layer.visibility;
        }else if (genericEvent==viewer.viewercontroller.controller.Event.ON_LAYER_REMOVED){
            options.layer=this.getLayerByOpenLayersId(args.layer.id);
            if (options.layer ==undefined){
                //if no layer found return, dont fire
                return;
            }
        }else if (genericEvent==viewer.viewercontroller.controller.Event.ON_FINISHED_CHANGE_EXTENT ||
                  genericEvent==viewer.viewercontroller.controller.Event.ON_CHANGE_EXTENT){
            options.extent=this.getExtent();
        }else{
            this.viewerController.logger.error("The event "+genericEvent+" is not implemented in the OpenLayersMap.handleEvent()");
        }
        this.fireEvent(genericEvent,this,options);
    },

    /**
    *See @link Map.getScale
    *@deprecated, use getResolution because it returns the resolution and not the scale
    */
    getScale : function(){
        return this.getResolution();
    },

    /**
    *See @link Map.getResolution
    */
    getResolution : function(){
        return this.getFrameworkMap().getResolution();
    },
    /**
     *See @link Map.getResolutions
     */
    getResolutions : function(){
        return this.getFrameworkMap().resolutions;
    },
    /**
    *See @link Map.coordinateToPixel
    *@returns a OpenLayers.pixel object (has a .x and a .y)
    */
    coordinateToPixel : function(x,y){
        return this.getFrameworkMap().getPixelFromLonLat(new OpenLayers.LonLat(x,y));
    },

    pixelToCoordinate : function (x,y){
        var lonLat = this.getFrameworkMap().getLonLatFromPixel(new OpenLayers.Pixel(x,y));
        return {x: lonLat.lon,y: lonLat.lat};
    },

    /**
    *see @link Map.getCenter
    *@return a OpenLayers.LonLat object with .x references to .lon and .y references to .lat
    */
    getCenter : function(){
        var lonlat=this.getFrameworkMap().getCenter();
        lonlat.x=lonlat.lon;
        lonlat.y=lonlat.lat;
        return lonlat;
    },

    getWidth : function(){
        var size = this.frameworkMap.getSize();
        return size.w;
    },

    getHeight : function (){
        var size = this.frameworkMap.getSize();
        return size.h;
    },
    updateSize : function(){
        this.getFrameworkMap().updateSize();
    },

    /**
     * The OpenLayers ID can't be changed. With this function you can get the
     * viewer.viewercontroller.openlayers.OpenLayersLayer with the openlayersid
     * @param olId the openlayers id of the layer
     * @return a viewer.viewercontroller.openlayers.OpenLayersLayer that
     * contains the openlayers layer with the given id.
     */
    getLayerByOpenLayersId: function(olId){
        for (var i=0; i < this.layers.length; i++){
            if (this.layers[i].frameworkLayer){
                if (this.layers[i].frameworkLayer.id == olId){
                    return this.layers[i];
                }
            }
        }
    }
});<commit_msg>Fix #32. Removed click tool in OpenLayersMap component. Default tool is created later, defaultTool is handling click.<commit_after>/*
 * Copyright (C) 2012 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * @class
 * @constructor
 * @description
 * The openlayers map object wrapper
 */
Ext.define ("viewer.viewercontroller.openlayers.OpenLayersMap",{
    extend: "viewer.viewercontroller.controller.Map",
    layersLoading : null,
    utils:null,
    /**
     * @constructor
     * @see viewer.viewercontroller.controller.Map#constructor
     */
    constructor: function(config){
        viewer.viewercontroller.openlayers.OpenLayersMap.superclass.constructor.call(this, config);
        this.initConfig(config);
        this.utils = Ext.create("viewer.viewercontroller.openlayers.Utils");
        var maxBounds=null;
        if (config.options.maxExtent){
            maxBounds = this.utils.createBounds(config.options.maxExtent);
        }
        var startBounds;
        if (config.options.startExtent){
            startBounds= this.utils.createBounds(config.options.startExtent);
        }
        //set the Center point
        if (startBounds){
            config.center = startBounds.getCenterLonLat();
        }else if (maxBounds){
            config.center = maxBounds.getCenterLonLat();
        }else{
            this.viewerController.logger.error("No bounds found, can't center viewport");
        }

        config.restrictedExtent = maxBounds;

        //create a click control that handles only single click
        var me=this;

        //Overwrite default OpenLayers tools,don't set any mouse controls
        config.controls=[
            new OpenLayers.Control.Attribution(),
            new OpenLayers.Control.Navigation()
        ];
        this.frameworkMap=new OpenLayers.Map(config.domId,config);
        this.frameworkMap.centerLayerContainer();
        /* Zoom to the start extent when the first layer is added
         * because openlayers needs the baselayer to zoom. After zooming, remove the listener.
         */
        if(config.options.startExtent){
            var me = this;
            var handler = function(){
                me.zoomToExtent(config.options.startExtent);
                me.removeListener(viewer.viewercontroller.controller.Event.ON_LAYER_ADDED,handler,handler);
            };
            this.addListener(viewer.viewercontroller.controller.Event.ON_LAYER_ADDED,handler,handler);
        }
        this.layersLoading = 0;
        this.markerLayer=null;
        this.defaultIcon=null;
        this.markers=new Object();
        this.getFeatureInfoControl = null;
        this.addListener(viewer.viewercontroller.controller.Event.ON_LAYER_REMOVED,this.layerRemoved, this);
        return this;
    },

    /**
    *See @link Map.getAllWMSLayers
    */
    getAllWMSLayers : function(){
        var lagen = new Array();
        for(var i = 0 ; i < this.layers.length;i++){
            if(this.layers[i] instanceof OpenLayersWMSLayer){
                lagen.push(this.layers[i]);
            }
        }
        return lagen;
    },

    /**
    *See @link Map.getAllVectorLayers
    */
    getAllVectorLayers : function(){
        var lagen = new Array();
        for(var i = 0 ; i < this.layers.length;i++){
            if(this.layers[i] instanceof OpenLayersVectorLayer){
                lagen.push(this.layers[i]);
            }
        }
        return lagen;
    },

    /**
    *See @link Map.remove
    */
    remove : function(){
        this.getFrameworkMap().destroy();
    },

    /**
    *Add a layer. Also see @link Map.addLayer
    **/
    addLayer : function(layer){
        this.superclass.addLayer.call(this,layer);
        //delete layer.getFrameworkLayer().id;
        var map = this.getFrameworkMap()
        var l = layer.getFrameworkLayer();
        map.addLayer(l);
    },

    /**
    *remove the specific layer. See @link Map.removeLayer
    **/
    removeLayer : function(layer){
        //remove layer from framework
        this.getFrameworkMap().removeLayer(layer.getFrameworkLayer());
        /**
         *Dont call super because we listen to a remove of the layer with a listener
         *at the framework:
         *viewer.viewercontroller.openlayers.OpenLayersMap.superclass.removeLayer.call(this,layer);
         */
    },

    layerRemoved : function (map, options){
        var l = options.layer.getFrameworkLayer();
        for ( var i = 0 ; i < this.layers.length ;i++){
            var frameworkLayer = this.layers[i].getFrameworkLayer();
            if(frameworkLayer.id == l.id){
                this.layers.splice(i,1);
                break;
            }
        }
    },

    setLayerVisible : function (layer, visible){
        this.superclass.setLayerVisible.call(this,layer,visible);
        layer.setVisible(visible);
    },

    /**
    * see @link Map.setLayerIndex
    */
    setLayerIndex : function (layer, newIndex){
        if (!(layer instanceof OpenLayersLayer)){
            throw("OpenLayersMap.setLayerIndex(): Given layer not of type OpenLayersLayer.");
        }
        this.getFrameworkMap().setLayerIndex(layer.getFrameworkLayer(),newIndex);
        return Map.prototype.setLayerIndex(layer,newIndex);
    },

    /**
    * Sets the getfeatureinfo control of this map
    */
    setGetFeatureInfoControl : function (control){
        if( control.type != Tool.GET_FEATURE_INFO){
            throw ("Type of given control not of type GET_FEATURE_INFO, but: " + control.type);
        }
        this.getFeatureInfoControl = control;
    },

    /**
    * Move the viewport to the maxExtent. See @link Map.zoomToMaxExtent
    **/
    zoomToMaxExtent : function (){
        this.getFrameworkMap().zoomToExtent(this.getFrameworkMap().getMaxExtent());
    },
    /**
    * See @link Map.zoomToExtent
    **/
    zoomToExtent : function(extent){
        var bounds=this.utils.createBounds(extent)
        this.getFrameworkMap().zoomToExtent(bounds);
    },

    /**
    * See @link Map.zoomToScale
    * @deprecated, use zoomToResolution because it zooms to a resolution and not a scale
    */
    zoomToScale : function(scale){
        this.getFrameworkMap().zoomToResolution(scale);
    },

    /**
    * See @link Map.zoomToResolution
    */
    zoomToResolution : function(resolution){
        this.getFrameworkMap().zoomTo(this.getFrameworkMap().getZoomForResolution(resolution));
    },

    /**
    * See @link viewer.viewercontroller.controller.Map#moveTo
    */
    moveTo: function(x,y){
        this.getFrameworkMap().panTo(new OpenLayers.LonLat(x,y));
    },

    /**
    * See @link Map.setMaxExtent
    * WARNING: Bug in openlayers: doesn't change the maxextent
    * As workaround add the maxExtent when initing the map
    */
    setMaxExtent : function(extent){
        if (this.getFrameworkMap()!=null){
            this.getFrameworkMap().setOptions({
                maxExtent: Utils.createBounds(extent)
            });
        }
    },

    /**
    * See @link Map.getMaxExtent
    */
    getMaxExtent : function(){
        return Utils.createExtent(this.getFrameworkMap().getMaxExtent());
    },

    /**
    * See @link Map.getExtent
    */
    getExtent : function(){
        var extent = this.utils.createExtent(this.getFrameworkMap().getExtent());
        return extent;
    },

    /*TODO:
        doIdentify : function(x,y){}
        update : function(){}
        removeMarker : function(markerName){}
        */
    /**
    *see @link Map.setMarker
    *TODO: marker icon path...
    */
    setMarker : function(markerName,x,y,type){
        if (this.markerLayer==null){
            this.markerLayer = new OpenLayers.Layer.Markers("Markers");
            this.frameworkMap.addLayer(this.markerLayer);
            var size = new OpenLayers.Size(17,17);
            var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
            this.defaultIcon= new OpenLayers.Icon(contextPath+'/viewer-html/common/openlayers/img/marker.png',size,offset);
        }
        /*According the 'type' load a icon: no types yet only default*/
        var icon= this.defaultIcon.clone();
        if (this.markers[markerName]==undefined){
            this.markers[markerName]= new OpenLayers.Marker(new OpenLayers.LonLat(x,y),icon);
            this.markerLayer.addMarker(this.markers[markerName]);
        }else{
            this.markers[markerName].moveTo(this.frameworkMap.getLayerPxFromLonLat(new OpenLayers.LonLat(x,y)));
        }

    },

    /**
    * see @link Map.removeMarker
    */
    removeMarker : function(markerName){
        if (this.markers[markerName] && this.markerLayer!=null){
            this.markerLayer.removeMarker(this.markers[markerName]);
            this.markers[markerName].destroy();
            delete this.markers[markerName];
        }
    },

    /**
     * @see Ext.util.Observable#addListener
     * @param event the event
     * @param handler the handler
     * @param scope the scope
     * Overwrite the addListener. Register event on the OpenLayers Map (only once)
     * If the event is thrown by the OpenLayers event the given handlers are called.
     */
    addListener : function(event,handler,scope){
        var olSpecificEvent = this.viewerController.mapComponent.getSpecificEventName(event);
        if(olSpecificEvent){
            if(!scope){
                scope = this;
            }
            /* Add event to OpenLayersMap only once, to prevent multiple fired events.
             * count the events for removing the listener again.
             */
            if(this.enabledEvents[olSpecificEvent]){
                this.enabledEvents[olSpecificEvent]++;
            }else{
                this.enabledEvents[olSpecificEvent] = 1;
                this.frameworkMap.events.register(olSpecificEvent, this, this.handleEvent);
            }
        }
        viewer.viewercontroller.openlayers.OpenLayersMap.superclass.addListener.call(this,event,handler,scope);
    },

    /**
     * @see Ext.util.Observable#removeListener
     * @param event the event
     * @param handler the handler
     * @param scope the scope
     * Overwrite the removeListener. Unregister the event on the OpenLayers Map if there
     * are no listeners anymore.
     */
    removeListener : function (event,handler,scope){
        var olSpecificEvent = this.viewerController.mapComponent.getSpecificEventName(event);
        if(olSpecificEvent){
            if(!scope){
                scope = this;
            }
            /* Remove event from OpenLayersMap if the number of events == 0
             * If there are no listeners for the OpenLayers event, remove the listener.
             */
            if(this.enabledEvents[olSpecificEvent]){
                this.enabledEvents[olSpecificEvent]--;
                if (this.enabledEvents[olSpecificEvent] <= 0){
                    this.enabledEvents[olSpecificEvent]=0;
                    this.frameworkMap.events.unregister(olSpecificEvent, this, this.handleEvent);
                }
            }
            viewer.viewercontroller.openlayers.OpenLayersMap.superclass.removeListener.call(this,event,handler,scope);
        }else{
            this.viewerController.logger.warning("Event not listed in OpenLayersMapComponent >"+ event + "<. The application  might not work correctly.");
        }
    },

    /**
     * Handles the events fired by OpenLayers.Map and propagates them to the registered objects.
     *
     */
    handleEvent : function(args){
        var event = args.type;
        var options={};
        var genericEvent = this.viewerController.mapComponent.getGenericEventName(event);
        if (genericEvent==viewer.viewercontroller.controller.Event.ON_LAYER_ADDED){
            options.layer=this.getLayerByOpenLayersId(args.layer.id);
            if (options.layer ==undefined){
                //if no layer found return, dont fire
                return;
            }
        }else if (genericEvent== viewer.viewercontroller.controller.Event.ON_LAYER_VISIBILITY_CHANGED){
            options.layer=this.getLayerByOpenLayersId(args.layer.id);
            if (options.layer ==undefined){
                //if no layer found return, dont fire
                return;
            }
            options.visible=args.layer.visibility;
        }else if (genericEvent==viewer.viewercontroller.controller.Event.ON_LAYER_REMOVED){
            options.layer=this.getLayerByOpenLayersId(args.layer.id);
            if (options.layer ==undefined){
                //if no layer found return, dont fire
                return;
            }
        }else if (genericEvent==viewer.viewercontroller.controller.Event.ON_FINISHED_CHANGE_EXTENT ||
                  genericEvent==viewer.viewercontroller.controller.Event.ON_CHANGE_EXTENT){
            options.extent=this.getExtent();
        }else{
            this.viewerController.logger.error("The event "+genericEvent+" is not implemented in the OpenLayersMap.handleEvent()");
        }
        this.fireEvent(genericEvent,this,options);
    },

    /**
    *See @link Map.getScale
    *@deprecated, use getResolution because it returns the resolution and not the scale
    */
    getScale : function(){
        return this.getResolution();
    },

    /**
    *See @link Map.getResolution
    */
    getResolution : function(){
        return this.getFrameworkMap().getResolution();
    },
    /**
     *See @link Map.getResolutions
     */
    getResolutions : function(){
        return this.getFrameworkMap().resolutions;
    },
    /**
    *See @link Map.coordinateToPixel
    *@returns a OpenLayers.pixel object (has a .x and a .y)
    */
    coordinateToPixel : function(x,y){
        return this.getFrameworkMap().getPixelFromLonLat(new OpenLayers.LonLat(x,y));
    },

    pixelToCoordinate : function (x,y){
        var lonLat = this.getFrameworkMap().getLonLatFromPixel(new OpenLayers.Pixel(x,y));
        return {x: lonLat.lon,y: lonLat.lat};
    },

    /**
    *see @link Map.getCenter
    *@return a OpenLayers.LonLat object with .x references to .lon and .y references to .lat
    */
    getCenter : function(){
        var lonlat=this.getFrameworkMap().getCenter();
        lonlat.x=lonlat.lon;
        lonlat.y=lonlat.lat;
        return lonlat;
    },

    getWidth : function(){
        var size = this.frameworkMap.getSize();
        return size.w;
    },

    getHeight : function (){
        var size = this.frameworkMap.getSize();
        return size.h;
    },
    updateSize : function(){
        this.getFrameworkMap().updateSize();
    },

    /**
     * The OpenLayers ID can't be changed. With this function you can get the
     * viewer.viewercontroller.openlayers.OpenLayersLayer with the openlayersid
     * @param olId the openlayers id of the layer
     * @return a viewer.viewercontroller.openlayers.OpenLayersLayer that
     * contains the openlayers layer with the given id.
     */
    getLayerByOpenLayersId: function(olId){
        for (var i=0; i < this.layers.length; i++){
            if (this.layers[i].frameworkLayer){
                if (this.layers[i].frameworkLayer.id == olId){
                    return this.layers[i];
                }
            }
        }
    }
});<|endoftext|>


<commit_before>"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2020-08-0
 * @modified
 *
 * @description
 * @difficulty Easy Medium Hard
 * @complexity O(n)
 * @augments
 * @example
 * @link
 * @solutions
 *
 */

// const log = console.log;

// window.SEO_FALG_FINISHED = window.SEO_FALG_FINISHED || false;
window.SEO_FALG_FINISHED = false;

const SEO_FALG = () => {
  const log = console.log;
  // let finished = false;
  let counter = 1;
  function auto() {
    const img = document.querySelector(`[data-flagcounter="img"]`);
    const a = document.querySelector(`[data-flagcounter="a"]`);
    if(a && img && !window.SEO_FALG_FINISHED) {
      log(`❓flagcounter.com trying ${counter} times`);
      a.href = `https://info.flagcounter.com/QIXi`;
      img.src = `https://s11.flagcounter.com/count2/QIXi/bg_000000/txt_00FF00/border_FF00FF/columns_3/maxflags_12/viewers_0/labels_1/pageviews_1/flags_0/percent_1/`;
      window.SEO_FALG_FINISHED = true;
    } else {
      log(` 🎉 flagcounter.com finished!`);
    }
    counter += 1;
  }
  let timer = setInterval(() => {
    if(!window.SEO_FALG_FINISHED) {
      auto();
    } else {
      log(` ✅ clearInterval, after ${counter} times`);
      clearInterval(timer);
    }
  }, 1000);
}

// IIFE
// (() => SEO_FALG())();

// new version, 2021.07.13
setTimeout(() => {
    (() => {
    const flagBox = document.querySelector(`[data-uid="seo-flag"]`);
    const domStr = `

    `;
    flagBox.insertAdjacentHTML(`beforeend`, domStr);
    log(` 🎉 flagcounter.com finished!`);
    })();
}, 1000);

<commit_msg>Update seo-flag.js<commit_after>"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2020-08-0
 * @modified
 *
 * @description
 * @difficulty Easy Medium Hard
 * @complexity O(n)
 * @augments
 * @example
 * @link
 * @solutions
 *
 */

// new version, 2021.07.13
setTimeout(() => {
    (() => {
        const flagBox = document.querySelector(`[data-uid="seo-flag"]`);
        const domStr = `

        `;
        flagBox.insertAdjacentHTML(`beforeend`, domStr);
        const log = console.log;
        log(` 🎉 flagcounter.com finished!`);
    })();
}, 1000);

// const log = console.log;

// window.SEO_FALG_FINISHED = window.SEO_FALG_FINISHED || false;
/*
window.SEO_FALG_FINISHED = false;

const SEO_FALG = () => {
  const log = console.log;
  // let finished = false;
  let counter = 1;
  function auto() {
    const img = document.querySelector(`[data-flagcounter="img"]`);
    const a = document.querySelector(`[data-flagcounter="a"]`);
    if(a && img && !window.SEO_FALG_FINISHED) {
      log(`❓flagcounter.com trying ${counter} times`);
      a.href = `https://info.flagcounter.com/QIXi`;
      img.src = `https://s11.flagcounter.com/count2/QIXi/bg_000000/txt_00FF00/border_FF00FF/columns_3/maxflags_12/viewers_0/labels_1/pageviews_1/flags_0/percent_1/`;
      window.SEO_FALG_FINISHED = true;
    } else {
      log(` 🎉 flagcounter.com finished!`);
    }
    counter += 1;
  }
  let timer = setInterval(() => {
    if(!window.SEO_FALG_FINISHED) {
      auto();
    } else {
      log(` ✅ clearInterval, after ${counter} times`);
      clearInterval(timer);
    }
  }, 1000);
}

// IIFE
(() => SEO_FALG())();

*/
<|endoftext|>


<commit_before>'use strict';
/* Controller for Fragment Sankey Diagram View */
angular.module('lcaApp.fragment.sankey',
                ['ui.router', 'lcaApp.sankey', 'lcaApp.resources.service', 'lcaApp.idmap.service', 'angularSpinner'])
    .controller('FragmentSankeyCtrl',
        ['$scope', '$stateParams', 'usSpinnerService', '$q', '$window',
        'ScenarioService', 'FragmentService', 'FragmentFlowService', 'FlowForFragmentService', 'ProcessService',
        'FlowPropertyForFragmentService', 'NodeTypeService',
        function ($scope, $stateParams, usSpinnerService, $q, $window, ScenarioService, FragmentService,
                  FragmentFlowService, FlowForFragmentService, ProcessService, FlowPropertyForFragmentService,
                  NodeTypeService) {
            var fragmentID = $stateParams.fragmentID,
                scenarioID = $stateParams.scenarioID,
            //
                graph = {},
                reverseIndex = {},  // map fragmentFlowID to graph.nodes and graph.links
                baseValue = 1E-14;  // sankey link base value (replaces 0).

            /**
             * Build sankey graph from loaded data
             * @param {Boolean} makeNew  Indicates if new graph should be created. False means update existing graph.
             */
            function buildGraph(makeNew) {
                var fragmentFlows = FragmentFlowService.objects;
                graph.isNew = makeNew;
                if (makeNew) {
                    reverseIndex = {};
                    graph.nodes = [];
                    // Add a node for every flow
                    fragmentFlows.forEach(addGraphNode);
                }
                // Add a link for every flow. source and target are indexes into nodes array.
                graph.links = [];
                fragmentFlows.forEach(addGraphLink);
            }

            /**
             * Get magnitude of link with a flow property
             * @param {{fragmentFlowID:Number, parentFragmentFlowID:Number, directionID:Number, flowPropertyMagnitudes:Array}}  link
             * @param {Number}  flowPropertyID    flow property key
             * @param {Number}  activityLevel    current scenario's activity level
             * @return {Number} The magnitude, if link has the flow property. Otherwise, null.
             */
            function getMagnitude(link, flowPropertyID, activityLevel) {
                var magnitude = null, flowPropertyMagnitudes = [];
                if ("flowPropertyMagnitudes" in link) {
                    flowPropertyMagnitudes = link.flowPropertyMagnitudes.filter(
                        /**
                         * @param {{flowPropertyID:number}}  lm
                         */
                            function (lm) {
                            return +lm.flowPropertyID === flowPropertyID;
                        });
                }
                if (flowPropertyMagnitudes && flowPropertyMagnitudes.length > 0) {
                    magnitude = flowPropertyMagnitudes[0].magnitude * activityLevel;
                }
                return magnitude;
            }

            /**
             * Add graph node for fragment flow element
             * @param {{fragmentFlowID:number}} element
             */
            function addGraphNode(element) {
                var node = {
                        nodeTypeID: element.nodeTypeID,
                        nodeID: element.fragmentFlowID,
                        nodeName: "",
                        toolTip: ""
                    },
                    fragFlow = FragmentFlowService.get(element.fragmentFlowID),
                    nodeType = NodeTypeService.get(element.nodeTypeID),
                    refObj //, navState
                    ;

                if (fragFlow) {
                    node.nodeName = fragFlow["shortName"];
                }
                if (nodeType) {
                    node.toolTip = "" + nodeType.name + "";
                }
                if ("processID" in element) {
                    refObj = ProcessService.get(element.processID);
//                    node.toolTip = node.toolTip + "
" + refObj.name + "
";
                } else if ("subFragmentID" in element) {
                    refObj = FragmentService.get(element.subFragmentID);
//                    navState = "scenarios.fragment({fragmentID: " + fragmentID +
//                        ", scenarioID: " + scenarioID + "})";
//                    node.toolTip = node.toolTip + "
" + refObj.name + "
";
//                    navState = "#/scenarios/" + scenarioID + "/fragment-sankey/" + fragmentID;
//                    node.toolTip = node.toolTip + "
" + refObj.name + "
";
                }
                if (refObj) {
                    node.selectable = true;
                    node.toolTip = node.toolTip + "
" + refObj.name +
                        "
Click to navigate";
                }

                reverseIndex[element.fragmentFlowID] = graph.nodes.push(node) - 1;
            }

            /**
             * Add graph link for fragmentflow element
             * @param {{fragmentFlowID:Number, parentFragmentFlowID:Number, directionID:Number, flowPropertyMagnitudes:Array}} element
             */
            function addGraphLink(element) {
                var link, parentIndex,
                    nodeIndex = reverseIndex[element.fragmentFlowID],
                    activityLevel = $scope.scenario["activityLevel"];

                if ("parentFragmentFlowID" in element) {
                    var magnitude = getMagnitude(element, $scope.selectedFlowProperty["flowPropertyID"], activityLevel),
                        value = (magnitude === null || magnitude <= 0) ? baseValue : baseValue + magnitude,
                        flow = FlowForFragmentService.get(element.flowID),
                        unit = $scope.selectedFlowProperty["referenceUnit"];

                    parentIndex = reverseIndex[element.parentFragmentFlowID];
                    link = {
                        nodeID: element.fragmentFlowID,
                        flowID: element.flowID,
                        value: value
                    };
                    if (magnitude) {
                        link.magnitude = magnitude;
                        link.toolTip = flow.name + " : " + magnitude.toPrecision(3) + " " + unit;
                    } else {
                        link.toolTip = flow.name + " does not have property : " + $scope.selectedFlowProperty["name"];
                    }
                    if (element.directionID === 1) {
                        link.source = nodeIndex;
                        link.target = parentIndex;
                    } else {
                        link.source = parentIndex;
                        link.target = nodeIndex;
                    }
                    graph.links.push(link);
                }
            }

            function stopWaiting() {
                usSpinnerService.stop("spinner-lca");
            }

            function handleFailure(errMsg) {
                stopWaiting();
                $window.alert(errMsg);
            }

            /**
             * Prepare fragment data for visualization
             */
            function visualizeFragment() {
                $scope.fragment = FragmentService.get(fragmentID);
                if ($scope.fragment) {
                    setFlowProperties();
                    buildGraph(true);
                    stopWaiting();
                    $scope.graph = graph;
                } else {
                    handleFailure("Invalid fragmentID: " + fragmentID);
                }
            }

            /**
             * Function called after requests for resources have been fulfilled.
             */
            function handleSuccess() {
                $scope.scenario = ScenarioService.get(scenarioID);
                if ($scope.scenario) {
                    visualizeFragment();
                } else {
                    handleFailure("Invalid scenarioID: " + scenarioID);
                }
            }

            /**
             * Compare function used to sort array of objects by name
             */
            function compareNames(a, b) {
                if (a.name > b.name) {
                    return 1;
                }
                if (a.name < b.name) {
                    return -1;
                }
                // a must be equal to b
                return 0;
            }

            /**
             * Update scope with flow properties and select one
             */
            function setFlowProperties() {
                //
                //  If the last flow property selection is not in the current list, reset to the default flow property,
                //  if that is in the list. Otherwise, set to first element in resource payload.
                //
                var selectedFlowProperty = $scope.selectedFlowProperty,
                    flowProperties = FlowPropertyForFragmentService.objects;
                flowProperties.sort(compareNames);
                if (flowProperties) {
                    if (selectedFlowProperty) {
                        selectedFlowProperty = flowProperties.find(function (element) {
                            return (element["flowPropertyID"] === selectedFlowProperty["flowPropertyID"]);
                        });
                    }
                    if (!selectedFlowProperty) {
                        selectedFlowProperty = flowProperties.find(function (element) {
                            return (element.name === "Mass");
                        });
                        if (!selectedFlowProperty) {
                            selectedFlowProperty = flowProperties[0];
                        }
                    }
                } else {
                    selectedFlowProperty = null;
                }
                $scope.flowProperties = flowProperties;
                $scope.selectedFlowProperty = selectedFlowProperty;
            }

            /**
             * Get all data resources
             */
            function getData() {
                $q.all([ScenarioService.load(), FragmentService.load(), ProcessService.load(),
                    FlowPropertyForFragmentService.load({fragmentID: fragmentID}),
                    FragmentFlowService.load({scenarioID: scenarioID, fragmentID: fragmentID}),
                    FlowForFragmentService.load({fragmentID: fragmentID}),
                    NodeTypeService.load()])
                    .then(handleSuccess,
                    handleFailure);
            }

            /**
             * Get data resources that are filtered by fragment.
             * Called after fragment selection changes.
             * If successful, visualize selected fragment.
             */
            function getDataForFragment() {
                $q.all([FlowPropertyForFragmentService.load({fragmentID: fragmentID}),
                    FragmentFlowService.load({scenarioID: scenarioID, fragmentID: fragmentID}),
                    FlowForFragmentService.load({fragmentID: fragmentID})])
                    .then(visualizeFragment,
                    handleFailure);
            }

            /**
             * Called when flow property selection changes.
             * Updates existing sankey graph.
             */
            $scope.onFlowPropertyChange = function () {
                //console.log("Flow property changed. Current: " + $scope.selectedFlowProperty.name);
                buildGraph(false);
                $scope.graph = graph;
            };

            /**
             * Called when a parent fragment is selected from fragment breadcrumbs.
             * Updates fragment breadcrumbs and gets new fragment data.
             * @param fragment  Fragment selected
             * @param index     Breadcrumb index
             */
            $scope.onParentFragmentSelected = function (fragment, index) {
                $scope.parentFragments.splice(index);
                fragmentID = fragment.fragmentID;
                getDataForFragment();
            };

            /**
             * Called when a node in sankey directive is selected.
             * The node can represent either a fragment or a process.
             * @param newVal    The selected node
             */
            function onNodeSelectionChange(newVal) {
                if (newVal) {
                    var fragmentFlow = FragmentFlowService.get(newVal.nodeID);
                    if (newVal.nodeTypeID === 2) {
                        $scope.parentFragments.push($scope.fragment);
                        $scope.fragment = null;
                        fragmentID = fragmentFlow.subFragmentID;
                        getDataForFragment();
                    }
                }
            }

            /**
             * Get flow table content
             * For each Sankey link provided, get
             * flow name, magnitude and unit associated with selected flow property.
             * If the link does have the selected flow property, display
             * magnitude and unit for the flow's reference flow property.
             *
             * @param {Array}  nodeLinks     Sankey links
             * @return {Array}  Data for display in table
             */
            function getFlowRows(nodeLinks) {
                var flowData = [],
                    activityLevel = $scope.scenario["activityLevel"];

                nodeLinks.forEach( function (l) {
                    if ("flowID" in l) {
                        var flow = FlowForFragmentService.get(l.flowID),
                            flowPropertyID = $scope.selectedFlowProperty["flowPropertyID"],
                            magnitude = l.magnitude,
                            unit = "";
                        if (magnitude) {
                            unit = $scope.selectedFlowProperty["referenceUnit"];
                        } else  {
                            var fp;
                            flowPropertyID = flow["referenceFlowPropertyID"];
                            magnitude = getMagnitude(l, flowPropertyID, activityLevel);
                            fp = FlowPropertyForFragmentService.get(flowPropertyID);
                            if (fp) {
                               unit = fp["referenceUnit"];
                            }
                        }
                        flowData.push({ name: flow.name, magnitude: magnitude, unit: unit });
                    }
                });
                return flowData;
            }

            function onMouseOverNode(node) {
                if (node) {
                    $scope.inputFlows = getFlowRows(node.targetLinks);
                    $scope.outputFlows = getFlowRows(node.sourceLinks);
                }
            }

            usSpinnerService.spin("spinner-lca");
            $scope.color = { domain: ([2, 3, 4, 1, 0]), range: colorbrewer.Set3[5], property: "nodeTypeID" };
            $scope.selectedFlowProperty = null;
            $scope.selectedNode = null;
            $scope.mouseOverNode = null;
            $scope.parentFragments = [];
            $scope.fragment = null;
            $scope.scenario = null;
            $scope.$watch("selectedNode", onNodeSelectionChange);
            $scope.$watch("mouseOverNode", onMouseOverNode);
            getData();

        }]);<commit_msg>Vis: Fill in missing flow magnitude<commit_after>'use strict';
/* Controller for Fragment Sankey Diagram View */
angular.module('lcaApp.fragment.sankey',
                ['ui.router', 'lcaApp.sankey', 'lcaApp.resources.service', 'lcaApp.idmap.service', 'angularSpinner'])
    .controller('FragmentSankeyCtrl',
        ['$scope', '$stateParams', 'usSpinnerService', '$q', '$window',
        'ScenarioService', 'FragmentService', 'FragmentFlowService', 'FlowForFragmentService', 'ProcessService',
        'FlowPropertyForFragmentService', 'NodeTypeService',
        function ($scope, $stateParams, usSpinnerService, $q, $window, ScenarioService, FragmentService,
                  FragmentFlowService, FlowForFragmentService, ProcessService, FlowPropertyForFragmentService,
                  NodeTypeService) {
            var fragmentID = $stateParams.fragmentID,
                scenarioID = $stateParams.scenarioID,
            //
                graph = {},
                reverseIndex = {},  // map fragmentFlowID to graph.nodes and graph.links
                baseValue = 1E-14;  // sankey link base value (replaces 0).

            /**
             * Build sankey graph from loaded data
             * @param {Boolean} makeNew  Indicates if new graph should be created. False means update existing graph.
             */
            function buildGraph(makeNew) {
                var fragmentFlows = FragmentFlowService.objects;
                graph.isNew = makeNew;
                if (makeNew) {
                    reverseIndex = {};
                    graph.nodes = [];
                    // Add a node for every flow
                    fragmentFlows.forEach(addGraphNode);
                }
                // Add a link for every flow. source and target are indexes into nodes array.
                graph.links = [];
                fragmentFlows.forEach(addGraphLink);
            }

            /**
             * Get magnitude of link with a flow property
             * @param {{fragmentFlowID:Number, parentFragmentFlowID:Number, directionID:Number, flowPropertyMagnitudes:Array}}  link
             * @param {Number}  flowPropertyID    flow property key
             * @param {Number}  activityLevel    current scenario's activity level
             * @return {Number} The magnitude, if link has the flow property. Otherwise, null.
             */
            function getMagnitude(link, flowPropertyID, activityLevel) {
                var magnitude = null, flowPropertyMagnitudes = [];
                if ("flowPropertyMagnitudes" in link) {
                    flowPropertyMagnitudes = link.flowPropertyMagnitudes.filter(
                        /**
                         * @param {{flowPropertyID:number}}  lm
                         */
                            function (lm) {
                            return +lm.flowPropertyID === flowPropertyID;
                        });
                }
                if (flowPropertyMagnitudes && flowPropertyMagnitudes.length > 0) {
                    magnitude = flowPropertyMagnitudes[0].magnitude * activityLevel;
                }
                return magnitude;
            }

            /**
             * Add graph node for fragment flow element
             * @param {{fragmentFlowID:number}} element
             */
            function addGraphNode(element) {
                var node = {
                        nodeTypeID: element.nodeTypeID,
                        nodeID: element.fragmentFlowID,
                        nodeName: "",
                        toolTip: ""
                    },
                    fragFlow = FragmentFlowService.get(element.fragmentFlowID),
                    nodeType = NodeTypeService.get(element.nodeTypeID),
                    refObj //, navState
                    ;

                if (fragFlow) {
                    node.nodeName = fragFlow["shortName"];
                }
                if (nodeType) {
                    node.toolTip = "" + nodeType.name + "";
                }
                if ("processID" in element) {
                    refObj = ProcessService.get(element.processID);
//                    node.toolTip = node.toolTip + "
" + refObj.name + "
";
                } else if ("subFragmentID" in element) {
                    refObj = FragmentService.get(element.subFragmentID);
//                    navState = "scenarios.fragment({fragmentID: " + fragmentID +
//                        ", scenarioID: " + scenarioID + "})";
//                    node.toolTip = node.toolTip + "
" + refObj.name + "
";
//                    navState = "#/scenarios/" + scenarioID + "/fragment-sankey/" + fragmentID;
//                    node.toolTip = node.toolTip + "
" + refObj.name + "
";
                }
                if (refObj) {
                    node.selectable = true;
                    node.toolTip = node.toolTip + "
" + refObj.name +
                        "
Click to navigate";
                }

                reverseIndex[element.fragmentFlowID] = graph.nodes.push(node) - 1;
            }

            /**
             * Add graph link for fragmentflow element
             * @param {{fragmentFlowID:Number, parentFragmentFlowID:Number, directionID:Number, flowPropertyMagnitudes:Array}} element
             */
            function addGraphLink(element) {
                var link, parentIndex,
                    nodeIndex = reverseIndex[element.fragmentFlowID],
                    activityLevel = $scope.scenario["activityLevel"];

                if ("parentFragmentFlowID" in element) {
                    var magnitude = getMagnitude(element, $scope.selectedFlowProperty["flowPropertyID"], activityLevel),
                        value = (magnitude === null || magnitude <= 0) ? baseValue : baseValue + magnitude,
                        flow = FlowForFragmentService.get(element.flowID),
                        unit = $scope.selectedFlowProperty["referenceUnit"];

                    parentIndex = reverseIndex[element.parentFragmentFlowID];
                    link = {
                        nodeID: element.fragmentFlowID,
                        flowID: element.flowID,
                        value: value
                    };
                    if (magnitude) {
                        link.magnitude = magnitude;
                        link.toolTip = flow.name + " : " + magnitude.toPrecision(3) + " " + unit;
                    } else {
                        link.toolTip = flow.name + " does not have property : " + $scope.selectedFlowProperty["name"];
                    }
                    if (element.directionID === 1) {
                        link.source = nodeIndex;
                        link.target = parentIndex;
                    } else {
                        link.source = parentIndex;
                        link.target = nodeIndex;
                    }
                    graph.links.push(link);
                }
            }

            function stopWaiting() {
                usSpinnerService.stop("spinner-lca");
            }

            function handleFailure(errMsg) {
                stopWaiting();
                $window.alert(errMsg);
            }

            /**
             * Prepare fragment data for visualization
             */
            function visualizeFragment() {
                $scope.fragment = FragmentService.get(fragmentID);
                if ($scope.fragment) {
                    setFlowProperties();
                    buildGraph(true);
                    stopWaiting();
                    $scope.graph = graph;
                } else {
                    handleFailure("Invalid fragmentID: " + fragmentID);
                }
            }

            /**
             * Function called after requests for resources have been fulfilled.
             */
            function handleSuccess() {
                $scope.scenario = ScenarioService.get(scenarioID);
                if ($scope.scenario) {
                    visualizeFragment();
                } else {
                    handleFailure("Invalid scenarioID: " + scenarioID);
                }
            }

            /**
             * Compare function used to sort array of objects by name
             */
            function compareNames(a, b) {
                if (a.name > b.name) {
                    return 1;
                }
                if (a.name < b.name) {
                    return -1;
                }
                // a must be equal to b
                return 0;
            }

            /**
             * Update scope with flow properties and select one
             */
            function setFlowProperties() {
                //
                //  If the last flow property selection is not in the current list, reset to the default flow property,
                //  if that is in the list. Otherwise, set to first element in resource payload.
                //
                var selectedFlowProperty = $scope.selectedFlowProperty,
                    flowProperties = FlowPropertyForFragmentService.objects;
                flowProperties.sort(compareNames);
                if (flowProperties) {
                    if (selectedFlowProperty) {
                        selectedFlowProperty = flowProperties.find(function (element) {
                            return (element["flowPropertyID"] === selectedFlowProperty["flowPropertyID"]);
                        });
                    }
                    if (!selectedFlowProperty) {
                        selectedFlowProperty = flowProperties.find(function (element) {
                            return (element.name === "Mass");
                        });
                        if (!selectedFlowProperty) {
                            selectedFlowProperty = flowProperties[0];
                        }
                    }
                } else {
                    selectedFlowProperty = null;
                }
                $scope.flowProperties = flowProperties;
                $scope.selectedFlowProperty = selectedFlowProperty;
            }

            /**
             * Get all data resources
             */
            function getData() {
                $q.all([ScenarioService.load(), FragmentService.load(), ProcessService.load(),
                    FlowPropertyForFragmentService.load({fragmentID: fragmentID}),
                    FragmentFlowService.load({scenarioID: scenarioID, fragmentID: fragmentID}),
                    FlowForFragmentService.load({fragmentID: fragmentID}),
                    NodeTypeService.load()])
                    .then(handleSuccess,
                    handleFailure);
            }

            /**
             * Get data resources that are filtered by fragment.
             * Called after fragment selection changes.
             * If successful, visualize selected fragment.
             */
            function getDataForFragment() {
                $q.all([FlowPropertyForFragmentService.load({fragmentID: fragmentID}),
                    FragmentFlowService.load({scenarioID: scenarioID, fragmentID: fragmentID}),
                    FlowForFragmentService.load({fragmentID: fragmentID})])
                    .then(visualizeFragment,
                    handleFailure);
            }

            /**
             * Called when flow property selection changes.
             * Updates existing sankey graph.
             */
            $scope.onFlowPropertyChange = function () {
                //console.log("Flow property changed. Current: " + $scope.selectedFlowProperty.name);
                buildGraph(false);
                $scope.graph = graph;
            };

            /**
             * Called when a parent fragment is selected from fragment breadcrumbs.
             * Updates fragment breadcrumbs and gets new fragment data.
             * @param fragment  Fragment selected
             * @param index     Breadcrumb index
             */
            $scope.onParentFragmentSelected = function (fragment, index) {
                $scope.parentFragments.splice(index);
                fragmentID = fragment.fragmentID;
                getDataForFragment();
            };

            /**
             * Called when a node in sankey directive is selected.
             * The node can represent either a fragment or a process.
             * @param newVal    The selected node
             */
            function onNodeSelectionChange(newVal) {
                if (newVal) {
                    var fragmentFlow = FragmentFlowService.get(newVal.nodeID);
                    if (newVal.nodeTypeID === 2) {
                        $scope.parentFragments.push($scope.fragment);
                        $scope.fragment = null;
                        fragmentID = fragmentFlow.subFragmentID;
                        getDataForFragment();
                    }
                }
            }

            /**
             * Get flow table content
             * For each Sankey link provided, get
             * flow name, magnitude and unit associated with selected flow property.
             * If the link does have the selected flow property, display
             * magnitude and unit for the flow's reference flow property.
             *
             * @param {Array}  nodeLinks     Sankey links
             * @return {Array}  Data for display in table
             */
            function getFlowRows(nodeLinks) {
                var flowData = [],
                    activityLevel = $scope.scenario["activityLevel"];

                nodeLinks.forEach( function (l) {
                    if ("flowID" in l) {
                        var flow = FlowForFragmentService.get(l.flowID),
                            flowPropertyID = $scope.selectedFlowProperty["flowPropertyID"],
                            magnitude = l.magnitude,
                            unit = "";
                        if (magnitude) {
                            unit = $scope.selectedFlowProperty["referenceUnit"];
                        } else  {
                            var fp, ff;
                            ff = FragmentFlowService.get(l.nodeID);
                            flowPropertyID = flow["referenceFlowPropertyID"];
                            magnitude = getMagnitude(ff, flowPropertyID, activityLevel);
                            fp = FlowPropertyForFragmentService.get(flowPropertyID);
                            if (fp) {
                               unit = fp["referenceUnit"];
                            }
                        }
                        flowData.push({ name: flow.name, magnitude: magnitude, unit: unit });
                    }
                });
                return flowData;
            }

            function onMouseOverNode(node) {
                if (node) {
                    $scope.inputFlows = getFlowRows(node.targetLinks);
                    $scope.outputFlows = getFlowRows(node.sourceLinks);
                }
            }

            usSpinnerService.spin("spinner-lca");
            $scope.color = { domain: ([2, 3, 4, 1, 0]), range: colorbrewer.Set3[5], property: "nodeTypeID" };
            $scope.selectedFlowProperty = null;
            $scope.selectedNode = null;
            $scope.mouseOverNode = null;
            $scope.parentFragments = [];
            $scope.fragment = null;
            $scope.scenario = null;
            $scope.$watch("selectedNode", onNodeSelectionChange);
            $scope.$watch("mouseOverNode", onMouseOverNode);
            getData();

        }]);<|endoftext|>


<commit_before>/*
 *  local storage(web/react native) wrapper
 *  sunnylqm 2016-05-27
 *  version 0.1.0
 */

export default class Storage {
  constructor(options = {}) {
    let me = this;

    me._SIZE = options.size || 1000;   // maximum capacity
    me.sync = options.sync || {};      // remote sync method
    me.defaultExpires = options.defaultExpires || 1000 * 3600 * 24;
    me.enableCache = options.enableCache || true;
    me._s = options.storageBackend || null;
    me.isPromise = options.isPromise || true;
    me._innerVersion = 11;
    me.cache = {};

    if(!me._s) {
      if(typeof window !== 'undefined' && window.localStorage) {
        try {
          // avoid key conflict
          window.localStorage.setItem('__react_native_storage_test', 'test');

          me._s = window.localStorage;
          me.isPromise = false;
        }
        catch(e) {
          console.warn(e);
          delete me._s;
          throw e;
        }
      }
      else {
        me._s = require('react-native').AsyncStorage;
      }
    }

    me._mapPromise = me.getItem('map').then( map => {
      me._m = me._checkMap(map && JSON.parse(map) || {});
      // delete me._mapPromise;
    });
  }
  getItem(key) {
    return this._s
      ?
      this.isPromise ? this._s.getItem(key) : Promise.resolve(this._s.getItem(key))
      :
      Promise.resolve();
  }
  setItem(key, value) {
    return this._s
      ?
      this.isPromise ? this._s.setItem(key, value) : Promise.resolve(this._s.setItem(key, value))
      :
      Promise.resolve();
  }
  removeItem(key) {
    return this._s
      ? this.isPromise ? this._s.removeItem(key) : Promise.resolve(this._s.removeItem(key))
      :
      Promise.resolve();
  }
  _checkMap(map) {
    let me = this;
    if(map && map.innerVersion && map.innerVersion === me._innerVersion ) {
      return map;
    }
    else {
      return {
        innerVersion: me._innerVersion,
        index: 0,
        __keys__: {}
      };
    }
  }
  _getId(key, id) {
    return key + '_' + id;
  }
  _saveToMap(params) {
    let { key, id, data } = params,
      newId = this._getId(key, id),
      m = this._m;
    if(m[newId] !== undefined) {
      //update existed data
      if(this.enableCache) this.cache[newId] = JSON.parse(data);
      return this.setItem('map_' + m[newId], data);
    }
    if(m[m.index] !== undefined){
      //loop over, delete old data
      let oldId = m[m.index];
      let splitOldId = oldId.split('_')
      delete m[oldId];
      this._removeIdInKey(splitOldId[0], splitOldId[1])
      if(this.enableCache) {
        delete this.cache[oldId];
      }
    }
    m[newId] = m.index;
    m[m.index] = newId;

    m.__keys__[key] = m.__keys__[key] || [];
    m.__keys__[key].push(id);

    if(this.enableCache) {
      const cacheData = JSON.parse(data);
      this.cache[newId] = cacheData;
    }
    let currentIndex = m.index;
    if(++m.index === this._SIZE) {
      m.index = 0;
    }
    this.setItem('map_' + currentIndex, data);
    this.setItem('map', JSON.stringify(m));
  }
  save(params) {
    let me = this;
    let { key, id, rawData, expires } = params;
    if(key.toString().indexOf('_') !== -1) {
      console.error('Please do not use "_" in key!');
    }
    let data = {
      rawData
    };
    let now = new Date().getTime();
    if(expires === undefined) {
      expires = me.defaultExpires;
    }
    if(expires !== null) {
      data.expires = now + expires;
    }
    data = JSON.stringify(data);
    if(id === undefined) {
      if(me.enableCache) {
        const cacheData = JSON.parse(data);
        me.cache[key] = cacheData;
      }
      return me.setItem(key, data);
    }
    else {
      if(id.toString().indexOf('_') !== -1) {
        console.error('Please do not use "_" in id!');
      }
      return this._mapPromise.then(() => me._saveToMap({
        key,
        id,
        data
      }));
    }
  }
  getBatchData(querys) {
    let me = this;
    let tasks = [];
    for(let i = 0, query; query = querys[i]; i++) {
      tasks[i] = me.load(query);
    }
    return Promise.all(tasks);
  }
  getBatchDataWithIds(params) {
    let me = this;
    let { key, ids, syncInBackground } = params;

    return Promise.all(
      ids.map((id) => me.load({ key, id, syncInBackground, autoSync: false, batched: true }))
    ).then((results) => {
      return new Promise((resolve, reject) => {
        const ids = results.filter((value) => value.syncId !== undefined);
        if(!ids.length){
          return resolve();
        }
        return me.sync[key]({
          id: ids.map((value) => value.syncId),
          resolve,
          reject
        });
      }).then((data) => {
        return results.map(value => {
          return value.syncId ? data.shift() : value
        });
      });
    })
  }
  _lookupGlobalItem(params) {
    let me = this,
      ret;
    let { key } = params;
    if(me.enableCache && me.cache[key] !== undefined) {
      ret = me.cache[key];
      return me._loadGlobalItem({ ret, ...params });
    }
    return me.getItem(key).then(ret => me._loadGlobalItem({ ret, ...params }));
  }
  _loadGlobalItem(params) {
    let me = this;
    let { key, ret, autoSync, syncInBackground } = params;
    if(ret === null || ret === undefined) {
      if(autoSync && me.sync[key]) {
        return new Promise((resolve, reject) => me.sync[key]({resolve, reject}));
      }
      return Promise.reject();
    }
    if(typeof ret === 'string') {
      ret = JSON.parse(ret);
    }
    let now = new Date().getTime();
    if(autoSync && ret.expires < now) {
      if (me.sync[key]){
        if(syncInBackground) {
          me.sync[key]({});
          return Promise.resolve(ret.rawData);
        }
        return new Promise((resolve, reject) => me.sync[key]({resolve, reject}));
      }
      return Promise.reject();
    }
    return Promise.resolve(ret.rawData);
  }
  _noItemFound(params) {
    let me = this;
    let { key, id, autoSync } = params;
    if(me.sync[key]) {
      if(autoSync) {
        return new Promise((resolve, reject) => me.sync[key]({id, resolve, reject}));
      }
      return Promise.resolve({ syncId: id });
    }
    return Promise.reject();
  }
  _loadMapItem(params) {
    let me = this;
    let { ret, key, id, autoSync, batched, syncInBackground } = params;
    if(ret === null || ret === undefined) {
      return me._noItemFound(params);
    }
    if(typeof ret === 'string'){
      ret = JSON.parse(ret);
    }
    let now = new Date().getTime();
    if(autoSync && ret.expires < now) {
      if(me.sync[key]) {
        if(syncInBackground){
          me.sync[key]({id});
          return Promise.resolve(ret.rawData);
        }
        return new Promise((resolve, reject) => me.sync[key]({id, resolve, reject}));
      }
      if(batched) {
        return Promise.resolve({ syncId: id });
      }
      return Promise.reject();
    }
    return Promise.resolve(ret.rawData);
  }
  _lookUpInMap(params) {
    let me = this,
      m = me._m,
      ret;
    let { key, id } = params;
    let newId = me._getId(key, id);
    if(me.enableCache && me.cache[newId]) {
      ret = me.cache[newId];
      return me._loadMapItem( {ret, ...params } );
    }
    if(m[newId] !== undefined) {
      return me.getItem('map_' + m[newId]).then( ret => me._loadMapItem( {ret, ...params } ) );
    }
    return me._noItemFound( {ret, ...params } );
  }
  remove(params) {
    return this._mapPromise.then(() => {
      let me = this,
        m = me._m;
      let { key, id } = params;

      if(id === undefined) {
        if(me.enableCache && me.cache[key]) {
          delete me.cache[key];
        }
        return me.removeItem(key);
      }
      let newId = me._getId(key, id);

      //remove existed data
      if(m[newId] !== undefined) {
        if(me.enableCache && me.cache[newId]) {
          delete me.cache[newId];
        }
        me._removeIdInKey(key, id);
        let idTobeDeleted = m[newId];
        delete m[newId];
        me.setItem('map', JSON.stringify(m));
        return me.removeItem('map_' + idTobeDeleted);
      }
    });
  }
  _removeIdInKey(key, id) {
    let indexTobeRemoved = this._m.__keys__[key].indexOf(id);
    if(indexTobeRemoved !== -1) {
      this._m.__keys__[key].splice(indexTobeRemoved, 1);
    }
  }
  load(params) {
    let me = this;
    let { key, id, autoSync, syncInBackground } = params;
    if(autoSync === undefined) {
      autoSync = true;
    }
    if(syncInBackground === undefined) {
      syncInBackground = true;
    }
    return me._mapPromise.then(() => new Promise((resolve, reject) => {
      if(id === undefined) {
        return resolve(me._lookupGlobalItem({key, resolve, reject, autoSync, syncInBackground}));
      }
      return resolve(me._lookUpInMap({key, id, resolve, reject, autoSync, syncInBackground}));
    }));
  }
  clearMap(){
    let me = this;
    me.removeItem('map');
    me._m = {
      innerVersion: me._innerVersion,
      index: 0
    };
  }
  clearMapForKey(key) {
    return this._mapPromise.then(() => {
      let tasks = this._m.__keys__[key].map(id => this.remove({ key, id }));
      return Promise.all(tasks);
    });
  }
  getIdsForKey(key) {
    return this._mapPromise.then(() => {
      return this._m.__keys__[key] || [];
    });
  }
  getAllDataForKey(key, options) {
    options = Object.assign({ syncInBackground: true }, options);
    return this.getIdsForKey(key).then(ids => {
      let querys = ids.map(id => ({ key, id, syncInBackground: options.syncInBackground }));
      return this.getBatchData(querys);
    });
  }

}

// function noop() {}
//
// // compatible with legacy version promise
// function handlePromise (fn) {
//   return new Promise((resolve, reject) => {
//     var promise, isPromise;
//     if (isPromise = (promise = fn((data) => isPromise ? noop() : resolve(data), (err) => isPromise ? noop() : reject(err))) instanceof Promise) resolve(promise);
//   });
// }
<commit_msg>defaultExpires can be null now<commit_after>/*
 *  local storage(web/react native) wrapper
 *  sunnylqm 2016-05-27
 *  version 0.1.0
 */

export default class Storage {
  constructor(options = {}) {
    let me = this;

    me._SIZE = options.size || 1000;   // maximum capacity
    me.sync = options.sync || {};      // remote sync method
    me.defaultExpires = options.defaultExpires !== undefined ?
      options.defaultExpires || 1000 * 3600 * 24;
    me.enableCache = options.enableCache || true;
    me._s = options.storageBackend || null;
    me.isPromise = options.isPromise || true;
    me._innerVersion = 11;
    me.cache = {};

    if(!me._s) {
      if(typeof window !== 'undefined' && window.localStorage) {
        try {
          // avoid key conflict
          window.localStorage.setItem('__react_native_storage_test', 'test');

          me._s = window.localStorage;
          me.isPromise = false;
        }
        catch(e) {
          console.warn(e);
          delete me._s;
          throw e;
        }
      }
      else {
        me._s = require('react-native').AsyncStorage;
      }
    }

    me._mapPromise = me.getItem('map').then( map => {
      me._m = me._checkMap(map && JSON.parse(map) || {});
      // delete me._mapPromise;
    });
  }
  getItem(key) {
    return this._s
      ?
      this.isPromise ? this._s.getItem(key) : Promise.resolve(this._s.getItem(key))
      :
      Promise.resolve();
  }
  setItem(key, value) {
    return this._s
      ?
      this.isPromise ? this._s.setItem(key, value) : Promise.resolve(this._s.setItem(key, value))
      :
      Promise.resolve();
  }
  removeItem(key) {
    return this._s
      ? this.isPromise ? this._s.removeItem(key) : Promise.resolve(this._s.removeItem(key))
      :
      Promise.resolve();
  }
  _checkMap(map) {
    let me = this;
    if(map && map.innerVersion && map.innerVersion === me._innerVersion ) {
      return map;
    }
    else {
      return {
        innerVersion: me._innerVersion,
        index: 0,
        __keys__: {}
      };
    }
  }
  _getId(key, id) {
    return key + '_' + id;
  }
  _saveToMap(params) {
    let { key, id, data } = params,
      newId = this._getId(key, id),
      m = this._m;
    if(m[newId] !== undefined) {
      //update existed data
      if(this.enableCache) this.cache[newId] = JSON.parse(data);
      return this.setItem('map_' + m[newId], data);
    }
    if(m[m.index] !== undefined){
      //loop over, delete old data
      let oldId = m[m.index];
      let splitOldId = oldId.split('_')
      delete m[oldId];
      this._removeIdInKey(splitOldId[0], splitOldId[1])
      if(this.enableCache) {
        delete this.cache[oldId];
      }
    }
    m[newId] = m.index;
    m[m.index] = newId;

    m.__keys__[key] = m.__keys__[key] || [];
    m.__keys__[key].push(id);

    if(this.enableCache) {
      const cacheData = JSON.parse(data);
      this.cache[newId] = cacheData;
    }
    let currentIndex = m.index;
    if(++m.index === this._SIZE) {
      m.index = 0;
    }
    this.setItem('map_' + currentIndex, data);
    this.setItem('map', JSON.stringify(m));
  }
  save(params) {
    let me = this;
    let { key, id, rawData, expires } = params;
    if(key.toString().indexOf('_') !== -1) {
      console.error('Please do not use "_" in key!');
    }
    let data = {
      rawData
    };
    let now = new Date().getTime();
    if(expires === undefined) {
      expires = me.defaultExpires;
    }
    if(expires !== null) {
      data.expires = now + expires;
    }
    data = JSON.stringify(data);
    if(id === undefined) {
      if(me.enableCache) {
        const cacheData = JSON.parse(data);
        me.cache[key] = cacheData;
      }
      return me.setItem(key, data);
    }
    else {
      if(id.toString().indexOf('_') !== -1) {
        console.error('Please do not use "_" in id!');
      }
      return this._mapPromise.then(() => me._saveToMap({
        key,
        id,
        data
      }));
    }
  }
  getBatchData(querys) {
    let me = this;
    let tasks = [];
    for(let i = 0, query; query = querys[i]; i++) {
      tasks[i] = me.load(query);
    }
    return Promise.all(tasks);
  }
  getBatchDataWithIds(params) {
    let me = this;
    let { key, ids, syncInBackground } = params;

    return Promise.all(
      ids.map((id) => me.load({ key, id, syncInBackground, autoSync: false, batched: true }))
    ).then((results) => {
      return new Promise((resolve, reject) => {
        const ids = results.filter((value) => value.syncId !== undefined);
        if(!ids.length){
          return resolve();
        }
        return me.sync[key]({
          id: ids.map((value) => value.syncId),
          resolve,
          reject
        });
      }).then((data) => {
        return results.map(value => {
          return value.syncId ? data.shift() : value
        });
      });
    })
  }
  _lookupGlobalItem(params) {
    let me = this,
      ret;
    let { key } = params;
    if(me.enableCache && me.cache[key] !== undefined) {
      ret = me.cache[key];
      return me._loadGlobalItem({ ret, ...params });
    }
    return me.getItem(key).then(ret => me._loadGlobalItem({ ret, ...params }));
  }
  _loadGlobalItem(params) {
    let me = this;
    let { key, ret, autoSync, syncInBackground } = params;
    if(ret === null || ret === undefined) {
      if(autoSync && me.sync[key]) {
        return new Promise((resolve, reject) => me.sync[key]({resolve, reject}));
      }
      return Promise.reject();
    }
    if(typeof ret === 'string') {
      ret = JSON.parse(ret);
    }
    let now = new Date().getTime();
    if(autoSync && ret.expires < now) {
      if (me.sync[key]){
        if(syncInBackground) {
          me.sync[key]({});
          return Promise.resolve(ret.rawData);
        }
        return new Promise((resolve, reject) => me.sync[key]({resolve, reject}));
      }
      return Promise.reject();
    }
    return Promise.resolve(ret.rawData);
  }
  _noItemFound(params) {
    let me = this;
    let { key, id, autoSync } = params;
    if(me.sync[key]) {
      if(autoSync) {
        return new Promise((resolve, reject) => me.sync[key]({id, resolve, reject}));
      }
      return Promise.resolve({ syncId: id });
    }
    return Promise.reject();
  }
  _loadMapItem(params) {
    let me = this;
    let { ret, key, id, autoSync, batched, syncInBackground } = params;
    if(ret === null || ret === undefined) {
      return me._noItemFound(params);
    }
    if(typeof ret === 'string'){
      ret = JSON.parse(ret);
    }
    let now = new Date().getTime();
    if(autoSync && ret.expires < now) {
      if(me.sync[key]) {
        if(syncInBackground){
          me.sync[key]({id});
          return Promise.resolve(ret.rawData);
        }
        return new Promise((resolve, reject) => me.sync[key]({id, resolve, reject}));
      }
      if(batched) {
        return Promise.resolve({ syncId: id });
      }
      return Promise.reject();
    }
    return Promise.resolve(ret.rawData);
  }
  _lookUpInMap(params) {
    let me = this,
      m = me._m,
      ret;
    let { key, id } = params;
    let newId = me._getId(key, id);
    if(me.enableCache && me.cache[newId]) {
      ret = me.cache[newId];
      return me._loadMapItem( {ret, ...params } );
    }
    if(m[newId] !== undefined) {
      return me.getItem('map_' + m[newId]).then( ret => me._loadMapItem( {ret, ...params } ) );
    }
    return me._noItemFound( {ret, ...params } );
  }
  remove(params) {
    return this._mapPromise.then(() => {
      let me = this,
        m = me._m;
      let { key, id } = params;

      if(id === undefined) {
        if(me.enableCache && me.cache[key]) {
          delete me.cache[key];
        }
        return me.removeItem(key);
      }
      let newId = me._getId(key, id);

      //remove existed data
      if(m[newId] !== undefined) {
        if(me.enableCache && me.cache[newId]) {
          delete me.cache[newId];
        }
        me._removeIdInKey(key, id);
        let idTobeDeleted = m[newId];
        delete m[newId];
        me.setItem('map', JSON.stringify(m));
        return me.removeItem('map_' + idTobeDeleted);
      }
    });
  }
  _removeIdInKey(key, id) {
    let indexTobeRemoved = this._m.__keys__[key].indexOf(id);
    if(indexTobeRemoved !== -1) {
      this._m.__keys__[key].splice(indexTobeRemoved, 1);
    }
  }
  load(params) {
    let me = this;
    let { key, id, autoSync, syncInBackground } = params;
    if(autoSync === undefined) {
      autoSync = true;
    }
    if(syncInBackground === undefined) {
      syncInBackground = true;
    }
    return me._mapPromise.then(() => new Promise((resolve, reject) => {
      if(id === undefined) {
        return resolve(me._lookupGlobalItem({key, resolve, reject, autoSync, syncInBackground}));
      }
      return resolve(me._lookUpInMap({key, id, resolve, reject, autoSync, syncInBackground}));
    }));
  }
  clearMap(){
    let me = this;
    me.removeItem('map');
    me._m = {
      innerVersion: me._innerVersion,
      index: 0
    };
  }
  clearMapForKey(key) {
    return this._mapPromise.then(() => {
      let tasks = this._m.__keys__[key].map(id => this.remove({ key, id }));
      return Promise.all(tasks);
    });
  }
  getIdsForKey(key) {
    return this._mapPromise.then(() => {
      return this._m.__keys__[key] || [];
    });
  }
  getAllDataForKey(key, options) {
    options = Object.assign({ syncInBackground: true }, options);
    return this.getIdsForKey(key).then(ids => {
      let querys = ids.map(id => ({ key, id, syncInBackground: options.syncInBackground }));
      return this.getBatchData(querys);
    });
  }

}

// function noop() {}
//
// // compatible with legacy version promise
// function handlePromise (fn) {
//   return new Promise((resolve, reject) => {
//     var promise, isPromise;
//     if (isPromise = (promise = fn((data) => isPromise ? noop() : resolve(data), (err) => isPromise ? noop() : reject(err))) instanceof Promise) resolve(promise);
//   });
// }
<|endoftext|>