<commit_before>'use strict';

import React, { NativeModules, DeviceEventEmitter } from 'react-native';
import { EventEmitter } from 'events';

const RNZeroconf = NativeModules.RNZeroconf;

export default class Zeroconf extends EventEmitter {

  constructor (props) {
    super(props);

    this._services = {};

    DeviceEventEmitter.addListener('RNZeroconfStart', () => {
      this.emit('start');
    });

    DeviceEventEmitter.addListener('RNZeroconfStop', () => {
      this.emit('stop');
    });

    DeviceEventEmitter.addListener('RNZeroconfError', err => {
      this.emit('error', err);
    });

    DeviceEventEmitter.addListener('RNZeroconfFound', service => {
      if (!service) { return; }

      const name = service["name"];
      if (!name) { return; }

      this._services[name] = service;
      this.emit('found', service);
      this.emit('update');
    });

    DeviceEventEmitter.addListener('RNZeroconfRemove', service => {
      if (!service) { return; }

      const name = service["name"];
      if (!name) { return; }

      delete this.services[name];

      this.emit('remove', service);
      this.emit('update');
    });

    DeviceEventEmitter.addListener('RNZeroconfResolved', service => {
      if (!service) { return; }

      const name = service["name"];
      if (!name) { return; }

      this._services[name] = service;
      this.emit('resolved', service);
      this.emit('update');
    });

  }

  /**
   * Get all the services already resolved
   */
  getServices () {
    return this._services;
  }

  /**
   * Scan for Zeroconf services,
   * Defaults to _http._tcp. on local domain
   */
  scan (type = 'http', protocol = 'tcp', domain = 'local.') {
    this._services = {};
    this.emit('update');
    RNZeroconf.scan(type, protocol, domain);
  }

  /**
   * Stop current scan if any
   */
  stop () {
    RNZeroconf.stop();
  }

}
<commit_msg>style(core): better return check<commit_after>'use strict';

import React, { NativeModules, DeviceEventEmitter } from 'react-native';
import { EventEmitter } from 'events';

const RNZeroconf = NativeModules.RNZeroconf;

export default class Zeroconf extends EventEmitter {

  constructor (props) {
    super(props);

    this._services = {};

    DeviceEventEmitter.addListener('RNZeroconfStart', () => {
      this.emit('start');
    });

    DeviceEventEmitter.addListener('RNZeroconfStop', () => {
      this.emit('stop');
    });

    DeviceEventEmitter.addListener('RNZeroconfError', err => {
      this.emit('error', err);
    });

    DeviceEventEmitter.addListener('RNZeroconfFound', service => {
      if (!service || !service.name) { return; }

      this._services[service.name] = service;
      this.emit('found', service);
      this.emit('update');
    });

    DeviceEventEmitter.addListener('RNZeroconfRemove', service => {
      if (!service || !service.name) { return; }

      delete this.services[service.name];

      this.emit('remove', service);
      this.emit('update');
    });

    DeviceEventEmitter.addListener('RNZeroconfResolved', service => {
      if (!service || !service.name) { return; }

      this._services[service.name] = service;
      this.emit('resolved', service);
      this.emit('update');
    });

  }

  /**
   * Get all the services already resolved
   */
  getServices () {
    return this._services;
  }

  /**
   * Scan for Zeroconf services,
   * Defaults to _http._tcp. on local domain
   */
  scan (type = 'http', protocol = 'tcp', domain = 'local.') {
    this._services = {};
    this.emit('update');
    RNZeroconf.scan(type, protocol, domain);
  }

  /**
   * Stop current scan if any
   */
  stop () {
    RNZeroconf.stop();
  }

}
<|endoftext|>


<commit_before>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
* @class WebGLFilterManager
* @constructor
*/
PIXI.WebGLFilterManager = function()
{
    /**
     * @property filterStack
     * @type Array
     */
    this.filterStack = [];

    /**
     * @property offsetX
     * @type Number
     */
    this.offsetX = 0;

    /**
     * @property offsetY
     * @type Number
     */
    this.offsetY = 0;
};

PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager;

/**
* Initialises the context and the properties.
*
* @method setContext
* @param gl {WebGLContext} the current WebGL drawing context
*/
PIXI.WebGLFilterManager.prototype.setContext = function(gl)
{
    this.gl = gl;
    this.texturePool = [];

    this.initShaderBuffers();
};

/**
* @method begin
* @param renderSession {RenderSession}
* @param buffer {ArrayBuffer}
*/
PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer)
{
    this.renderSession = renderSession;
    this.defaultShader = renderSession.shaderManager.defaultShader;

    var projection = this.renderSession.projection;
    this.width = projection.x * 2;
    this.height = -projection.y * 2;
    this.buffer = buffer;
};

/**
* Applies the filter and adds it to the current filter stack.
*
* @method pushFilter
* @param filterBlock {Object} the filter that will be pushed to the current filter stack
*/
PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
{
    var gl = this.gl;

    var projection = this.renderSession.projection;
    var offset = this.renderSession.offset;

    filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds();

    // >>> modify by nextht
    filterBlock._previous_stencil_mgr = this.renderSession.stencilManager;
    this.renderSession.stencilManager = new PIXI.WebGLStencilManager();
    this.renderSession.stencilManager.setContext(gl);
    gl.disable(gl.STENCIL_TEST);
    // <<<  modify by nextht

    // filter program
    // OPTIMISATION - the first filter is free if its a simple color change?
    this.filterStack.push(filterBlock);

    var filter = filterBlock.filterPasses[0];

    this.offsetX += filterBlock._filterArea.x;
    this.offsetY += filterBlock._filterArea.y;

    var texture = this.texturePool.pop();
    if(!texture)
    {
        texture = new PIXI.FilterTexture(this.gl, this.width, this.height);
    }
    else
    {
        texture.resize(this.width, this.height);
    }

    gl.bindTexture(gl.TEXTURE_2D,  texture.texture);

    var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea;

    var padding = filter.padding;
    filterArea.x -= padding;
    filterArea.y -= padding;
    filterArea.width += padding * 2;
    filterArea.height += padding * 2;

    // cap filter to screen size..
    if(filterArea.x < 0)filterArea.x = 0;
    if(filterArea.width > this.width)filterArea.width = this.width;
    if(filterArea.y < 0)filterArea.y = 0;
    if(filterArea.height > this.height)filterArea.height = this.height;

    //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,  filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);

    // set view port
    gl.viewport(0, 0, filterArea.width, filterArea.height);

    projection.x = filterArea.width/2;
    projection.y = -filterArea.height/2;

    offset.x = -filterArea.x;
    offset.y = -filterArea.y;

    // update projection
    // now restore the regular shader..
    // this.renderSession.shaderManager.setShader(this.defaultShader);
    //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
    //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y);

    gl.colorMask(true, true, true, true);
    gl.clearColor(0,0,0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    filterBlock._glFilterTexture = texture;

};

/**
* Removes the last filter from the filter stack and doesn't return it.
*
* @method popFilter
*/
PIXI.WebGLFilterManager.prototype.popFilter = function()
{
    var gl = this.gl;
    var filterBlock = this.filterStack.pop();
    var filterArea = filterBlock._filterArea;
    var texture = filterBlock._glFilterTexture;
    var projection = this.renderSession.projection;
    var offset = this.renderSession.offset;

    if(filterBlock.filterPasses.length > 1)
    {
        gl.viewport(0, 0, filterArea.width, filterArea.height);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

        this.vertexArray[0] = 0;
        this.vertexArray[1] = filterArea.height;

        this.vertexArray[2] = filterArea.width;
        this.vertexArray[3] = filterArea.height;

        this.vertexArray[4] = 0;
        this.vertexArray[5] = 0;

        this.vertexArray[6] = filterArea.width;
        this.vertexArray[7] = 0;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
        // now set the uvs..
        this.uvArray[2] = filterArea.width/this.width;
        this.uvArray[5] = filterArea.height/this.height;
        this.uvArray[6] = filterArea.width/this.width;
        this.uvArray[7] = filterArea.height/this.height;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

        var inputTexture = texture;
        var outputTexture = this.texturePool.pop();
        if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height);
        outputTexture.resize(this.width, this.height);

        // need to clear this FBO as it may have some left over elements from a previous filter.
        gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.disable(gl.BLEND);

        for (var i = 0; i < filterBlock.filterPasses.length-1; i++)
        {
            var filterPass = filterBlock.filterPasses[i];

            gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );

            // set texture
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);

            // draw texture..
            //filterPass.applyFilterPass(filterArea.width, filterArea.height);
            this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);

            // swap the textures..
            var temp = inputTexture;
            inputTexture = outputTexture;
            outputTexture = temp;
        }

        gl.enable(gl.BLEND);

        texture = inputTexture;
        this.texturePool.push(outputTexture);
    }

    var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1];

    this.offsetX -= filterArea.x;
    this.offsetY -= filterArea.y;

    var sizeX = this.width;
    var sizeY = this.height;

    var offsetX = 0;
    var offsetY = 0;

    var buffer = this.buffer;

    // time to render the filters texture to the previous scene
    if(this.filterStack.length === 0)
    {
        gl.colorMask(true, true, true, true);//this.transparent);
    }
    else
    {
        var currentFilter = this.filterStack[this.filterStack.length-1];
        filterArea = currentFilter._filterArea;

        sizeX = filterArea.width;
        sizeY = filterArea.height;

        offsetX = filterArea.x;
        offsetY = filterArea.y;

        buffer =  currentFilter._glFilterTexture.frameBuffer;
    }

    // TODO need to remove these global elements..
    projection.x = sizeX/2;
    projection.y = -sizeY/2;

    offset.x = offsetX;
    offset.y = offsetY;

    filterArea = filterBlock._filterArea;

    var x = filterArea.x-offsetX;
    var y = filterArea.y-offsetY;

    // update the buffers..
    // make sure to flip the y!
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

    this.vertexArray[0] = x;
    this.vertexArray[1] = y + filterArea.height;

    this.vertexArray[2] = x + filterArea.width;
    this.vertexArray[3] = y + filterArea.height;

    this.vertexArray[4] = x;
    this.vertexArray[5] = y;

    this.vertexArray[6] = x + filterArea.width;
    this.vertexArray[7] = y;

    gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);

    this.uvArray[2] = filterArea.width/this.width;
    this.uvArray[5] = filterArea.height/this.height;
    this.uvArray[6] = filterArea.width/this.width;
    this.uvArray[7] = filterArea.height/this.height;

    gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

    gl.viewport(0, 0, sizeX * this.renderSession.resolution, sizeY * this.renderSession.resolution);

    // bind the buffer
    gl.bindFramebuffer(gl.FRAMEBUFFER, buffer );

    // set the blend mode!
    //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)

    // set texture
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture.texture);

    // >>> modify by nextht
    if (this.renderSession.stencilManager) {
        this.renderSession.stencilManager.destroy();
    }
    this.renderSession.stencilManager = filterBlock._previous_stencil_mgr;
    filterBlock._previous_stencil_mgr = null;
    if (this.renderSession.stencilManager.count > 0) {
        gl.enable(gl.STENCIL_TEST);
    }
    else {
        gl.disable(gl.STENCIL_TEST);
    }
    // <<< modify by nextht

    // apply!
    this.applyFilterPass(filter, filterArea, sizeX, sizeY);

    // now restore the regular shader.. should happen automatically now..
    // this.renderSession.shaderManager.setShader(this.defaultShader);
    // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2);
    // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY);

    // return the texture to the pool
    this.texturePool.push(texture);
    filterBlock._glFilterTexture = null;
};

/**
* Applies the filter to the specified area.
*
* @method applyFilterPass
* @param filter {AbstractFilter} the filter that needs to be applied
* @param filterArea {Texture} TODO - might need an update
* @param width {Number} the horizontal range of the filter
* @param height {Number} the vertical range of the filter
*/
PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
{
    // use program
    var gl = this.gl;
    var shader = filter.shaders[gl.id];

    if(!shader)
    {
        shader = new PIXI.PixiShader(gl);

        shader.fragmentSrc = filter.fragmentSrc;
        shader.uniforms = filter.uniforms;
        shader.init();

        filter.shaders[gl.id] = shader;
    }

    // set the shader
    this.renderSession.shaderManager.setShader(shader);

//    gl.useProgram(shader.program);

    gl.uniform2f(shader.projectionVector, width/2, -height/2);
    gl.uniform2f(shader.offsetVector, 0,0);

    if(filter.uniforms.dimensions)
    {
        filter.uniforms.dimensions.value[0] = this.width;//width;
        filter.uniforms.dimensions.value[1] = this.height;//height;
        filter.uniforms.dimensions.value[2] = this.vertexArray[0];
        filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
    }

    shader.syncUniforms();

    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
    gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
    gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

    // draw the filter...
    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );

    this.renderSession.drawCount++;
};

/**
* Initialises the shader buffers.
*
* @method initShaderBuffers
*/
PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
{
    var gl = this.gl;

    // create some buffers
    this.vertexBuffer = gl.createBuffer();
    this.uvBuffer = gl.createBuffer();
    this.colorBuffer = gl.createBuffer();
    this.indexBuffer = gl.createBuffer();

    // bind and upload the vertexs..
    // keep a reference to the vertexFloatData..
    this.vertexArray = new PIXI.Float32Array([0.0, 0.0,
                                         1.0, 0.0,
                                         0.0, 1.0,
                                         1.0, 1.0]);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW);

    // bind and upload the uv buffer
    this.uvArray = new PIXI.Float32Array([0.0, 0.0,
                                     1.0, 0.0,
                                     0.0, 1.0,
                                     1.0, 1.0]);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW);

    this.colorArray = new PIXI.Float32Array([1.0, 0xFFFFFF,
                                        1.0, 0xFFFFFF,
                                        1.0, 0xFFFFFF,
                                        1.0, 0xFFFFFF]);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW);

    // bind and upload the index
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW);

};

/**
* Destroys the filter and removes it from the filter stack.
*
* @method destroy
*/
PIXI.WebGLFilterManager.prototype.destroy = function()
{
    var gl = this.gl;

    this.filterStack = null;

    this.offsetX = 0;
    this.offsetY = 0;

    // destroy textures
    for (var i = 0; i < this.texturePool.length; i++) {
        this.texturePool[i].destroy();
    }

    this.texturePool = null;

    //destroy buffers..
    gl.deleteBuffer(this.vertexBuffer);
    gl.deleteBuffer(this.uvBuffer);
    gl.deleteBuffer(this.colorBuffer);
    gl.deleteBuffer(this.indexBuffer);
};
<commit_msg>Fix #2320: properly size filter texture and viewport<commit_after>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
* @class WebGLFilterManager
* @constructor
*/
PIXI.WebGLFilterManager = function()
{
    /**
     * @property filterStack
     * @type Array
     */
    this.filterStack = [];

    /**
     * @property offsetX
     * @type Number
     */
    this.offsetX = 0;

    /**
     * @property offsetY
     * @type Number
     */
    this.offsetY = 0;
};

PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager;

/**
* Initialises the context and the properties.
*
* @method setContext
* @param gl {WebGLContext} the current WebGL drawing context
*/
PIXI.WebGLFilterManager.prototype.setContext = function(gl)
{
    this.gl = gl;
    this.texturePool = [];

    this.initShaderBuffers();
};

/**
* @method begin
* @param renderSession {RenderSession}
* @param buffer {ArrayBuffer}
*/
PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer)
{
    this.renderSession = renderSession;
    this.defaultShader = renderSession.shaderManager.defaultShader;

    var projection = this.renderSession.projection;
    this.width = projection.x * 2;
    this.height = -projection.y * 2;
    this.buffer = buffer;
};

/**
* Applies the filter and adds it to the current filter stack.
*
* @method pushFilter
* @param filterBlock {Object} the filter that will be pushed to the current filter stack
*/
PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
{
    var gl = this.gl;

    var projection = this.renderSession.projection;
    var offset = this.renderSession.offset;

    filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds();

    // >>> modify by nextht
    filterBlock._previous_stencil_mgr = this.renderSession.stencilManager;
    this.renderSession.stencilManager = new PIXI.WebGLStencilManager();
    this.renderSession.stencilManager.setContext(gl);
    gl.disable(gl.STENCIL_TEST);
    // <<<  modify by nextht

    // filter program
    // OPTIMISATION - the first filter is free if its a simple color change?
    this.filterStack.push(filterBlock);

    var filter = filterBlock.filterPasses[0];

    this.offsetX += filterBlock._filterArea.x;
    this.offsetY += filterBlock._filterArea.y;

    var texture = this.texturePool.pop();
    if(!texture)
    {
        texture = new PIXI.FilterTexture(this.gl, this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
    }
    else
    {
        texture.resize(this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
    }

    gl.bindTexture(gl.TEXTURE_2D,  texture.texture);

    var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea;

    var padding = filter.padding;
    filterArea.x -= padding;
    filterArea.y -= padding;
    filterArea.width += padding * 2;
    filterArea.height += padding * 2;

    // cap filter to screen size..
    if(filterArea.x < 0)filterArea.x = 0;
    if(filterArea.width > this.width)filterArea.width = this.width;
    if(filterArea.y < 0)filterArea.y = 0;
    if(filterArea.height > this.height)filterArea.height = this.height;

    //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,  filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);

    // set view port
    gl.viewport(0, 0, filterArea.width * this.renderSession.resolution, filterArea.height * this.renderSession.resolution);

    projection.x = filterArea.width/2;
    projection.y = -filterArea.height/2;

    offset.x = -filterArea.x;
    offset.y = -filterArea.y;

    // update projection
    // now restore the regular shader..
    // this.renderSession.shaderManager.setShader(this.defaultShader);
    //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
    //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y);

    gl.colorMask(true, true, true, true);
    gl.clearColor(0,0,0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    filterBlock._glFilterTexture = texture;

};

/**
* Removes the last filter from the filter stack and doesn't return it.
*
* @method popFilter
*/
PIXI.WebGLFilterManager.prototype.popFilter = function()
{
    var gl = this.gl;
    var filterBlock = this.filterStack.pop();
    var filterArea = filterBlock._filterArea;
    var texture = filterBlock._glFilterTexture;
    var projection = this.renderSession.projection;
    var offset = this.renderSession.offset;

    if(filterBlock.filterPasses.length > 1)
    {
        gl.viewport(0, 0, filterArea.width, filterArea.height);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

        this.vertexArray[0] = 0;
        this.vertexArray[1] = filterArea.height;

        this.vertexArray[2] = filterArea.width;
        this.vertexArray[3] = filterArea.height;

        this.vertexArray[4] = 0;
        this.vertexArray[5] = 0;

        this.vertexArray[6] = filterArea.width;
        this.vertexArray[7] = 0;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
        // now set the uvs..
        this.uvArray[2] = filterArea.width/this.width;
        this.uvArray[5] = filterArea.height/this.height;
        this.uvArray[6] = filterArea.width/this.width;
        this.uvArray[7] = filterArea.height/this.height;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

        var inputTexture = texture;
        var outputTexture = this.texturePool.pop();
        if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height);
        outputTexture.resize(this.width, this.height);

        // need to clear this FBO as it may have some left over elements from a previous filter.
        gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.disable(gl.BLEND);

        for (var i = 0; i < filterBlock.filterPasses.length-1; i++)
        {
            var filterPass = filterBlock.filterPasses[i];

            gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );

            // set texture
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);

            // draw texture..
            //filterPass.applyFilterPass(filterArea.width, filterArea.height);
            this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);

            // swap the textures..
            var temp = inputTexture;
            inputTexture = outputTexture;
            outputTexture = temp;
        }

        gl.enable(gl.BLEND);

        texture = inputTexture;
        this.texturePool.push(outputTexture);
    }

    var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1];

    this.offsetX -= filterArea.x;
    this.offsetY -= filterArea.y;

    var sizeX = this.width;
    var sizeY = this.height;

    var offsetX = 0;
    var offsetY = 0;

    var buffer = this.buffer;

    // time to render the filters texture to the previous scene
    if(this.filterStack.length === 0)
    {
        gl.colorMask(true, true, true, true);//this.transparent);
    }
    else
    {
        var currentFilter = this.filterStack[this.filterStack.length-1];
        filterArea = currentFilter._filterArea;

        sizeX = filterArea.width;
        sizeY = filterArea.height;

        offsetX = filterArea.x;
        offsetY = filterArea.y;

        buffer =  currentFilter._glFilterTexture.frameBuffer;
    }

    // TODO need to remove these global elements..
    projection.x = sizeX/2;
    projection.y = -sizeY/2;

    offset.x = offsetX;
    offset.y = offsetY;

    filterArea = filterBlock._filterArea;

    var x = filterArea.x-offsetX;
    var y = filterArea.y-offsetY;

    // update the buffers..
    // make sure to flip the y!
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

    this.vertexArray[0] = x;
    this.vertexArray[1] = y + filterArea.height;

    this.vertexArray[2] = x + filterArea.width;
    this.vertexArray[3] = y + filterArea.height;

    this.vertexArray[4] = x;
    this.vertexArray[5] = y;

    this.vertexArray[6] = x + filterArea.width;
    this.vertexArray[7] = y;

    gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);

    this.uvArray[2] = filterArea.width/this.width;
    this.uvArray[5] = filterArea.height/this.height;
    this.uvArray[6] = filterArea.width/this.width;
    this.uvArray[7] = filterArea.height/this.height;

    gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

    gl.viewport(0, 0, sizeX * this.renderSession.resolution, sizeY * this.renderSession.resolution);

    // bind the buffer
    gl.bindFramebuffer(gl.FRAMEBUFFER, buffer );

    // set the blend mode!
    //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)

    // set texture
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture.texture);

    // >>> modify by nextht
    if (this.renderSession.stencilManager) {
        this.renderSession.stencilManager.destroy();
    }
    this.renderSession.stencilManager = filterBlock._previous_stencil_mgr;
    filterBlock._previous_stencil_mgr = null;
    if (this.renderSession.stencilManager.count > 0) {
        gl.enable(gl.STENCIL_TEST);
    }
    else {
        gl.disable(gl.STENCIL_TEST);
    }
    // <<< modify by nextht

    // apply!
    this.applyFilterPass(filter, filterArea, sizeX, sizeY);

    // now restore the regular shader.. should happen automatically now..
    // this.renderSession.shaderManager.setShader(this.defaultShader);
    // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2);
    // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY);

    // return the texture to the pool
    this.texturePool.push(texture);
    filterBlock._glFilterTexture = null;
};

/**
* Applies the filter to the specified area.
*
* @method applyFilterPass
* @param filter {AbstractFilter} the filter that needs to be applied
* @param filterArea {Texture} TODO - might need an update
* @param width {Number} the horizontal range of the filter
* @param height {Number} the vertical range of the filter
*/
PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
{
    // use program
    var gl = this.gl;
    var shader = filter.shaders[gl.id];

    if(!shader)
    {
        shader = new PIXI.PixiShader(gl);

        shader.fragmentSrc = filter.fragmentSrc;
        shader.uniforms = filter.uniforms;
        shader.init();

        filter.shaders[gl.id] = shader;
    }

    // set the shader
    this.renderSession.shaderManager.setShader(shader);

//    gl.useProgram(shader.program);

    gl.uniform2f(shader.projectionVector, width/2, -height/2);
    gl.uniform2f(shader.offsetVector, 0,0);

    if(filter.uniforms.dimensions)
    {
        filter.uniforms.dimensions.value[0] = this.width;//width;
        filter.uniforms.dimensions.value[1] = this.height;//height;
        filter.uniforms.dimensions.value[2] = this.vertexArray[0];
        filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
    }

    shader.syncUniforms();

    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
    gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
    gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

    // draw the filter...
    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );

    this.renderSession.drawCount++;
};

/**
* Initialises the shader buffers.
*
* @method initShaderBuffers
*/
PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
{
    var gl = this.gl;

    // create some buffers
    this.vertexBuffer = gl.createBuffer();
    this.uvBuffer = gl.createBuffer();
    this.colorBuffer = gl.createBuffer();
    this.indexBuffer = gl.createBuffer();

    // bind and upload the vertexs..
    // keep a reference to the vertexFloatData..
    this.vertexArray = new PIXI.Float32Array([0.0, 0.0,
                                         1.0, 0.0,
                                         0.0, 1.0,
                                         1.0, 1.0]);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW);

    // bind and upload the uv buffer
    this.uvArray = new PIXI.Float32Array([0.0, 0.0,
                                     1.0, 0.0,
                                     0.0, 1.0,
                                     1.0, 1.0]);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW);

    this.colorArray = new PIXI.Float32Array([1.0, 0xFFFFFF,
                                        1.0, 0xFFFFFF,
                                        1.0, 0xFFFFFF,
                                        1.0, 0xFFFFFF]);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW);

    // bind and upload the index
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW);

};

/**
* Destroys the filter and removes it from the filter stack.
*
* @method destroy
*/
PIXI.WebGLFilterManager.prototype.destroy = function()
{
    var gl = this.gl;

    this.filterStack = null;

    this.offsetX = 0;
    this.offsetY = 0;

    // destroy textures
    for (var i = 0; i < this.texturePool.length; i++) {
        this.texturePool[i].destroy();
    }

    this.texturePool = null;

    //destroy buffers..
    gl.deleteBuffer(this.vertexBuffer);
    gl.deleteBuffer(this.uvBuffer);
    gl.deleteBuffer(this.colorBuffer);
    gl.deleteBuffer(this.indexBuffer);
};
<|endoftext|>


<commit_before>import {List} from 'immutable';

import React from 'react';
import { connect } from 'react-redux';

import page from 'page';

import StackChart from '../../../../shared/js/components/StackChart';

import PageTitle from '../../../../shared/js/components/gironde.fr/PageTitle';
import SecundaryTitle from '../../../../shared/js/components/gironde.fr/SecundaryTitle';
import PrimaryCallToAction from '../../../../shared/js/components/gironde.fr/PrimaryCallToAction';
import Markdown from '../../../../shared/js/components/Markdown';
import {makeAmountString} from '../../../../shared/js/components/MoneyAmount';

import FocusDetail from '../FocusDetail';
import FocusDonut from '../FocusDonut';

import {m52ToAggregated, hierarchicalAggregated} from '../../../../shared/js/finance/memoized';
import {flattenTree} from '../../../../shared/js/finance/visitHierarchical';
import {EXPENDITURES, DI} from '../../../../shared/js/finance/constants';

import {makePartition, makeElementById} from './FinanceElement';

import {urls, COLLEGE_PICTO, ENVIRONNEMENT_AMENAGEMENT_PICTO, PATRIMOINE_PICTO, ROUTES_PICTO, SOUTIEN_COMMUNES_PICTO} from '../../constants/resources';

import colorClassById from '../../colorClassById';

export function FocusSol({
    year, yearInvestments, partitionByYear, population, yearDIDetails, screenWidth, urls
}) {
    const investmentProportion = yearInvestments && yearInvestments.investments/yearInvestments.expenditures;

    const years = partitionByYear.keySeq().toJS();

    // sort all partitions part according to the order in this year's partition
    let thisYearPartition = partitionByYear.get(year);

    const focusDetailsDenominator = yearDIDetails ? yearDIDetails['DI-1'] + yearDIDetails['DI-2'] : NaN;

    return React.createElement('article', {className: 'focus'},
        React.createElement('section', {},
            React.createElement(PageTitle, {text: `Focus Investissements`}),
            React.createElement(Markdown, {},
                `Le Département investit en moyenne 200 millions d’euros chaque année pour réaménager les routes, construire de nouveaux collèges, entretenir des espaces naturels sensibles, etc.`
            )
        ),
        React.createElement('section', {className: 'top-infos'},
            React.createElement(FocusDonut, {
                proportion: investmentProportion,
                outerRadius:  screenWidth < 400 ? (screenWidth/2 - 20) : 188,
                innerText: [
                    `de dépenses d'Investissements`,
                    `sur l'ensemble des dépenses`
                ]
            }),
            React.createElement('div', {},
                React.createElement(Markdown, {}, `**En ${year}, le Département de la Gironde a investi ${yearInvestments && makeAmountString(yearInvestments.investments)} soit ${(investmentProportion*100).toFixed(1)}% de la totalité des dépenses. Cela représente une augmentation de +3.2% comparé à 2015. Ce sont les 1,5 Millions d’habitants en Gironde qui bénéficient directement de ces investissements avec par exemple le réaménagement des routes, la construction de collèges ou encore à l’entretien d’espaces naturels.**`),
                React.createElement(PrimaryCallToAction, {href: '#!/finance-details/DI', text: `en savoir plus`})
            ),
            React.createElement('div', {className: 'people-fraction'},
                React.createElement('div', {},
                    React.createElement('div', {}, `Le Département a investi`),
                    React.createElement('div', {className: 'number'}, yearInvestments && (yearInvestments.investments/population).toFixed(2).replace('.', ',')),
                    React.createElement('div', {}, `euros par habitant en 2016`)
                )
            )
        ),
        React.createElement('section', {},
            React.createElement(SecundaryTitle, {text: 'Évolution des dépenses d’investissements de 2012 à 2016'}),
            React.createElement(StackChart, {
                WIDTH: screenWidth >= 800 + 80 ?
                    800 :
                    (screenWidth - 85 >= 600 ? screenWidth - 85 : (
                        screenWidth <= 600 ? screenWidth - 10 : 600
                    )),
                portrait: screenWidth <= 600,
                xs: years,
                ysByX: partitionByYear.map(partition => partition.map(part => part.partAmount)),
                onBrickClicked: (year, id) => { page(`#!/finance-details/${id}`); },
                legendItems: thisYearPartition && thisYearPartition.map(p => ({
                    id: p.contentId,
                    className: p.contentId,
                    url: p.url,
                    text: p.texts.label,
                    // .slice to keep 'DI-1' from 'DI-1-4'
                    colorClassName: colorClassById.get(p.contentId.slice(0, 4))
                })).toArray(),
                yValueDisplay: makeAmountString
            }),
            React.createElement(PrimaryCallToAction, {href: '#!/finance-details/DI', text: `en savoir plus`})
        ),
        React.createElement('section', {},
            React.createElement(SecundaryTitle, {text: `Les secteurs d’investissement`}),
            React.createElement(Markdown, {}, `Le Département ne peut investir que dans les domaines liés à ses compétences. Ses investissements portent par exemple sur la construction, l’entretien et la rénovation des collèges, l'entretien du réseau routier départemental, de son patrimoine immobilier, des espaces naturels sensibles. Il verse également des subventions aux communes pour soutenir leur propre politique d’investissement.`),
            React.createElement(FocusDetail, {
                className: 'colleges',
                title: 'Les Collèges',
                illustrationUrl: urls[COLLEGE_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-1'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-1']/focusDetailsDenominator : 1,
                text: `Le Département construit, entretient, rénove l’ensemble du parc des 105 collèges publics. A la rentrée scolaire 2017-2018, on comptait 58 226 collégiens dans les collèges publics girondins et 12 835 collégiens dans les collèges privés. En 2017, seront notamment livrés les restructurations des collèges Alfred Mauguin à Gradignan et Claude Massé à Ambarès-et-Lagrave, la salle de sport du Collège Marguerite Duras à Libourne, l’extension de la salle de restauration et des sanitaires au sein du Collège Georges Brassens à Podensac.`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-1'
            }),
            React.createElement(FocusDetail, {
                className: 'roads',
                title: 'Infrastructures et routes',
                illustrationUrl: urls[ROUTES_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-2'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-2']/focusDetailsDenominator : 1,
                text: `Le réseau routier girondin est constitué de 6 500 kilomètres de routes départementales, dont 350 kilomètres de pistes cyclables et 1 900 ouvrages d’art (ponts et murs de soutènement). En 2017, la mise en sécurité du réseau se traduira notamment par les réfections de la RD9 à Aillas Mitton, de la RD18 à Génissac Moulon Grézillac, de la RD3 à Hourtin Lesparre, la fin des travaux du pont Eiffel, mais aussi des aménagements de sécurité entre Biganos et Arès, etc.  Le Plan départemental de déplacement traduit la volonté du Département d’élargir son action en matière d’organisation du système de déplacements (infrastructures routières, transports collectifs départementaux et covoiturage). Ce Plan prévoit un budget total d’investissement de 14 millions d’euros entre 2017 et 2030.`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-2'
            }),
            React.createElement(FocusDetail, {
                className: 'buildings',
                title: 'Patrimoine et Batiments',
                illustrationUrl: urls[PATRIMOINE_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-3'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-3']/focusDetailsDenominator : 1,
                text: `Avec 425 sites de travail et lieux d’accueil des publics répartis sur la Gironde, le Département doit entretenir, rénover ou construire près de 1 000 bâtiments : Maison départementale de la solidarité et de l’insertion, Maison des adolescents, Archives départementales, Hôtel du Département à Bordeaux,etc.`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-3'
            }),
            React.createElement(FocusDetail, {
                className: 'environment',
                title: 'Environnement et aménagement',
                illustrationUrl: urls[ENVIRONNEMENT_AMENAGEMENT_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-4'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-4']/focusDetailsDenominator : 1,
                text: `Le Département agit pour la protection et la valorisation des espaces naturels sensibles. Il assure leur gestion, organise des actions de sensibilisation à l'environnement, et permet au public de visiter ces sites préservés en visites libres, ou guidées par des guides naturalistes. 2017 marque la fin des travaux du pôle régional de connaissance de préservation et de valorisation de la biodiversité au domaine de Certes et Graveyron à Audenge. Celui-ci accueille dans le cadre de la préservation de la faune, de la flore et des habitants, le Conservatoire botanique Sud-Atlantique, un centre de sauvegarde de la Ligue pour la Protection des Oiseaux (LPO) et un centre de conservation et d’étude (CCE) de la Direction Régionale des Affaires Culturelles (DRAC).`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-4'
            }),
            React.createElement(FocusDetail, {
                className: 'city-subsidy',
                title: 'Subventions',
                illustrationUrl: urls[SOUTIEN_COMMUNES_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-2'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-2']/focusDetailsDenominator : 1,
                text: `Le Département est le chef de file des solidarités humaines et territoriales. Il est l’interlocuteur privilégié des collectivités dans le domaine de l’ingénierie territoriale. Dans un contexte économique de plus en plus tendu et face à une pression démographique forte, les communes connaissent des situations économiques très contrastées qui rendent difficile la réalisation de projets pourtant nécessaires. Face à cette situation, le Département les accompagne(offre d’ingénierie avec Gironde Ressources et subventions) pour un développement harmonieux et équilibré du territoire.`,
                highlights: [
                    {
                        strong: "25,1 M€",
                        span: "subventions aux communes"
                    },
                    {
                        strong: "7,9 M€",
                        span: "subvention au logement social"
                    },
                    {
                        strong: "6,8 M€",
                        span: "subventions pour la fibre optique"
                    }
                ],
                moreUrl: '#!/finance-details/DI-2'
            })
        )
    );
}

export default connect(
    state => {
        const { m52InstructionByYear, corrections, currentYear, textsById, screenWidth} = state;

        const investmentsByYear = m52InstructionByYear.map( ((instruction) => {
            const agg = m52ToAggregated(instruction, corrections);

            const hierAgg = hierarchicalAggregated(agg);

            const hierAggByPrestationList = flattenTree(hierAgg);

            const expenditures = hierAggByPrestationList.find(e => e.id === EXPENDITURES).total;
            let investments = hierAggByPrestationList.find(e => e.id === 'DI-1').total +
                hierAggByPrestationList.find(e => e.id === 'DI-2').total;

            return {
                expenditures,
                investments
            }

        }))

        // code adapted from FinanceElement mapStateToProps
        const displayedContentId = DI;

        const partitionByYear = m52InstructionByYear.map(m52i => {
            const elementById = makeElementById(
                hierarchicalAggregated(m52ToAggregated(m52i, corrections))
            );

            const yearElement = elementById.get(displayedContentId);

            return yearElement && yearElement.children && new List(
                ['DI-1-1', 'DI-1-2', 'DI-1-3', 'DI-1-4', 'DI-2']
                .map(id => ({
                    contentId: id,
                    partAmount: elementById.get(id).total,
                    texts: textsById.get(id),
                    url: `#!/finance-details/${id}`
                }))
            )
        });

        /*
        List(children)
        .map(child => ({
            contentId: child.id,
            partAmount: totalById.get(child.id),
            texts: textsById.get(child.id),
            url: `#!/finance-details/${child.id}`
        }))
        */

        // DI details
        const elementById = m52InstructionByYear.get(currentYear) ? makeElementById(
            hierarchicalAggregated(m52ToAggregated(m52InstructionByYear.get(currentYear), corrections))
        ) : undefined;

        const yearDIDetails = elementById ? {
            'DI-1': elementById.get('DI-1').total,
            'DI-2': elementById.get('DI-2').total,
            'DI-1-1': elementById.get('DI-1-1').total,
            'DI-1-2': elementById.get('DI-1-2').total,
            'DI-1-3': elementById.get('DI-1-3').total,
            'DI-1-4': elementById.get('DI-1-4').total,
            'DI-2-1': elementById.get('DI-2-1').total,
        } : undefined;

        return {
            year: currentYear,
            yearDIDetails,
            yearInvestments: investmentsByYear.get(currentYear),
            partitionByYear,
            population: 1505517, // source : https://www.gironde.fr/le-departement
            screenWidth,
            urls
        };
    },
    () => ({})
)(FocusSol);
<commit_msg>ajout d'un mot<commit_after>import {List} from 'immutable';

import React from 'react';
import { connect } from 'react-redux';

import page from 'page';

import StackChart from '../../../../shared/js/components/StackChart';

import PageTitle from '../../../../shared/js/components/gironde.fr/PageTitle';
import SecundaryTitle from '../../../../shared/js/components/gironde.fr/SecundaryTitle';
import PrimaryCallToAction from '../../../../shared/js/components/gironde.fr/PrimaryCallToAction';
import Markdown from '../../../../shared/js/components/Markdown';
import {makeAmountString} from '../../../../shared/js/components/MoneyAmount';

import FocusDetail from '../FocusDetail';
import FocusDonut from '../FocusDonut';

import {m52ToAggregated, hierarchicalAggregated} from '../../../../shared/js/finance/memoized';
import {flattenTree} from '../../../../shared/js/finance/visitHierarchical';
import {EXPENDITURES, DI} from '../../../../shared/js/finance/constants';

import {makePartition, makeElementById} from './FinanceElement';

import {urls, COLLEGE_PICTO, ENVIRONNEMENT_AMENAGEMENT_PICTO, PATRIMOINE_PICTO, ROUTES_PICTO, SOUTIEN_COMMUNES_PICTO} from '../../constants/resources';

import colorClassById from '../../colorClassById';

export function FocusSol({
    year, yearInvestments, partitionByYear, population, yearDIDetails, screenWidth, urls
}) {
    const investmentProportion = yearInvestments && yearInvestments.investments/yearInvestments.expenditures;

    const years = partitionByYear.keySeq().toJS();

    // sort all partitions part according to the order in this year's partition
    let thisYearPartition = partitionByYear.get(year);

    const focusDetailsDenominator = yearDIDetails ? yearDIDetails['DI-1'] + yearDIDetails['DI-2'] : NaN;

    return React.createElement('article', {className: 'focus'},
        React.createElement('section', {},
            React.createElement(PageTitle, {text: `Focus Investissements`}),
            React.createElement(Markdown, {},
                `Le Département investit en moyenne 200 millions d’euros chaque année pour réaménager les routes, construire de nouveaux collèges, entretenir des espaces naturels sensibles, etc.`
            )
        ),
        React.createElement('section', {className: 'top-infos'},
            React.createElement(FocusDonut, {
                proportion: investmentProportion,
                outerRadius:  screenWidth < 400 ? (screenWidth/2 - 20) : 188,
                innerText: [
                    `de dépenses d'Investissements`,
                    `sur l'ensemble des dépenses`
                ]
            }),
            React.createElement('div', {},
                React.createElement(Markdown, {}, `**En ${year}, le Département de la Gironde a investi ${yearInvestments && makeAmountString(yearInvestments.investments)} soit ${(investmentProportion*100).toFixed(1)}% de la totalité des dépenses. Cela représente une augmentation de +3.2% comparé à 2015. Ce sont les 1,5 Millions d’habitants en Gironde qui bénéficient directement de ces investissements avec par exemple le réaménagement des routes, la construction de collèges ou encore à l’entretien d’espaces naturels.**`),
                React.createElement(PrimaryCallToAction, {href: '#!/finance-details/DI', text: `en savoir plus`})
            ),
            React.createElement('div', {className: 'people-fraction'},
                React.createElement('div', {},
                    React.createElement('div', {}, `Le Département a investi`),
                    React.createElement('div', {className: 'number'}, yearInvestments && (yearInvestments.investments/population).toFixed(2).replace('.', ',')),
                    React.createElement('div', {}, `euros par habitant en 2016`)
                )
            )
        ),
        React.createElement('section', {},
            React.createElement(SecundaryTitle, {text: 'Évolution des dépenses d’investissements de 2012 à 2016'}),
            React.createElement(StackChart, {
                WIDTH: screenWidth >= 800 + 80 ?
                    800 :
                    (screenWidth - 85 >= 600 ? screenWidth - 85 : (
                        screenWidth <= 600 ? screenWidth - 10 : 600
                    )),
                portrait: screenWidth <= 600,
                xs: years,
                ysByX: partitionByYear.map(partition => partition.map(part => part.partAmount)),
                onBrickClicked: (year, id) => { page(`#!/finance-details/${id}`); },
                legendItems: thisYearPartition && thisYearPartition.map(p => ({
                    id: p.contentId,
                    className: p.contentId,
                    url: p.url,
                    text: p.texts.label,
                    // .slice to keep 'DI-1' from 'DI-1-4'
                    colorClassName: colorClassById.get(p.contentId.slice(0, 4))
                })).toArray(),
                yValueDisplay: makeAmountString
            }),
            React.createElement(PrimaryCallToAction, {href: '#!/finance-details/DI', text: `en savoir plus`})
        ),
        React.createElement('section', {},
            React.createElement(SecundaryTitle, {text: `Les secteurs d’investissement`}),
            React.createElement(Markdown, {}, `Le Département ne peut investir que dans les domaines liés à ses compétences. Ses investissements portent par exemple sur la construction, l’entretien et la rénovation des collèges, l'entretien du réseau routier départemental, de son patrimoine immobilier, des espaces naturels sensibles. Il verse également des subventions notamment aux communes pour soutenir leur propre politique d’investissement.`),
            React.createElement(FocusDetail, {
                className: 'colleges',
                title: 'Les Collèges',
                illustrationUrl: urls[COLLEGE_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-1'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-1']/focusDetailsDenominator : 1,
                text: `Le Département construit, entretient, rénove l’ensemble du parc des 105 collèges publics. A la rentrée scolaire 2017-2018, on comptait 58 226 collégiens dans les collèges publics girondins et 12 835 collégiens dans les collèges privés. En 2017, seront notamment livrés les restructurations des collèges Alfred Mauguin à Gradignan et Claude Massé à Ambarès-et-Lagrave, la salle de sport du Collège Marguerite Duras à Libourne, l’extension de la salle de restauration et des sanitaires au sein du Collège Georges Brassens à Podensac.`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-1'
            }),
            React.createElement(FocusDetail, {
                className: 'roads',
                title: 'Infrastructures et routes',
                illustrationUrl: urls[ROUTES_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-2'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-2']/focusDetailsDenominator : 1,
                text: `Le réseau routier girondin est constitué de 6 500 kilomètres de routes départementales, dont 350 kilomètres de pistes cyclables et 1 900 ouvrages d’art (ponts et murs de soutènement). En 2017, la mise en sécurité du réseau se traduira notamment par les réfections de la RD9 à Aillas Mitton, de la RD18 à Génissac Moulon Grézillac, de la RD3 à Hourtin Lesparre, la fin des travaux du pont Eiffel, mais aussi des aménagements de sécurité entre Biganos et Arès, etc.  Le Plan départemental de déplacement traduit la volonté du Département d’élargir son action en matière d’organisation du système de déplacements (infrastructures routières, transports collectifs départementaux et covoiturage). Ce Plan prévoit un budget total d’investissement de 14 millions d’euros entre 2017 et 2030.`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-2'
            }),
            React.createElement(FocusDetail, {
                className: 'buildings',
                title: 'Patrimoine et Batiments',
                illustrationUrl: urls[PATRIMOINE_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-3'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-3']/focusDetailsDenominator : 1,
                text: `Avec 425 sites de travail et lieux d’accueil des publics répartis sur la Gironde, le Département doit entretenir, rénover ou construire près de 1 000 bâtiments : Maison départementale de la solidarité et de l’insertion, Maison des adolescents, Archives départementales, Hôtel du Département à Bordeaux,etc.`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-3'
            }),
            React.createElement(FocusDetail, {
                className: 'environment',
                title: 'Environnement et aménagement',
                illustrationUrl: urls[ENVIRONNEMENT_AMENAGEMENT_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-1-4'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-1-4']/focusDetailsDenominator : 1,
                text: `Le Département agit pour la protection et la valorisation des espaces naturels sensibles. Il assure leur gestion, organise des actions de sensibilisation à l'environnement, et permet au public de visiter ces sites préservés en visites libres, ou guidées par des guides naturalistes. 2017 marque la fin des travaux du pôle régional de connaissance de préservation et de valorisation de la biodiversité au domaine de Certes et Graveyron à Audenge. Celui-ci accueille dans le cadre de la préservation de la faune, de la flore et des habitants, le Conservatoire botanique Sud-Atlantique, un centre de sauvegarde de la Ligue pour la Protection des Oiseaux (LPO) et un centre de conservation et d’étude (CCE) de la Direction Régionale des Affaires Culturelles (DRAC).`,
                highlights: [
                    /*{
                        strong: "",
                        span: ""
                    },
                    {
                        strong: "",
                        span: ""
                    }*/
                ],
                moreUrl: '#!/finance-details/DI-1-4'
            }),
            React.createElement(FocusDetail, {
                className: 'city-subsidy',
                title: 'Subventions',
                illustrationUrl: urls[SOUTIEN_COMMUNES_PICTO],
                amount: yearDIDetails ? yearDIDetails['DI-2'] : undefined,
                proportion: yearDIDetails ? yearDIDetails['DI-2']/focusDetailsDenominator : 1,
                text: `Le Département est le chef de file des solidarités humaines et territoriales. Il est l’interlocuteur privilégié des collectivités dans le domaine de l’ingénierie territoriale. Dans un contexte économique de plus en plus tendu et face à une pression démographique forte, les communes connaissent des situations économiques très contrastées qui rendent difficile la réalisation de projets pourtant nécessaires. Face à cette situation, le Département les accompagne(offre d’ingénierie avec Gironde Ressources et subventions) pour un développement harmonieux et équilibré du territoire.`,
                highlights: [
                    {
                        strong: "25,1 M€",
                        span: "subventions aux communes"
                    },
                    {
                        strong: "7,9 M€",
                        span: "subvention au logement social"
                    },
                    {
                        strong: "6,8 M€",
                        span: "subventions pour la fibre optique"
                    }
                ],
                moreUrl: '#!/finance-details/DI-2'
            })
        )
    );
}

export default connect(
    state => {
        const { m52InstructionByYear, corrections, currentYear, textsById, screenWidth} = state;

        const investmentsByYear = m52InstructionByYear.map( ((instruction) => {
            const agg = m52ToAggregated(instruction, corrections);

            const hierAgg = hierarchicalAggregated(agg);

            const hierAggByPrestationList = flattenTree(hierAgg);

            const expenditures = hierAggByPrestationList.find(e => e.id === EXPENDITURES).total;
            let investments = hierAggByPrestationList.find(e => e.id === 'DI-1').total +
                hierAggByPrestationList.find(e => e.id === 'DI-2').total;

            return {
                expenditures,
                investments
            }

        }))

        // code adapted from FinanceElement mapStateToProps
        const displayedContentId = DI;

        const partitionByYear = m52InstructionByYear.map(m52i => {
            const elementById = makeElementById(
                hierarchicalAggregated(m52ToAggregated(m52i, corrections))
            );

            const yearElement = elementById.get(displayedContentId);

            return yearElement && yearElement.children && new List(
                ['DI-1-1', 'DI-1-2', 'DI-1-3', 'DI-1-4', 'DI-2']
                .map(id => ({
                    contentId: id,
                    partAmount: elementById.get(id).total,
                    texts: textsById.get(id),
                    url: `#!/finance-details/${id}`
                }))
            )
        });

        /*
        List(children)
        .map(child => ({
            contentId: child.id,
            partAmount: totalById.get(child.id),
            texts: textsById.get(child.id),
            url: `#!/finance-details/${child.id}`
        }))
        */

        // DI details
        const elementById = m52InstructionByYear.get(currentYear) ? makeElementById(
            hierarchicalAggregated(m52ToAggregated(m52InstructionByYear.get(currentYear), corrections))
        ) : undefined;

        const yearDIDetails = elementById ? {
            'DI-1': elementById.get('DI-1').total,
            'DI-2': elementById.get('DI-2').total,
            'DI-1-1': elementById.get('DI-1-1').total,
            'DI-1-2': elementById.get('DI-1-2').total,
            'DI-1-3': elementById.get('DI-1-3').total,
            'DI-1-4': elementById.get('DI-1-4').total,
            'DI-2-1': elementById.get('DI-2-1').total,
        } : undefined;

        return {
            year: currentYear,
            yearDIDetails,
            yearInvestments: investmentsByYear.get(currentYear),
            partitionByYear,
            population: 1505517, // source : https://www.gironde.fr/le-departement
            screenWidth,
            urls
        };
    },
    () => ({})
)(FocusSol);
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */
sap.ui.define([
  'jquery.sap.global',
  './Matcher',
  './I18NText',
  'sap/ui/test/Opa5'
], function (jQuery, Matcher, I18NText, Opa5) {
  "use strict";

  var oI18nMatcher = new I18NText();

  /**
   * @class
   * The LabelFor matcher searches for given control associated with labelFor property.
   * The matcher does automatically
   *

   *

   *         retrieve control associated by label by given text
   *

   *

   *         retrieve control associated by label by given i18n key, modelName, parameters or propertyName. See {@link sap.ui.test.matchers.I18NText}
   *

   *

   *         combination of text and key is not possible
   *     <\li>
   *

   *
   * @extends sap.ui.test.matchers.Matcher
   * @param {object} [mSettings] optional map/JSON-object with initial settings for the new LabelFor
   * @public
   * @name sap.ui.test.matchers.LabelFor
   * @author SAP SE
   * @since 1.46
   */
  var LabelFor = Matcher.extend("sap.ui.test.matchers.LabelFor", /** @lends sap.ui.test.matchers.LabelFor.prototype */ {

    metadata : {
      publicMethods : [ "isMatching" ],
      properties : {
        /**
         * The text of the (@link sap.m.Label) which have the labelFor property.
         */
        text : {
          type : "string"
        },

        /**
         * The name of the {@link sap.ui.model.resource.ResourceModel} assigned to the control.
         */
        modelName : {
          type : "string",
          defaultValue : "i18n"
        },

        /**
         * The key of the I18N text in the containing {@link jQuery.sap.util.ResourceBundle}.
         */
        key : {
          type : "string"
        },
        /**
         * The parameters for replacing the placeholders of the I18N text. See {@link jQuery.sap.util.ResourceBundle#getText}.
         */
        parameters : {
          type : "any"
        },
        /**
         * The name of the control property to match the I18N text with.
         */
        propertyName: {
          type: "string",
          defaultValue : "text"
        }
      }
    },

    /**
     * Checks for control with labelFor property annotating other control
     *
     * @param {sap.ui.core.Control} oControl the control that is checked by the matcher
     * @return {boolean} true if the Control has a label that matches the criteria
     * @public
     */
    isMatching : function (oControl) {
      var bIsMatching;
      var oPlugin = Opa5.getPlugin();
      var sModelName = this.getModelName();
      var sLabelText = this.getText();
      var aParameters = this.getParameters();
      var sPropertyName = this.getPropertyName();
      var sKey = this.getKey();

      if (sLabelText && sKey) {
        throw new Error("Combination of text and key properties is not possible");
      } else if (!sLabelText && !sKey) {
        throw new Error("No label text or key found");
      }

      var aLabelsInPage = oPlugin.getMatchingControls({controlType: "sap.m.Label", visible: false});

      oI18nMatcher.applySettings({
        key: sKey,
        modelName: sModelName,
        parameters: aParameters,
        propertyName: sPropertyName
      });

      bIsMatching = aLabelsInPage.some(function(oLabel) {
        if (sKey && oI18nMatcher.isMatching(oLabel)) {
          return oControl.getId() === oLabel.getLabelForRendering();
        } else if (sLabelText && oLabel.getText() === sLabelText){
          return oControl.getId() === oLabel.getLabelForRendering();
        }
      });

      if (!bIsMatching) {
        var sPropertyType = sKey ? "key: " + sKey : "text: " + sLabelText;
        this._oLogger.debug("No label with " + sPropertyType + " for control: " + oControl);
      }

      return bIsMatching;
    }
  });

  return LabelFor;
}, /* bExport= */ true);<commit_msg>[INTERNAL][FIX] sap.ui.test.matchers.LabelFor: jsdoc corrections<commit_after>/*!
 * ${copyright}
 */
sap.ui.define([
  'jquery.sap.global',
  './Matcher',
  './I18NText',
  'sap/ui/test/Opa5'
], function (jQuery, Matcher, I18NText, Opa5) {
  "use strict";

  var oI18nMatcher = new I18NText();

  /**
   * @class
   * The LabelFor matcher searches for given control associated with labelFor property.
   * The matcher does automatically
   *

   *

   *         retrieve control associated by label by given text
   *

   *

   *         retrieve control associated by label by given i18n key, modelName, parameters or propertyName. See {@link sap.ui.test.matchers.I18NText}
   *

   *

   *         combination of text and key is not possible
   *

   *

   *
   * @extends sap.ui.test.matchers.Matcher
   * @param {object} [mSettings] optional map/JSON-object with initial settings for the new LabelFor
   * @public
   * @name sap.ui.test.matchers.LabelFor
   * @author SAP SE
   * @since 1.46
   */
  var LabelFor = Matcher.extend("sap.ui.test.matchers.LabelFor", /** @lends sap.ui.test.matchers.LabelFor.prototype */ {

    metadata : {
      publicMethods : [ "isMatching" ],
      properties : {
        /**
         * The text of the {@link sap.m.Label} which have the labelFor property.
         */
        text : {
          type : "string"
        },

        /**
         * The name of the {@link sap.ui.model.resource.ResourceModel} assigned to the control.
         */
        modelName : {
          type : "string",
          defaultValue : "i18n"
        },

        /**
         * The key of the I18N text in the containing {@link jQuery.sap.util.ResourceBundle}.
         */
        key : {
          type : "string"
        },
        /**
         * The parameters for replacing the placeholders of the I18N text. See {@link jQuery.sap.util.ResourceBundle#getText}.
         */
        parameters : {
          type : "any"
        },
        /**
         * The name of the control property to match the I18N text with.
         */
        propertyName: {
          type: "string",
          defaultValue : "text"
        }
      }
    },

    /**
     * Checks for control with labelFor property annotating other control
     *
     * @param {sap.ui.core.Control} oControl the control that is checked by the matcher
     * @return {boolean} true if the Control has a label that matches the criteria
     * @public
     */
    isMatching : function (oControl) {
      var bIsMatching;
      var oPlugin = Opa5.getPlugin();
      var sModelName = this.getModelName();
      var sLabelText = this.getText();
      var aParameters = this.getParameters();
      var sPropertyName = this.getPropertyName();
      var sKey = this.getKey();

      if (sLabelText && sKey) {
        throw new Error("Combination of text and key properties is not possible");
      } else if (!sLabelText && !sKey) {
        throw new Error("No label text or key found");
      }

      var aLabelsInPage = oPlugin.getMatchingControls({controlType: "sap.m.Label", visible: false});

      oI18nMatcher.applySettings({
        key: sKey,
        modelName: sModelName,
        parameters: aParameters,
        propertyName: sPropertyName
      });

      bIsMatching = aLabelsInPage.some(function(oLabel) {
        if (sKey && oI18nMatcher.isMatching(oLabel)) {
          return oControl.getId() === oLabel.getLabelForRendering();
        } else if (sLabelText && oLabel.getText() === sLabelText){
          return oControl.getId() === oLabel.getLabelForRendering();
        }
      });

      if (!bIsMatching) {
        var sPropertyType = sKey ? "key: " + sKey : "text: " + sLabelText;
        this._oLogger.debug("No label with " + sPropertyType + " for control: " + oControl);
      }

      return bIsMatching;
    }
  });

  return LabelFor;
}, /* bExport= */ true);<|endoftext|>


<commit_before>/* eslint-disable promise/always-return */
import { runAuthenticatedQuery } from "schema/v2/test/utils"

describe("Me", () => {
  describe("CollectorProfile", () => {
    it("returns the collector profile", () => {
      const query = `
        {
          me {
            collectorProfile {
              internalID
              name
              email
              selfReportedPurchases
              intents
            }
          }
        }
      `

      const collectorProfile = {
        id: "3",
        name: "Percy",
        email: "percy@cat.com",
        self_reported_purchases: "treats",
        intents: ["buy art & design"],
      }

      const expectedProfileData = {
        internalID: "3",
        name: "Percy",
        email: "percy@cat.com",
        selfReportedPurchases: "treats",
        intents: ["buy art & design"],
      }

      const context = {
        collectorProfileLoader: sinon
          .stub()
          .returns(Promise.resolve(collectorProfile)),
      }

      return runAuthenticatedQuery(query, context).then(
        ({ me: { collectorProfile } }) => {
          expect(collectorProfile).toEqual(expectedProfileData)
        }
      )
    })
  })
})
<commit_msg>Update collector_profile.test.js<commit_after>/* eslint-disable promise/always-return */
import { runAuthenticatedQuery } from "schema/v2/test/utils"

describe("Me", () => {
  describe("CollectorProfile", () => {
    it("returns the collector profile", () => {
      const query = `
        {
          me {
            collectorProfile {
              internalID
              name
              email
              selfReportedPurchases
              intents
              privacy
            }
          }
        }
      `

      const collectorProfile = {
        id: "3",
        name: "Percy",
        email: "percy@cat.com",
        self_reported_purchases: "treats",
        intents: ["buy art & design"],
        privacy: 1,
      }

      const expectedProfileData = {
        internalID: "3",
        name: "Percy",
        email: "percy@cat.com",
        selfReportedPurchases: "treats",
        intents: ["buy art & design"],
        privacy: 1,
      }

      const context = {
        collectorProfileLoader: sinon
          .stub()
          .returns(Promise.resolve(collectorProfile)),
      }

      return runAuthenticatedQuery(query, context).then(
        ({ me: { collectorProfile } }) => {
          expect(collectorProfile).toEqual(expectedProfileData)
        }
      )
    })
  })
})
<|endoftext|>


<commit_before>const metadataParser = require('./metadataParserJson');

/**
 *
 * @param {*} biopaxElement Metadata for a node in the network
 * @param {*} biopaxFile BioPAX metadata `Map`
 * @param {*} visited Array containing all previously visited network nodes
 * @param {*} nodeType Is the node a cross-reference or not?
 * @returns A tree array containing metadata for the node
 */
function buildBioPaxSubtree(biopaxElement, biopaxFile, visited, nodeType = 'default') {
  let result = [];
  //Collect relevant data on this node, and push it into output
  if (nodeType !== 'Reference') {
    //Get type
    let type = biopaxElement['@type'];
    if (type) { result.push(['Type', type]); }

    //Get data source
    let dataSource = biopaxElement['dataSource'];
    if (dataSource) { result.push(['Data Source', dataSource]); }

    //Get display name
    let dName = biopaxElement['displayName'];
    if (dName) { result.push(['Display Name', dName]); }

    //Get Comments
    let comment = biopaxElement['comment'];
    if (comment) { result.push(['Comment', comment]); }

    //Get Names
    let name = biopaxElement['name'];
    if (name) { result.push(['Names', name]); }

    //Get Standard Name
    let sName = biopaxElement['standardName'];
    if (sName) { result.push(['Standard Name', sName]); }

    //Get Chemical Formula
    let formula = biopaxElement['chemicalFormula'];
    if (formula) { result.push(['Chemical Formula', formula]); }

    //Get Cellular Location
    let cellLocation = biopaxElement['cellularLocation'];
    if (cellLocation && cellLocation.indexOf('http') !== -1) {
      cellLocation = getElementFromBioPax(biopaxFile, cellLocation);
      cellLocation = cellLocation['term'];
    }
    if (cellLocation) { result.push(['Cellular Location', cellLocation]); }
  }

  //Get database id
  let db = biopaxElement['db'];
  let id = biopaxElement['id'];
  if (db && id) { result.push(['Database ID', [db, id]]); }

  //Get any cross references
  let xref = biopaxElement['xref'];

  //Resolve String/Array Issue
  if (typeof xref == 'string') { xref = [xref]; }
  if (!(xref)) { xref = []; }

  //Get entity reference and add it to xref
  let eref = biopaxElement['entityReference'];
  if (eref) xref.push(eref);

  //Recurse on each cross reference (Lim it level depth to 2)
  if (xref) {

    for (let i = 0; i < xref.length; i++) {

      let keyName = 'Reference';
      if (i == (xref.length - 1) && eref) { keyName = 'EntityReference' };

      //Get Referenced Element
      let refElement = getElementFromBioPax(biopaxFile, xref[i]);

      //Make a copy of visited
      let visitCopy = visited.slice();

      //Check validity of element
      if (!(refElement)) { continue; }

      //Recurse on current element
      if (visitCopy.indexOf(xref[i]) <= -1 && visitCopy.length <= 2) {

        visitCopy.push(xref[i]);
        result.push([keyName, buildBioPaxSubtree(refElement, biopaxFile, visitCopy, keyName)]);

      }
    }
  }
  //Return subtree
  return result;
}

/**
 *
 * @param {*} children Metadata for an entity in the network
 * @param {*} biopaxFile BioPAX metadata `Map`
 * @returns Tree array containing all metadata about the given entity
 */
function buildBioPaxTree(children, biopaxFile) {
  //Wrapper function for buildBioPaxSubtree recursion

  let result = [];

  //make sure the metadata exists
  if (!(children))
    return;

  //Get the node id, as defined in getProcessedBioPax
  let id = children['pathid'];
  if (!(id)) { id = children['about']; }
  if (!(id)) { return; }

  //Build a subtree
  let visited = [id];
  let subtree = buildBioPaxSubtree(children, biopaxFile, visited);
  result.push([id, subtree]);

  //Return Biopax Tree
  return result;
}

/**
 *
 * @param {*} word A String containing at least n underscores
 * @param {*} n The number of underscores to be removed
 * @returns The same string, but with all characters after nth underscore removed
 */
function removeAfterUnderscore(word, n) {
  let splitWord = word.split('_');
  let newWord = '';
  for (let i = 0; i < n; i++) {
    if (i != (n - 1)) {
      newWord += splitWord[i] + '_';
    } else {
      newWord += splitWord[i];
    }
  }
  return newWord;
}

/**
 *
 * @param {*} biopaxFile BioPAX metadata `Map`
 * @param {*} id String representing potential BioPAX ID
 * @returns BioPAX metadata for that ID (if it exists) or null (if it doesn't)
 */
function getElementFromBioPax(biopaxFile, id) {

  //Remove any URL stuff from the ID, just like getProcessedBioPax
  if (id.indexOf('http') !== -1 || id.indexOf('/') !== -1) {
    var lastIndex = id.lastIndexOf('/');
    id = id.substring(lastIndex + 1);
  }

  //Get element matching the ID
  //make sure the ID isn't "" or null
  if(id)
    return biopaxFile.get(id);
  else return null;
}

/**
 *
 * @param {*} nodeId node ID from Cytoscape network JSON
 * @param {*} biopax BioPAX metadata `Map`
 * @returns Subtree containing BioPax metadata for the node
 */
function getBioPaxSubtree(nodeId, biopax) {

  // The original entity IDs have been converted in the cytoscape network.
  // Need to first find the original BioPAX entity ID, then collect metadata.

  //Search for ID exactly as it appears
  let searchTerm = getElementFromBioPax(biopax, nodeId);
  if (searchTerm)
    return buildBioPaxTree(searchTerm, biopax);

  //Search for ID after last underscore
  searchTerm = getElementFromBioPax(biopax, nodeId.substring(nodeId.lastIndexOf("_") +1));
  if (searchTerm)
    return buildBioPaxTree(searchTerm, biopax);

  //Remove extra identifiers appended by Cytoscape
  let fixedNodeId = removeAfterUnderscore(nodeId, 2);

  //Resolve issues if there is no appended identifiers
  if (nodeId.indexOf('_') <= -1)
    fixedNodeId = nodeId;

  //Search for ID in the first 2 underscores
  searchTerm = getElementFromBioPax(biopax, fixedNodeId);
  if (searchTerm)
    return buildBioPaxTree(searchTerm, biopax);

  //Search for ID in between first and second underscore
  searchTerm = getElementFromBioPax(biopax, fixedNodeId.substring(fixedNodeId.lastIndexOf("_") +1));
  if (searchTerm)
    return buildBioPaxTree(searchTerm, biopax);

  //Search Failed, return null
  return null;
}

/**
 *
 * @param {*} biopaxJsonText String containing BioPAX network metadata
 * @returns `Map` containing BioPAX network metadata
 */
function getProcessedBioPax(biopaxJsonText) {
  //parse String into JSON object, rename '@id' property to 'pathid'
  const graph = JSON.parse(biopaxJsonText.replace(new RegExp('@id', 'g'), 'pathid'))['@graph'];
  const biopaxElementMap = new Map();

  //The 'pathid' property has a format like: http://pathwaycommons.org/pc2/someID
  //Convert it to this format: someID, and make it the key for the map
  for (const element of graph) {
    const fullId = element['pathid'];
    const lastIndex = fullId.lastIndexOf('/');
    const subId = fullId.substring(lastIndex + 1);

    biopaxElementMap.set(subId, element);
  }

  return biopaxElementMap;
}

/**
 *
 * @param {*} biopaxJsonText String containing BioPAX metadata for the network
 * @param {*} nodes JSON object containing all the nodes in the network
 * @returns Processed metadata for each node in the network
 */
function getBioPaxMetadata(biopaxJsonText, nodes) {
  //turn the biopax string into json
  const biopaxElementMap = getProcessedBioPax(biopaxJsonText);
  const nodeMetadataMap = {};

  //try to map each id in the biopax data to an id in the cytoscape json
  //add the data from biopax to the json
  nodes.forEach(node => {
    const id = node.data.id;
    nodeMetadataMap[id] = metadataParser(getBioPaxSubtree(id, biopaxElementMap));
  });

  return nodeMetadataMap;
}

module.exports = { getProcessedBioPax, getBioPaxMetadata};<commit_msg>removed recursion<commit_after>const metadataParser = require('./metadataParserJson');
var biopaxFile = null;
/**
 *
 * @param {*} biopaxElement Metadata for an entity from BioPAX
 * @param {*} biopaxFile BioPAX metadata `Map`
 * @param {*} nodeType Is this a xref or entity?
 */
function collectEntityMetadata(biopaxElement, nodeType = 'default'){

  let result = [];
  //Collect relevant data on this node, and push it into output
  if (nodeType !== 'Reference') {
    //Get type
    let type = biopaxElement['@type'];
    if (type) { result.push(['Type', type]); }

    //Get data source
    let dataSource = biopaxElement['dataSource'];
    if (dataSource) { result.push(['Data Source', dataSource]); }

    //Get display name
    let dName = biopaxElement['displayName'];
    if (dName) { result.push(['Display Name', dName]); }

    //Get Comments
    let comment = biopaxElement['comment'];
    if (comment) { result.push(['Comment', comment]); }

    //Get Names
    let name = biopaxElement['name'];
    if (name) { result.push(['Names', name]); }

    //Get Standard Name
    let sName = biopaxElement['standardName'];
    if (sName) { result.push(['Standard Name', sName]); }

    //Get Chemical Formula
    let formula = biopaxElement['chemicalFormula'];
    if (formula) { result.push(['Chemical Formula', formula]); }

    //Get Cellular Location
    let cellLocation = biopaxElement['cellularLocation'];
    if (cellLocation && cellLocation.indexOf('http') !== -1) {
      cellLocation = getElementFromBioPax(cellLocation);
      cellLocation = cellLocation['term'];
    }
    if (cellLocation) { result.push(['Cellular Location', cellLocation]); }
  }

  //Get database id
  let db = biopaxElement['db'];
  let id = biopaxElement['id'];
  if (db && id) { result.push(['Database ID', [db, id]]); }

  return result;
}

/**
 *
 * @param {*} entity Metadata for an entity in the network
 * @param {*} biopaxFile BioPAX metadata `Map`
 * @returns Tree array containing all metadata about the given entity
 */
function buildBioPaxTree(entity) {
  let result = [];

  //make sure the metadata exists
  if (!(entity))
    return;

  //Get the node id, as defined in getProcessedBioPax
  let id = entity['pathid'];
  if (!(id)) { id = entity['about']; }
  if (!(id)) { return; }

  //get BioPAX metadata for this node
  let collectedData = collectEntityMetadata(entity);

  //Collect Data for cross-references
  let xref = entity['xref'];
  if (typeof xref == 'string')
    xref = [xref];
  if (!(xref))
    xref = [];
  let eref = entity['entityReference'];
  if (eref)
    xref.push(eref);

  for (let i = 0; i < xref.length; i++) {
    //Check if this is a cross-reference or entity reference
    let keyName = 'Reference';
    if (i == (xref.length - 1) && eref)
      keyName = 'EntityReference';

    //Get Referenced element and make sure it's valid
    let refElement = getElementFromBioPax(xref[i]);
    if (!(refElement))
      continue;
    //Collect data and add to tree array
    collectedData.push([keyName,collectEntityMetadata(refElement,keyName)]);
  }

  result.push([id,collectedData]);
  return result;
}

/**
 *
 * @param {*} word A String containing at least n underscores
 * @param {*} n The number of underscores to be removed
 * @returns The same string, but with all characters after nth underscore removed
 */
function removeAfterUnderscore(word, n) {
  let splitWord = word.split('_');
  let newWord = '';
  for (let i = 0; i < n; i++) {
    if (i != (n - 1)) {
      newWord += splitWord[i] + '_';
    } else {
      newWord += splitWord[i];
    }
  }
  return newWord;
}

/**
 *
 * @param {*} biopaxFile BioPAX metadata `Map`
 * @param {*} id String representing potential BioPAX ID
 * @returns BioPAX metadata for that ID (if it exists) or null (if it doesn't)
 */
function getElementFromBioPax(id) {

  //Remove any URL stuff from the ID, just like getProcessedBioPax
  if (id.indexOf('http') !== -1 || id.indexOf('/') !== -1) {
    var lastIndex = id.lastIndexOf('/');
    id = id.substring(lastIndex + 1);
  }

  //Get element matching the ID
  //make sure the ID isn't "" or null
  if(id)
    return biopaxFile.get(id);
  else return null;
}

/**
 *
 * @param {*} nodeId node ID from Cytoscape network JSON
 * @param {*} biopax BioPAX metadata `Map`
 * @returns Subtree containing BioPax metadata for the node
 */
function getBioPaxSubtree(nodeId) {

  // The original entity IDs have been converted in the cytoscape network.
  // Need to first find the original BioPAX entity ID, then collect metadata.

  //Search for ID exactly as it appears
  let searchTerm = getElementFromBioPax(nodeId);
  if (searchTerm)
    return buildBioPaxTree(searchTerm);

  //Search for ID after last underscore
  searchTerm = getElementFromBioPax(nodeId.substring(nodeId.lastIndexOf("_") +1));
  if (searchTerm)
    return buildBioPaxTree(searchTerm);

  //Remove extra identifiers appended by Cytoscape
  let fixedNodeId = removeAfterUnderscore(nodeId, 2);

  //Resolve issues if there is no appended identifiers
  if (nodeId.indexOf('_') <= -1)
    fixedNodeId = nodeId;

  //Search for ID in the first 2 underscores
  searchTerm = getElementFromBioPax(fixedNodeId);
  if (searchTerm)
    return buildBioPaxTree(searchTerm);

  //Search for ID in between first and second underscore
  searchTerm = getElementFromBioPax(fixedNodeId.substring(fixedNodeId.lastIndexOf("_") +1));
  if (searchTerm)
    return buildBioPaxTree(searchTerm);

  //Search Failed, return null
  return null;
}

/**
 *
 * @param {*} biopaxJsonText String containing BioPAX network metadata
 * @returns `Map` containing BioPAX network metadata
 */
function getProcessedBioPax(biopaxJsonText) {
  //parse String into JSON object, rename '@id' property to 'pathid'
  const graph = JSON.parse(biopaxJsonText.replace(new RegExp('@id', 'g'), 'pathid'))['@graph'];
  const biopaxElementMap = new Map();

  //The 'pathid' property has a format like: http://pathwaycommons.org/pc2/someID
  //Convert it to this format: someID, and make it the key for the map
  for (const element of graph) {
    const fullId = element['pathid'];
    const lastIndex = fullId.lastIndexOf('/');
    const subId = fullId.substring(lastIndex + 1);

    biopaxElementMap.set(subId, element);
  }

  return biopaxElementMap;
}

/**
 *
 * @param {*} biopaxJsonText String containing BioPAX metadata for the network
 * @param {*} nodes JSON object containing all the nodes in the network
 * @returns Processed metadata for each node in the network
 */
function getBioPaxMetadata(biopaxJsonText, nodes) {
  //turn the biopax string into json
  biopaxFile = getProcessedBioPax(biopaxJsonText);
  const nodeMetadataMap = {};

  //try to map each id in the biopax data to an id in the cytoscape json
  //add the data from biopax to the json
  nodes.forEach(node => {
    const id = node.data.id;
    nodeMetadataMap[id] = metadataParser(getBioPaxSubtree(id));
  });

  return nodeMetadataMap;
}

module.exports = { getProcessedBioPax, getBioPaxMetadata};<|endoftext|>