<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import Dimensions from 'react-dimensions'

import * as d3 from 'd3';
import { map, groupBy, reduce, forEach, difference, find, uniq, remove, each, includes, assign } from 'lodash';
import moment from 'moment';

import { selectNodes, selectNode } from '../../modules/data/index';
import { phone, fieldLocator } from '../../helpers/index';

class Graph extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            nodes: [],
            links: [],
            highlight_nodes: [],
            edges: [],
            clusters: {},
            start: new Date(),
            time: 0,
            n: {
                id: 'test',
            },
            ticks: 0
        };

        const { containerHeight, containerWidth } = props;

        this.network = {
            graph: {
                nodes: [],
                links: [],
                highlight_nodes: [],
                selection: null,
                selectedNodes: [],
                tooltip: null,
                transform: d3.zoomIdentity,
                queries: []
            },
            height: containerHeight,
            width: containerWidth,
            simulation: {},
            lines: {
                stroke: {
                    color: "#ccc",
                    thickness: 1
                }
            },
            nodes: {
                fill: {
                    color: "#333"
                },
                stroke: {
                    color: "#fff",
                    thickness: 3
                },
                sizeRange: [12, 30]
            },
            zoomed: function () {
                this.graph.transform = d3.event.transform;
            },
            setup: function (el) {
                this.render = this.render.bind(this);
                this.drawNode = this.drawNode.bind(this);
                this.drawLink = this.drawLink.bind(this);

                this.canvas = el;
                this.context = this.canvas.getContext('2d');
                var canvas = d3.select(this.canvas)

                canvas.on("mousedown", this.mousedown.bind(this))
                    .on("mousemove", this.mousemove.bind(this))
                    .on("mouseup", this.mouseup.bind(this))
                    .call(d3.drag()
                        .filter(() => {
                            return d3.event.altKey;
                        })
                        .subject(this.dragsubject.bind(this))
                        .on("start", this.dragstarted.bind(this))
                        .on("drag", this.dragged.bind(this))
                        .on("end", this.dragended.bind(this))
                    )
                    .call(d3.zoom()
                        .filter(() => {
                            return d3.event.altKey;
                        })
                        .scaleExtent([1 / 2, 8])
                        .on("zoom", this.zoomed.bind(this))
                    );

                this.simulation = d3.forceSimulation()
                    .stop()
                    .force("link", d3.forceLink().id(function (d) {
                        return d.id;
                    }))
                    .force("charge", d3.forceManyBody().strength(-100).distanceMax(300))
                    .force("center", d3.forceCenter(this.width / 2, this.height / 2))
                    .force("vertical", d3.forceY().strength(0.018))
                    .force("horizontal", d3.forceX().strength(0.006));

                this.render();
            },
            forceScale: function (node) {
                var scale = d3.scaleLog().domain(this.nodes.sizeRange).range(this.nodes.sizeRange.slice().reverse());
                return node.r + scale(node.r);
            },
            select: function (nodes) {
                this.graph.selectedNodes = nodes;
            },
            highlight: function (nodes) {
                this.graph.highlight_nodes = nodes;
            },
            addNodes: function (graph) {
                var countExtent = d3.extent(graph.nodes, function (d) {
                        return d.connections;
                    }),
                    radiusScale = d3.scalePow().exponent(2).domain(countExtent).range(this.nodes.sizeRange);

                var newNodes = false;

                var that = this;
                each(graph.nodes, function (node) {
                    var n = find(that.graph.nodes, {id: node.id});
                    if (n) {
                        n.connections++;

                        n.r = radiusScale(n.connections);
                        n.force = that.forceScale(n);

                        n.query.push(node.query);
                        n.query = uniq(n.query);

                        n.color.push(node.color);
                        n.color = uniq(n.color);

                        return;
                    }

                    node.color = [node.color];
                    node.query = [node.query];
                    node.force = that.forceScale(node);
                    node.r = radiusScale(node.connections);
                    node.icon = "\uF047";

                    that.graph.nodes.push(node);

                    newNodes = true;
                });

                this.graph.links = this.graph.links.concat(graph.links);

                if (!newNodes)
                    return;

                this.simulation
                    .nodes(this.graph.nodes);

                this.simulation.force("link")
                    .links(this.graph.links);

                this.simulation.alpha(0.3).restart();
            },
            removeNodes: function (removed) {
                // remove nodes
                this.graph.nodes = remove(this.graph.nodes, (n) => {
                    return find(removed, {id: n});
                });

                // find links
                this.graph.links = remove(this.graph.links, (n) => {
                    return find(removed, {id: n.source.id}) || find(removed, {id: n.target.id});
                });
            },
            render: function () {
                if (!this.graph) {
                    return false;
                }

                this.context.save();
                this.context.clearRect(0, 0, this.width, this.height);

                this.context.translate((0) + this.graph.transform.x, (0) + this.graph.transform.y);

                this.context.scale(this.graph.transform.k, this.graph.transform.k);

                this.context.beginPath();

                this.graph.links.forEach((d)=> {
                    this.drawLink(d);
                });

                this.context.strokeStyle = this.lines.stroke.color;
                this.context.lineWidth = this.lines.stroke.thickness;
                this.context.stroke();

                this.graph.nodes.forEach((d)=> {
                    this.drawNode(d);
                });

                if (this.graph.selection) {
                    this.context.beginPath();
                    this.context.strokeStyle = '#c0c0c0';
                    this.context.fillStyle = "rgba(224, 224, 224, 0.6)";
                    //this.context.fillStyle = '#eee';
                    this.context.lineWidth = 1;
                    // this.context.setLineDash([6]);

                    this.context.rect(this.graph.selection.x1, this.graph.selection.y1, this.graph.selection.x2 - this.graph.selection.x1, this.graph.selection.y2 - this.graph.selection.y1);
                    this.context.fill();
                    this.context.stroke();
                }

                if (this.graph.tooltip) {
                    this.context.fillStyle = '#000'; //d.color[0];
                    this.context.font = "14px Arial";
                    this.context.fillText(this.graph.tooltip.node.id, this.graph.tooltip.x + 5, this.graph.tooltip.y - 5);
                }

                this.context.restore();

                requestAnimationFrame(this.render);
            },
            drawLink: function (d) {
                this.context.moveTo(d.source.x, d.source.y);
                this.context.lineTo(d.target.x, d.target.y);
            },
            drawNode: function (d) {
                // this.context.moveTo(d.x + d.r, d.y);
                // for each different query, show a part. This will show that the edge
                //  has been found in multiple queries.
                for (var i = 0; i < d.query.length; i++) {
                    // find color

                    this.context.beginPath();
                    this.context.arc(d.x, d.y, d.r, 2 * Math.PI * (i / d.color.length), 2 * Math.PI * ( (i + 1) / d.color.length));

                    var color = '#000'; // d.searches[i];

                    for (var j = 0; j < this.graph.queries.length; j++) {
                        if (this.graph.queries[j].q === d.query[i])
                            color = this.graph.queries[j].color;
                    }

                    this.context.fillStyle = color;
                    this.context.fill();

                    if (includes(this.graph.selectedNodes, d)) {
                        this.context.strokeStyle = '#993833';
                        this.context.lineWidth = this.nodes.stroke.thickness;
                        this.context.stroke();
                    }
                }

                // this.context.font="14px FontAwesome";
                // this.context.fillStyle = '#fff';
                // this.context.fillText(d.icon, d.x - ( d.r) + 1, d.y + 5);
            },
            mousedown: function () {
                const { dispatch } = this;

                if (d3.event.altKeys) {
                    return;
                }

                var x = this.graph.transform.invertX(d3.event.layerX),
                    y = this.graph.transform.invertY(d3.event.layerY);

                var subject = this.simulation.find(x, y, 20);
                if (typeof subject === undefined) {
                    this.graph.selection = {x1: x, y1: y, x2: x, y2: y};
                    dispatch(selectNodes({nodes: []}));
                    return;
                } else {
                    if (!includes(this.graph.selectedNodes, subject)) {
                        this.graph.selectedNodes.push(subject);
                    } else {
                        remove(this.graph.selectedNodes, subject);
                    }

                    dispatch(selectNodes({nodes: this.graph.selectedNodes}));
                }
            },
            mouseup: function () {
                if (d3.event.altKeys) {
                    return;
                }

                var x = this.graph.transform.invertX(d3.event.layerX),
                    y = this.graph.transform.invertY(d3.event.layerY);

                // find all nodes within selection and highliht
                this.graph.selection = null;
            },
            mousemove: function (n) {
                const { dispatch } = this;
                if (d3.event.altKeys) {
                    return;
                }

                var x = this.graph.transform.invertX(d3.event.layerX),
                    y = this.graph.transform.invertY(d3.event.layerY);

                if (this.graph.selection) {
                    this.graph.selection = assign(this.graph.selection, {x2: x, y2: y});

                    this.graph.nodes.forEach((d)=> {
                        if ((d.x > this.graph.selection.x1 && d.x < this.graph.selection.x2) &&
                            (d.y > this.graph.selection.y1 && d.y < this.graph.selection.y2)) {
                            if (!includes(this.graph.selectedNodes, d)) {
                                this.graph.selectedNodes.push(d);
                            }
                        }

                        if ((d.x > this.graph.selection.x2 && d.x < this.graph.selection.x1) &&
                            (d.y > this.graph.selection.y2 && d.y < this.graph.selection.y1)) {
                            if (!includes(this.graph.selectedNodes, d)) {
                                this.graph.selectedNodes.push(d);
                            }
                        }
                    });

                    dispatch(selectNodes({nodes: this.graph.selectedNodes}));
                    return;
                }

                var subject = this.simulation.find(x, y, 20);
                if (subject === undefined) {
                    this.graph.tooltip = null;
                } else {
                    this.graph.tooltip = {node: subject, x: x, y: y};
                    this.onmousemove(subject);
                }
            },
            dragstarted: function () {
                this.graph.selection = null;
                this.graph.tooltip = null;

                var x = d3.event.x,
                    y = d3.event.y;

                if (!d3.event.active) this.simulation.alphaTarget(0.3).restart();
                d3.event.subject.fx = x;
                d3.event.subject.fy = y;
            },
            dragged: function () {
                var x = d3.event.x,
                    y = d3.event.y;

                d3.event.subject.fx = (x);
                d3.event.subject.fy = (y);
            },
            dragended: function () {
                if (!d3.event.active) this.simulation.alphaTarget(0);
                d3.event.subject.fx = null;
                d3.event.subject.fy = null;
            },
            dragsubject: function () {
                const x = this.graph.transform.invertX(d3.event.x),
                    y = this.graph.transform.invertY(d3.event.y);

                return this.simulation.find(x, y, 20);
            },
            mousemoved: function () {
            }
        };
    }

    componentDidMount() {
        const { network } = this;
        const { dispatch } = this.props;

        network.dispatch = dispatch;
        network.onmouseclick = this.onMouseClick.bind(this);
        network.onmousemove = this.onMouseMove.bind(this);

        network.setup(this.refs["canvas"]);
    }

    onMouseClick(node) {
        const { dispatch } = this.props;
        dispatch(selectNode({node: node}));
    }

    onMouseMove(node) {
        //const { dispatch } = this.props;
        //dispatch(selectNode({node: node}));
    }

    onMouseOver(node) {
        //const { dispatch } = this.props;
        //dispatch(selectNode({node: node}));
    }

    componentWillReceiveProps(nextProps) {
    }

    componentDidUpdate(prevProps, prevState) {
        const { network } = this;
        const { queries } = this.props;

        network.graph.queries = queries;

        var { fields } = this.props;

        var nodes = [];
        var links = [];

        var removed = difference(prevProps.items, this.props.items);
        if (removed.length > 0) {
            var removed2 = [];
            forEach(removed, (d, i) => {
                forEach(fields, (field) => {
                    const value = fieldLocator(d.fields, field);

                    if (value) {
                        removed2.push(value);
                    }

                });
            });

            // this should be node id not packet
            network.removeNodes({
                nodes: removed2,
            });
        }

        if (this.props.items.length > 0) {
            forEach(this.props.items, (d, i) => {
                forEach(fields, (field) => {
                    const value = fieldLocator(d.fields, field);
                    if (!value) return;

                    nodes.push({
                        id: phone(value),
                        query: d.q,
                        name: value,
                        color: d.color,
                        connections: 1
                    });
                });

                forEach(fields, (source) => {
                    const sourceValue = fieldLocator(d.fields, source);
                    if (!sourceValue) return;

                    forEach(fields, (target) => {
                        const targetValue = fieldLocator(d.fields, target);
                        if (!targetValue) return;

                        links.push({
                            source: phone(sourceValue),
                            target: phone(targetValue),
                        });
                    });
                });
            });

            network.addNodes({
                nodes: nodes,
                links: links,
            });
        }
        network.select(this.props.node);
        network.highlight(this.props.highlight_nodes);
    }

    shouldComponentUpdate(){
        return true;
    }

    render() {
        const { containerHeight, containerWidth } = this.props;

        return (
            <canvas
                style={{fontFamily: 'fontAwesome'}}
                width={containerWidth}
                height={containerHeight}
                ref="canvas">
                histogram
            </canvas>
        );
    }
}

const select = (state, ownProps) => {
    return {
        ...ownProps,
        node: state.entries.node,
        queries: state.entries.searches,
        fields: state.entries.fields,
        items: state.entries.items,
        highlight_nodes: state.entries.highlight_nodes,
    }
}

export default connect(select)(Dimensions()(Graph));<commit_msg>fixed mouse selection<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import Dimensions from 'react-dimensions'

import * as d3 from 'd3';
import { map, groupBy, reduce, forEach, difference, find, uniq, remove, each, includes, assign } from 'lodash';
import moment from 'moment';

import { selectNodes, selectNode } from '../../modules/data/index';
import { phone, fieldLocator } from '../../helpers/index';

class Graph extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            nodes: [],
            links: [],
            highlight_nodes: [],
            edges: [],
            clusters: {},
            start: new Date(),
            time: 0,
            n: {
                id: 'test',
            },
            ticks: 0
        };

        const { containerHeight, containerWidth } = props;

        this.network = {
            graph: {
                nodes: [],
                links: [],
                highlight_nodes: [],
                selection: null,
                selectedNodes: [],
                tooltip: null,
                transform: d3.zoomIdentity,
                queries: []
            },
            height: containerHeight,
            width: containerWidth,
            simulation: {},
            lines: {
                stroke: {
                    color: "#ccc",
                    thickness: 1
                }
            },
            nodes: {
                fill: {
                    color: "#333"
                },
                stroke: {
                    color: "#fff",
                    thickness: 3
                },
                sizeRange: [12, 30]
            },
            zoomed: function () {
                this.graph.transform = d3.event.transform;
            },
            setup: function (el) {
                this.render = this.render.bind(this);
                this.drawNode = this.drawNode.bind(this);
                this.drawLink = this.drawLink.bind(this);

                this.canvas = el;
                this.context = this.canvas.getContext('2d');
                var canvas = d3.select(this.canvas)

                canvas.on("mousedown", this.mousedown.bind(this))
                    .on("mousemove", this.mousemove.bind(this))
                    .on("mouseup", this.mouseup.bind(this))
                    .call(d3.drag()
                        .filter(() => {
                            return d3.event.altKey;
                        })
                        .subject(this.dragsubject.bind(this))
                        .on("start", this.dragstarted.bind(this))
                        .on("drag", this.dragged.bind(this))
                        .on("end", this.dragended.bind(this))
                    )
                    .call(d3.zoom()
                        .filter(() => {
                            return d3.event.altKey;
                        })
                        .scaleExtent([1 / 2, 8])
                        .on("zoom", this.zoomed.bind(this))
                    );

                this.simulation = d3.forceSimulation()
                    .stop()
                    .force("link", d3.forceLink().id(function (d) {
                        return d.id;
                    }))
                    .force("charge", d3.forceManyBody().strength(-100).distanceMax(300))
                    .force("center", d3.forceCenter(this.width / 2, this.height / 2))
                    .force("vertical", d3.forceY().strength(0.018))
                    .force("horizontal", d3.forceX().strength(0.006));

                this.render();
            },
            forceScale: function (node) {
                var scale = d3.scaleLog().domain(this.nodes.sizeRange).range(this.nodes.sizeRange.slice().reverse());
                return node.r + scale(node.r);
            },
            select: function (nodes) {
                this.graph.selectedNodes = nodes;
            },
            highlight: function (nodes) {
                this.graph.highlight_nodes = nodes;
            },
            addNodes: function (graph) {
                var countExtent = d3.extent(graph.nodes, function (d) {
                        return d.connections;
                    }),
                    radiusScale = d3.scalePow().exponent(2).domain(countExtent).range(this.nodes.sizeRange);

                var newNodes = false;

                var that = this;
                each(graph.nodes, function (node) {
                    var n = find(that.graph.nodes, {id: node.id});
                    if (n) {
                        n.connections++;

                        n.r = radiusScale(n.connections);
                        n.force = that.forceScale(n);

                        n.query.push(node.query);
                        n.query = uniq(n.query);

                        n.color.push(node.color);
                        n.color = uniq(n.color);

                        return;
                    }

                    node.color = [node.color];
                    node.query = [node.query];
                    node.force = that.forceScale(node);
                    node.r = radiusScale(node.connections);
                    node.icon = "\uF047";

                    that.graph.nodes.push(node);

                    newNodes = true;
                });

                this.graph.links = this.graph.links.concat(graph.links);

                if (!newNodes)
                    return;

                this.simulation
                    .nodes(this.graph.nodes);

                this.simulation.force("link")
                    .links(this.graph.links);

                this.simulation.alpha(0.3).restart();
            },
            removeNodes: function (removed) {
                // remove nodes
                this.graph.nodes = remove(this.graph.nodes, (n) => {
                    return find(removed, {id: n});
                });

                // find links
                this.graph.links = remove(this.graph.links, (n) => {
                    return find(removed, {id: n.source.id}) || find(removed, {id: n.target.id});
                });
            },
            render: function () {
                if (!this.graph) {
                    return false;
                }

                this.context.save();
                this.context.clearRect(0, 0, this.width, this.height);

                this.context.translate((0) + this.graph.transform.x, (0) + this.graph.transform.y);

                this.context.scale(this.graph.transform.k, this.graph.transform.k);

                this.context.beginPath();

                this.graph.links.forEach((d)=> {
                    this.drawLink(d);
                });

                this.context.strokeStyle = this.lines.stroke.color;
                this.context.lineWidth = this.lines.stroke.thickness;
                this.context.stroke();

                this.graph.nodes.forEach((d)=> {
                    this.drawNode(d);
                });

                if (this.graph.selection) {
                    this.context.beginPath();
                    this.context.strokeStyle = '#c0c0c0';
                    this.context.fillStyle = "rgba(224, 224, 224, 0.6)";
                    //this.context.fillStyle = '#eee';
                    this.context.lineWidth = 1;
                    // this.context.setLineDash([6]);

                    this.context.rect(this.graph.selection.x1, this.graph.selection.y1, this.graph.selection.x2 - this.graph.selection.x1, this.graph.selection.y2 - this.graph.selection.y1);
                    this.context.fill();
                    this.context.stroke();
                }

                if (this.graph.tooltip) {
                    this.context.fillStyle = '#000'; //d.color[0];
                    this.context.font = "14px Arial";
                    this.context.fillText(this.graph.tooltip.node.id, this.graph.tooltip.x + 5, this.graph.tooltip.y - 5);
                }

                this.context.restore();

                requestAnimationFrame(this.render);
            },
            drawLink: function (d) {
                this.context.moveTo(d.source.x, d.source.y);
                this.context.lineTo(d.target.x, d.target.y);
            },
            drawNode: function (d) {
                // this.context.moveTo(d.x + d.r, d.y);
                // for each different query, show a part. This will show that the edge
                //  has been found in multiple queries.
                for (var i = 0; i < d.query.length; i++) {
                    // find color

                    this.context.beginPath();
                    this.context.arc(d.x, d.y, d.r, 2 * Math.PI * (i / d.color.length), 2 * Math.PI * ( (i + 1) / d.color.length));

                    var color = '#000'; // d.searches[i];

                    for (var j = 0; j < this.graph.queries.length; j++) {
                        if (this.graph.queries[j].q === d.query[i])
                            color = this.graph.queries[j].color;
                    }

                    this.context.fillStyle = color;
                    this.context.fill();

                    if (includes(this.graph.selectedNodes, d)) {
                        this.context.strokeStyle = '#993833';
                        this.context.lineWidth = this.nodes.stroke.thickness;
                        this.context.stroke();
                    }
                }

                // this.context.font="14px FontAwesome";
                // this.context.fillStyle = '#fff';
                // this.context.fillText(d.icon, d.x - ( d.r) + 1, d.y + 5);
            },
            mousedown: function () {
                const { dispatch, graph } = this;

                if (d3.event.altKeys) {
                    return;
                }

                var x = graph.transform.invertX(d3.event.layerX),
                    y = graph.transform.invertY(d3.event.layerY);

                var subject = this.simulation.find(x, y, 20);
                if (!subject) {
                    graph.selection = {x1: x, y1: y, x2: x, y2: y};
                    dispatch(selectNodes({nodes: []}));
                    return;
                } else {
                    if (!includes(graph.selectedNodes, subject)) {
                        graph.selectedNodes.push(subject);
                    } else {
                        remove(graph.selectedNodes, subject);
                    }

                    dispatch(selectNodes({nodes: graph.selectedNodes}));
                }
            },
            mouseup: function () {
                const { graph } = this;

                if (d3.event.altKeys) {
                    return;
                }

                var x = graph.transform.invertX(d3.event.layerX),
                    y = graph.transform.invertY(d3.event.layerY);

                // find all nodes within selection and highliht
                graph.selection = null;
            },
            mousemove: function (n) {
                const { dispatch, graph } = this;

                if (d3.event.altKeys) {
                    return;
                }

                var x = graph.transform.invertX(d3.event.layerX),
                    y = graph.transform.invertY(d3.event.layerY);

                if (graph.selection) {
                    console.debug("BLA", "mousemove");
                    graph.selection = assign(graph.selection, {x2: x, y2: y});

                    graph.nodes.forEach((d)=> {
                        if ((d.x > graph.selection.x1 && d.x < graph.selection.x2) &&
                            (d.y > graph.selection.y1 && d.y < graph.selection.y2)) {
                            if (!includes(graph.selectedNodes, d)) {
                                graph.selectedNodes.push(d);
                            }
                        }

                        if ((d.x > graph.selection.x2 && d.x < graph.selection.x1) &&
                            (d.y > graph.selection.y2 && d.y < graph.selection.y1)) {
                            if (!includes(graph.selectedNodes, d)) {
                                graph.selectedNodes.push(d);
                            }
                        }
                    });

                    dispatch(selectNodes({nodes: graph.selectedNodes}));
                    return;
                }

                var subject = this.simulation.find(x, y, 20);
                if (subject === undefined) {
                    graph.tooltip = null;
                } else {
                    graph.tooltip = {node: subject, x: x, y: y};
                    this.onmousemove(subject);
                }
            },
            dragstarted: function () {
                this.graph.selection = null;
                this.graph.tooltip = null;

                var x = d3.event.x,
                    y = d3.event.y;

                if (!d3.event.active) this.simulation.alphaTarget(0.3).restart();
                d3.event.subject.fx = x;
                d3.event.subject.fy = y;
            },
            dragged: function () {
                var x = d3.event.x,
                    y = d3.event.y;

                d3.event.subject.fx = (x);
                d3.event.subject.fy = (y);
            },
            dragended: function () {
                if (!d3.event.active) this.simulation.alphaTarget(0);
                d3.event.subject.fx = null;
                d3.event.subject.fy = null;
            },
            dragsubject: function () {
                const x = this.graph.transform.invertX(d3.event.x),
                    y = this.graph.transform.invertY(d3.event.y);

                return this.simulation.find(x, y, 20);
            },
            mousemoved: function () {
            }
        };
    }

    componentDidMount() {
        const { network } = this;
        const { dispatch } = this.props;

        network.dispatch = dispatch;
        network.onmouseclick = this.onMouseClick.bind(this);
        network.onmousemove = this.onMouseMove.bind(this);

        network.setup(this.refs["canvas"]);
    }

    onMouseClick(node) {
        const { dispatch } = this.props;
        dispatch(selectNode({node: node}));
    }

    onMouseMove(node) {
        //const { dispatch } = this.props;
        //dispatch(selectNode({node: node}));
    }

    onMouseOver(node) {
        //const { dispatch } = this.props;
        //dispatch(selectNode({node: node}));
    }

    componentWillReceiveProps(nextProps) {
    }

    componentDidUpdate(prevProps, prevState) {
        const { network } = this;
        const { queries } = this.props;

        network.graph.queries = queries;

        var { fields } = this.props;

        var nodes = [];
        var links = [];

        var removed = difference(prevProps.items, this.props.items);
        if (removed.length > 0) {
            var removed2 = [];
            forEach(removed, (d, i) => {
                forEach(fields, (field) => {
                    const value = fieldLocator(d.fields, field);

                    if (value) {
                        removed2.push(value);
                    }

                });
            });

            // this should be node id not packet
            network.removeNodes({
                nodes: removed2,
            });
        }

        if (this.props.items.length > 0) {
            forEach(this.props.items, (d, i) => {
                forEach(fields, (field) => {
                    const value = fieldLocator(d.fields, field);
                    if (!value) return;

                    nodes.push({
                        id: phone(value),
                        query: d.q,
                        name: value,
                        color: d.color,
                        connections: 1
                    });
                });

                forEach(fields, (source) => {
                    const sourceValue = fieldLocator(d.fields, source);
                    if (!sourceValue) return;

                    forEach(fields, (target) => {
                        const targetValue = fieldLocator(d.fields, target);
                        if (!targetValue) return;

                        links.push({
                            source: phone(sourceValue),
                            target: phone(targetValue),
                        });
                    });
                });
            });

            network.addNodes({
                nodes: nodes,
                links: links,
            });
        }
        network.select(this.props.node);
        network.highlight(this.props.highlight_nodes);
    }

    shouldComponentUpdate(){
        return true;
    }

    render() {
        const { containerHeight, containerWidth } = this.props;

        return (
            <canvas
                style={{fontFamily: 'fontAwesome'}}
                width={containerWidth}
                height={containerHeight}
                ref="canvas">
                histogram
            </canvas>
        );
    }
}

const select = (state, ownProps) => {
    return {
        ...ownProps,
        node: state.entries.node,
        queries: state.entries.searches,
        fields: state.entries.fields,
        items: state.entries.items,
        highlight_nodes: state.entries.highlight_nodes,
    }
}

export default connect(select)(Dimensions()(Graph));<|endoftext|>


<commit_before>import moment from 'moment';
import * as Sentry from '@sentry/browser';
import get from 'platform/utilities/data/get';
import { hasSession } from 'platform/user/profile/utilities';

import {
  convertToDateField,
  validateCurrentOrPastDate,
} from 'platform/forms-system/src/js/validation';
import { isValidDateRange } from 'platform/forms/validations';

function calculateEndDate() {
  const endDateLimit = 1;
  const description = '1 year';

  return {
    endDateLimit,
    description,
    endDate: moment()
      .endOf('day')
      .add(endDateLimit, 'years'),
  };
}

export function validateServiceDates(
  errors,
  { lastDischargeDate, lastEntryDate },
  { veteranDateOfBirth },
) {
  const fromDate = convertToDateField(lastEntryDate);
  const toDate = convertToDateField(lastDischargeDate);
  const endDateInfo = calculateEndDate();

  // TODO: Use a constant instead of a magic string
  if (
    !isValidDateRange(fromDate, toDate) ||
    moment(lastDischargeDate, 'YYYY-MM-DD').isAfter(endDateInfo.endDate)
  ) {
    errors.lastDischargeDate.addError(
      `Discharge date must be after the service period start date and before ${endDateInfo.endDate.format(
        'MMMM D, YYYY',
      )} (${endDateInfo.description} from today)`,
    );
  }

  if (veteranDateOfBirth) {
    const dateOfBirth = moment(veteranDateOfBirth);

    if (dateOfBirth.add(15, 'years').isAfter(moment(lastEntryDate))) {
      errors.lastEntryDate.addError(
        'You must have been at least 15 years old when you entered the service',
      );
    }
  }
}

function logMarriageError(
  messageType,
  errorMessage,
  spouseDateOfBirth,
  veteranDateOfBirth,
  marriageDate,
) {
  Sentry.withScope(scope => {
    const message = `hca_1010ez_error_${messageType}`;
    const momentVetDOB = moment(veteranDateOfBirth);
    const momentSpouseDOB = moment(spouseDateOfBirth);
    const momentMarriage = moment(marriageDate);
    scope.setContext(message, {
      spouseDateOfBirth,
      veteranDateOfBirth,
      marriageDate,
      momentSpouseDOB,
      momentVetDOB,
      momentMarriage,
      errorMessage,
    });
    Sentry.captureMessage(message);
  });
}

export function logValidateMarriageDate(
  errors,
  formfield,
  {
    dateOfMarriage,
    spouseDateOfBirth,
    veteranDateOfBirth,
    discloseFinancialInformation,
  },
) {
  const vetDOB = moment(veteranDateOfBirth);
  const spouseDOB = moment(spouseDateOfBirth);
  const marriage = moment(dateOfMarriage);
  if (
    discloseFinancialInformation &&
    spouseDOB.isAfter(marriage) &&
    vetDOB.isAfter(marriage)
  ) {
    logMarriageError(
      'marriage_date',
      'Date of marriage cannot be before the Veteran’s or the spouse’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  } else if (discloseFinancialInformation && spouseDOB.isAfter(marriage)) {
    logMarriageError(
      'spouse_dob',
      'Date of marriage cannot be before the spouse’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  } else if (discloseFinancialInformation && vetDOB.isAfter(marriage)) {
    logMarriageError(
      'veteran_dob',
      'Date of marriage cannot be before the Veteran’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  }
}

export function logValidateMarriageDateVaFacilityPage(
  errors,
  formField,
  {
    dateOfMarriage,
    spouseDateOfBirth,
    veteranDateOfBirth,
    discloseFinancialInformation,
    veteranFullName,
    veteranSocialSecurityNumber,
  },
) {
  // log if name, ssn or dob does not have a value
  if (!veteranFullName && !veteranSocialSecurityNumber && !veteranDateOfBirth) {
    const message = 'hca_1010ez_error_name_ssn_dob';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        veteranSocialSecurityNumber,
        veteranDateOfBirth,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranFullName && !veteranSocialSecurityNumber) {
    const message = 'hca_1010ez_error_name_ssn';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        veteranSocialSecurityNumber,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranFullName && !veteranDateOfBirth) {
    const message = 'hca_1010ez_error_name_dob';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        veteranDateOfBirth,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranSocialSecurityNumber && !veteranDateOfBirth) {
    const message = 'hca_1010ez_error_ssn_dob';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranSocialSecurityNumber,
        veteranDateOfBirth,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranFullName) {
    const message = 'hca_1010ez_error_name';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranSocialSecurityNumber) {
    const message = 'hca_1010ez_error_ssn';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranSocialSecurityNumber,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranDateOfBirth) {
    const message = 'hca_1010ez_error_dob';
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranDateOfBirth,
        hasSession: hasSession(),
      });
      Sentry.captureMessage(message);
    });
  }

  if (!dateOfMarriage) return;

  const vetDOB = moment(veteranDateOfBirth);
  const spouseDOB = moment(spouseDateOfBirth);
  const marriage = moment(dateOfMarriage);
  if (
    discloseFinancialInformation &&
    spouseDOB.isAfter(marriage) &&
    vetDOB.isAfter(marriage)
  ) {
    logMarriageError(
      'marriage_date_VaFacilityPage',
      'Date of marriage cannot be before the Veteran’s or the spouse’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  } else if (discloseFinancialInformation && spouseDOB.isAfter(marriage)) {
    logMarriageError(
      'spouse_dob_VaFacilityPage',
      'Date of marriage cannot be before the spouse’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  } else if (discloseFinancialInformation && vetDOB.isAfter(marriage)) {
    logMarriageError(
      'veteran_dob_VaFacilityPage',
      'Date of marriage cannot be before the Veteran’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  }
}

// export function validateMarriageDate(
//   errors,
//   marriageDate,
//   { spouseDateOfBirth, veteranDateOfBirth, discloseFinancialInformation },
// ) {
//   const vetDOB = moment(veteranDateOfBirth);
//   const spouseDOB = moment(spouseDateOfBirth);
//   const marriage = moment(marriageDate);
//   if (
//     discloseFinancialInformation &&
//     spouseDOB.isAfter(marriage) &&
//     vetDOB.isAfter(marriage)
//   ) {
//     errors.addError(
//       'Date of marriage cannot be before the Veteran’s or the spouse’s date of birth',
//     );
//   } else if (discloseFinancialInformation && spouseDOB.isAfter(marriage)) {
//     errors.addError(
//       'Date of marriage cannot be before the spouse’s date of birth',
//     );
//   } else if (discloseFinancialInformation && vetDOB.isAfter(marriage)) {
//     errors.addError(
//       'Date of marriage cannot be before the Veteran’s date of birth',
//     );
//   }
//   validateCurrentOrPastDate(errors, marriageDate);
// }

export function validateDependentDate(
  errors,
  dependentDate,
  formData,
  schema,
  messages,
  index,
) {
  const dependent = moment(dependentDate);
  const dob = moment(get(`dependents[${index}].dateOfBirth`, formData));

  if (formData.discloseFinancialInformation && dob.isAfter(dependent)) {
    errors.addError('This date must come after the dependent’s birth date');
  }
  validateCurrentOrPastDate(errors, dependentDate);
}

export function validateCurrency(errors, currencyAmount) {
  // Source: https://stackoverflow.com/a/16242575
  // HACK: Due to us-forms-system issue 269 (https://github.com/usds/us-forms-system/issues/269)
  if (
    !/(?=.*?\d)^\$?(([1-9]\d{0,2}(,\d{3})*)|\d+)?(\.\d{1,2})?$/.test(
      currencyAmount,
    )
  ) {
    errors.addError('Please enter a valid dollar amount');
  }
}
<commit_msg>logging hasSession and !hasSession separately (#21058)<commit_after>import moment from 'moment';
import * as Sentry from '@sentry/browser';
import get from 'platform/utilities/data/get';
import { hasSession } from 'platform/user/profile/utilities';

import {
  convertToDateField,
  validateCurrentOrPastDate,
} from 'platform/forms-system/src/js/validation';
import { isValidDateRange } from 'platform/forms/validations';

function calculateEndDate() {
  const endDateLimit = 1;
  const description = '1 year';

  return {
    endDateLimit,
    description,
    endDate: moment()
      .endOf('day')
      .add(endDateLimit, 'years'),
  };
}

export function validateServiceDates(
  errors,
  { lastDischargeDate, lastEntryDate },
  { veteranDateOfBirth },
) {
  const fromDate = convertToDateField(lastEntryDate);
  const toDate = convertToDateField(lastDischargeDate);
  const endDateInfo = calculateEndDate();

  // TODO: Use a constant instead of a magic string
  if (
    !isValidDateRange(fromDate, toDate) ||
    moment(lastDischargeDate, 'YYYY-MM-DD').isAfter(endDateInfo.endDate)
  ) {
    errors.lastDischargeDate.addError(
      `Discharge date must be after the service period start date and before ${endDateInfo.endDate.format(
        'MMMM D, YYYY',
      )} (${endDateInfo.description} from today)`,
    );
  }

  if (veteranDateOfBirth) {
    const dateOfBirth = moment(veteranDateOfBirth);

    if (dateOfBirth.add(15, 'years').isAfter(moment(lastEntryDate))) {
      errors.lastEntryDate.addError(
        'You must have been at least 15 years old when you entered the service',
      );
    }
  }
}

function logMarriageError(
  messageType,
  errorMessage,
  spouseDateOfBirth,
  veteranDateOfBirth,
  marriageDate,
) {
  Sentry.withScope(scope => {
    const message = `hca_1010ez_error_${messageType}`;
    const momentVetDOB = moment(veteranDateOfBirth);
    const momentSpouseDOB = moment(spouseDateOfBirth);
    const momentMarriage = moment(marriageDate);
    scope.setContext(message, {
      spouseDateOfBirth,
      veteranDateOfBirth,
      marriageDate,
      momentSpouseDOB,
      momentVetDOB,
      momentMarriage,
      errorMessage,
    });
    Sentry.captureMessage(message);
  });
}

export function logValidateMarriageDate(
  errors,
  formfield,
  {
    dateOfMarriage,
    spouseDateOfBirth,
    veteranDateOfBirth,
    discloseFinancialInformation,
  },
) {
  const vetDOB = moment(veteranDateOfBirth);
  const spouseDOB = moment(spouseDateOfBirth);
  const marriage = moment(dateOfMarriage);
  if (
    discloseFinancialInformation &&
    spouseDOB.isAfter(marriage) &&
    vetDOB.isAfter(marriage)
  ) {
    logMarriageError(
      'marriage_date',
      'Date of marriage cannot be before the Veteran’s or the spouse’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  } else if (discloseFinancialInformation && spouseDOB.isAfter(marriage)) {
    logMarriageError(
      'spouse_dob',
      'Date of marriage cannot be before the spouse’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  } else if (discloseFinancialInformation && vetDOB.isAfter(marriage)) {
    logMarriageError(
      'veteran_dob',
      'Date of marriage cannot be before the Veteran’s date of birth',
      spouseDateOfBirth,
      veteranDateOfBirth,
      dateOfMarriage,
    );
  }
}

export function logVeteranNameSsnDob(
  messageType,
  veteranFullName,
  veteranSocialSecurityNumber,
  veteranDateOfBirth,
  veteranHasSession,
) {
  // log if name, ssn or dob does not have a value
  if (!veteranFullName && !veteranSocialSecurityNumber && !veteranDateOfBirth) {
    const message = `hca_1010ez_error_name_ssn_dob${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        veteranSocialSecurityNumber,
        veteranDateOfBirth,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranFullName && !veteranSocialSecurityNumber) {
    const message = `hca_1010ez_error_name_ssn${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        veteranSocialSecurityNumber,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranFullName && !veteranDateOfBirth) {
    const message = `hca_1010ez_error_name_dob${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        veteranDateOfBirth,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranSocialSecurityNumber && !veteranDateOfBirth) {
    const message = `hca_1010ez_error_ssn_dob${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranSocialSecurityNumber,
        veteranDateOfBirth,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranFullName) {
    const message = `hca_1010ez_error_name${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranFullName,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranSocialSecurityNumber) {
    const message = `hca_1010ez_error_ssn${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranSocialSecurityNumber,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  } else if (!veteranDateOfBirth) {
    const message = `hca_1010ez_error_dob${messageType}`;
    Sentry.withScope(scope => {
      scope.setContext(message, {
        veteranDateOfBirth,
        hasSession: veteranHasSession,
      });
      Sentry.captureMessage(message);
    });
  }
}

export function logValidateMarriageDateVaFacilityPage(
  errors,
  formField,
  { veteranDateOfBirth, veteranFullName, veteranSocialSecurityNumber },
) {
  if (hasSession()) {
    logVeteranNameSsnDob(
      '_form_hasSession',
      veteranFullName,
      veteranSocialSecurityNumber,
      veteranDateOfBirth,
      true,
    );
  } else {
    logVeteranNameSsnDob(
      '_form_notHasSession',
      veteranFullName,
      veteranSocialSecurityNumber,
      veteranDateOfBirth,
      false,
    );
  }
}

// export function validateMarriageDate(
//   errors,
//   marriageDate,
//   { spouseDateOfBirth, veteranDateOfBirth, discloseFinancialInformation },
// ) {
//   const vetDOB = moment(veteranDateOfBirth);
//   const spouseDOB = moment(spouseDateOfBirth);
//   const marriage = moment(marriageDate);
//   if (
//     discloseFinancialInformation &&
//     spouseDOB.isAfter(marriage) &&
//     vetDOB.isAfter(marriage)
//   ) {
//     errors.addError(
//       'Date of marriage cannot be before the Veteran’s or the spouse’s date of birth',
//     );
//   } else if (discloseFinancialInformation && spouseDOB.isAfter(marriage)) {
//     errors.addError(
//       'Date of marriage cannot be before the spouse’s date of birth',
//     );
//   } else if (discloseFinancialInformation && vetDOB.isAfter(marriage)) {
//     errors.addError(
//       'Date of marriage cannot be before the Veteran’s date of birth',
//     );
//   }
//   validateCurrentOrPastDate(errors, marriageDate);
// }

export function validateDependentDate(
  errors,
  dependentDate,
  formData,
  schema,
  messages,
  index,
) {
  const dependent = moment(dependentDate);
  const dob = moment(get(`dependents[${index}].dateOfBirth`, formData));

  if (formData.discloseFinancialInformation && dob.isAfter(dependent)) {
    errors.addError('This date must come after the dependent’s birth date');
  }
  validateCurrentOrPastDate(errors, dependentDate);
}

export function validateCurrency(errors, currencyAmount) {
  // Source: https://stackoverflow.com/a/16242575
  // HACK: Due to us-forms-system issue 269 (https://github.com/usds/us-forms-system/issues/269)
  if (
    !/(?=.*?\d)^\$?(([1-9]\d{0,2}(,\d{3})*)|\d+)?(\.\d{1,2})?$/.test(
      currencyAmount,
    )
  ) {
    errors.addError('Please enter a valid dollar amount');
  }
}
<|endoftext|>


<commit_before>import { DocumentNode, STRING } from 'substance'

export default class Organisation extends DocumentNode {
  toString () {
    return this.institution
  }

  render (options = {}) {
    let { institution, division1, division2, division3 } = this
    let result = institution ? [ institution ] : '???'
    if (!options.short && institution) {
      if (division1) {
        result.push(', ', division1)
      }
      if (division2) {
        result.push(', ', division2)
      }
      if (division3) {
        result.push(', ', division3)
      }
    }
    return result
  }
}

Organisation.schema = {
  type: 'organisation',
  institution: STRING,
  division1: STRING,
  division2: STRING,
  division3: STRING,
  // Consider switching to address-line1,2,3
  street: STRING,
  addressComplements: STRING,
  city: STRING,
  state: STRING,
  postalCode: STRING,
  country: STRING,
  phone: STRING,
  fax: STRING,
  email: STRING,
  uri: STRING
}
<commit_msg>Use Organisation.render() for toString()<commit_after>import { DocumentNode, STRING } from 'substance'

export default class Organisation extends DocumentNode {
  toString () {
    return this.render()
  }

  render (options = {}) {
    let { institution, division1, division2, division3 } = this
    let result = institution ? [ institution ] : '???'
    // TODO: do we really want this? Because the divisions might
    // be necessary to really understand the displayed name
    if (!options.short && institution) {
      if (division1) {
        result.push(', ', division1)
      }
      if (division2) {
        result.push(', ', division2)
      }
      if (division3) {
        result.push(', ', division3)
      }
    }
    return result
  }
}

Organisation.schema = {
  type: 'organisation',
  institution: STRING,
  division1: STRING,
  division2: STRING,
  division3: STRING,
  // Consider switching to address-line1,2,3
  street: STRING,
  addressComplements: STRING,
  city: STRING,
  state: STRING,
  postalCode: STRING,
  country: STRING,
  phone: STRING,
  fax: STRING,
  email: STRING,
  uri: STRING
}
<|endoftext|>


<commit_before>/**
* @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document);
<commit_msg>Modernizr includes html5shiv.<commit_after><|endoftext|>


<commit_before>/**
 * @class
 * @constructor
 * @description The flamingo Tool Class
 **/
Ext.define("viewer.viewercontroller.flamingo.FlamingoTool",{
    extend: "viewer.viewercontroller.controller.Tool",
    enabledEvents: null,
    config: {
        width: null,
        height: null,
        left: null,
        right: null,
        top: null,
        bottom: null,
        listenTo: null,
        //for measure
        decimals: null,
        magicnumber: null,
        units: null
    },
    /** Create a new FlamingoTool
     *@construct
     *@param config.id id of this object
     *@param config.type the type name of the component
     *@param config.width the width of the component
     *@param config.height the height of the component
     *@param config.left margin at the left side
     *@param config.right margin of the right side
     *@param config.bottom margin at the bottom side
     *@param config.tooltip the tool tip of this component
     *@param config.listenTo the component id to listen to
     */
    constructor : function (config){
        //replace the . for flamingo
        if (config.id){
            config.id=config.id.replace(/\./g,"_");
        }
        this.enabledEvents=new Object();
        viewer.viewercontroller.flamingo.FlamingoTool.superclass.constructor.call(this, config);
        this.initConfig(config);
        return this;
    },
    /**
     * Sets the tool visibility
     * @param visibility the visibility
     * @see MapComponent#setVisible
     */
    setVisible: function (vis){
        this.visible=vis;
        if (this.getFrameworkTool()){
            this.getFrameworkTool().callMethod(this.getId(),'setVisible',vis);
        }
    },
    /**
     * Create a xml string for this object.
     * @return string of the xml.
     */
    toXML: function (){
        var xml="<fmc:";
        xml+=this.getTagName(this.getType());
        if (this.getId()!=null)
            xml+=" id='"+this.getId()+"'";
        if (this.getWidth()!=null)
            xml+=" width='"+this.getWidth()+"'";
        if (this.getHeight()!=null)
            xml+=" height='"+this.getHeight()+"'";
        if (this.getTop()!=null)
            xml+=" top='"+this.getTop()+"'";
        if (this.getLeft()!=null)
            xml+=" left='"+this.getLeft()+"'";
        if (this.getRight()!=null)
            xml+=" right='"+this.getRight()+"'";
        if (this.getBottom()!=null)
            xml+=" bottom='"+this.getBottom()+"'";
        if (this.getDecimals()!=null)
            xml+=" decimals='"+this.getDecimals()+"'";
        if (this.getMagicnumber()!=null)
            xml+=" magicnumber='"+this.getMagicnumber()+"'";
        if (this.getUnits()!=null)
            xml+=" units='"+this.getUnits()+"'";
        if (this.getListenTo()!=null){
            xml+=" listento='"+this.getListenTo()+"'";
        }
        xml+=">";
        if (this.getTooltip()!=null)
            xml+="<string id='tooltip' en='"+this.getTooltip()+"'/>";
        xml+="</fmc:"+this.getTagName(this.getType())+">"
        return xml;
    },
    getTagName: function (toolType){
        if (toolType == viewer.viewercontroller.controller.Tool.ZOOMIN_BOX){
            return "ToolZoomin";
        }else if (toolType == viewer.viewercontroller.controller.Tool.ZOOMOUT_BOX){
            return "ToolZoomout";
        }else if (toolType == viewer.viewercontroller.controller.Tool.GET_FEATURE_INFO){
            return "ToolIdentify";
        }else if (toolType == viewer.viewercontroller.controller.Tool.PAN){
            return "ToolPan"
        }else if (toolType == viewer.viewercontroller.controller.Tool.SUPERPAN){
            return "ToolSuperPan";
        }else if (toolType == viewer.viewercontroller.controller.Tool.MEASURE){
            return "ToolMeasure";
        }else if (toolType == viewer.viewercontroller.controller.Tool.DEFAULT){
            return "ToolDefault";
        }else if (toolType == viewer.viewercontroller.controller.Tool.FULL_EXTENT){
            return "ButtonFull";
        }else if (toolType == viewer.viewercontroller.controller.Tool.NEXT_EXTENT){
            return "ButtonNext";
        }else if (toolType == viewer.viewercontroller.controller.Tool.PREVIOUS_EXTENT){
            return "ButtonPrev";
        }else{
            return null;
        }
    },

    /**
     * @see viewer.viewercontroller.controller.Tool#activate
     */
    activate: function(){
        this.getFrameworkTool().callMethod(this.getId(),"setActive",true);
    },
    /**
     * @see viewer.viewercontroller.controller.Tool#deactivate
     */
    deactivate: function(){
        this.getFrameworkTool().callMethod(this.getId(),"setActive",false);
    },
    /**
     * Overwrites the addListener function. Add's the event to allowexternalinterface of flamingo
     * so flamingo is allowed to broadcast the event.
     */
    addListener : function(event,handler,scope){
        viewer.viewercontroller.flamingo.FlamingoTool.superclass.addListener.call(this,event,handler,scope);
        //enable flamingo event broadcasting
        var flamEvent=this.mapComponent.eventList[event];
        if (flamEvent!=undefined){
            //if not enabled yet, enable
            if (this.enabledEvents[flamEvent]==undefined){
                this.getFrameworkTool().callMethod(this.mapComponent.getId(),"addAllowExternalInterface",this.getId()+"."+flamEvent);
                this.enabledEvents[flamEvent]=true;
            }
        }
    }
});<commit_msg>To activate a tool use the setTool function of the ToolGroup in flamingo. Otherwise the activated button keeps activated...<commit_after>/**
 * @class
 * @constructor
 * @description The flamingo Tool Class
 **/
Ext.define("viewer.viewercontroller.flamingo.FlamingoTool",{
    extend: "viewer.viewercontroller.controller.Tool",
    enabledEvents: null,
    config: {
        width: null,
        height: null,
        left: null,
        right: null,
        top: null,
        bottom: null,
        listenTo: null,
        //for measure
        decimals: null,
        magicnumber: null,
        units: null
    },
    /** Create a new FlamingoTool
     *@construct
     *@param config.id id of this object
     *@param config.type the type name of the component
     *@param config.width the width of the component
     *@param config.height the height of the component
     *@param config.left margin at the left side
     *@param config.right margin of the right side
     *@param config.bottom margin at the bottom side
     *@param config.tooltip the tool tip of this component
     *@param config.listenTo the component id to listen to
     */
    constructor : function (config){
        //replace the . for flamingo
        if (config.id){
            config.id=config.id.replace(/\./g,"_");
        }
        this.enabledEvents=new Object();
        viewer.viewercontroller.flamingo.FlamingoTool.superclass.constructor.call(this, config);
        this.initConfig(config);
        return this;
    },
    /**
     * Sets the tool visibility
     * @param visibility the visibility
     * @see MapComponent#setVisible
     */
    setVisible: function (vis){
        this.visible=vis;
        if (this.getFrameworkTool()){
            this.getFrameworkTool().callMethod(this.getId(),'setVisible',vis);
        }
    },
    /**
     * Create a xml string for this object.
     * @return string of the xml.
     */
    toXML: function (){
        var xml="<fmc:";
        xml+=this.getTagName(this.getType());
        if (this.getId()!=null)
            xml+=" id='"+this.getId()+"'";
        if (this.getWidth()!=null)
            xml+=" width='"+this.getWidth()+"'";
        if (this.getHeight()!=null)
            xml+=" height='"+this.getHeight()+"'";
        if (this.getTop()!=null)
            xml+=" top='"+this.getTop()+"'";
        if (this.getLeft()!=null)
            xml+=" left='"+this.getLeft()+"'";
        if (this.getRight()!=null)
            xml+=" right='"+this.getRight()+"'";
        if (this.getBottom()!=null)
            xml+=" bottom='"+this.getBottom()+"'";
        if (this.getDecimals()!=null)
            xml+=" decimals='"+this.getDecimals()+"'";
        if (this.getMagicnumber()!=null)
            xml+=" magicnumber='"+this.getMagicnumber()+"'";
        if (this.getUnits()!=null)
            xml+=" units='"+this.getUnits()+"'";
        if (this.getListenTo()!=null){
            xml+=" listento='"+this.getListenTo()+"'";
        }
        xml+=">";
        if (this.getTooltip()!=null)
            xml+="<string id='tooltip' en='"+this.getTooltip()+"'/>";
        xml+="</fmc:"+this.getTagName(this.getType())+">"
        return xml;
    },
    getTagName: function (toolType){
        if (toolType == viewer.viewercontroller.controller.Tool.ZOOMIN_BOX){
            return "ToolZoomin";
        }else if (toolType == viewer.viewercontroller.controller.Tool.ZOOMOUT_BOX){
            return "ToolZoomout";
        }else if (toolType == viewer.viewercontroller.controller.Tool.GET_FEATURE_INFO){
            return "ToolIdentify";
        }else if (toolType == viewer.viewercontroller.controller.Tool.PAN){
            return "ToolPan"
        }else if (toolType == viewer.viewercontroller.controller.Tool.SUPERPAN){
            return "ToolSuperPan";
        }else if (toolType == viewer.viewercontroller.controller.Tool.MEASURE){
            return "ToolMeasure";
        }else if (toolType == viewer.viewercontroller.controller.Tool.DEFAULT){
            return "ToolDefault";
        }else if (toolType == viewer.viewercontroller.controller.Tool.FULL_EXTENT){
            return "ButtonFull";
        }else if (toolType == viewer.viewercontroller.controller.Tool.NEXT_EXTENT){
            return "ButtonNext";
        }else if (toolType == viewer.viewercontroller.controller.Tool.PREVIOUS_EXTENT){
            return "ButtonPrev";
        }else{
            return null;
        }
    },

    /**
     * @see viewer.viewercontroller.controller.Tool#activate
     */
    activate: function(){
        this.getFrameworkTool().callMethod(this.viewerController.mapComponent.toolGroupId,"setTool",this.getId());
    },
    /**
     * @see viewer.viewercontroller.controller.Tool#deactivate
     */
    deactivate: function(){
        this.getFrameworkTool().callMethod(this.getId(),"setActive",false);
    },
    /**
     * Overwrites the addListener function. Add's the event to allowexternalinterface of flamingo
     * so flamingo is allowed to broadcast the event.
     */
    addListener : function(event,handler,scope){
        viewer.viewercontroller.flamingo.FlamingoTool.superclass.addListener.call(this,event,handler,scope);
        //enable flamingo event broadcasting
        var flamEvent=this.mapComponent.eventList[event];
        if (flamEvent!=undefined){
            //if not enabled yet, enable
            if (this.enabledEvents[flamEvent]==undefined){
                this.getFrameworkTool().callMethod(this.mapComponent.getId(),"addAllowExternalInterface",this.getId()+"."+flamEvent);
                this.enabledEvents[flamEvent]=true;
            }
        }
    }
});<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

const fs = require('fs');
const os = require('os');
const assert = require('assert');
const path = require('path');
const shell = require('shelljs');
const Promise = require('promise');
const yeoman = require('yeoman-environment');
const TerminalAdapter = require('yeoman-environment/lib/adapter');
const log = require('npmlog');
const rimraf = require('rimraf');
const semver = require('semver');
const yarn = require('./yarn');

const {
  checkDeclaredVersion,
  checkMatchingVersions,
  checkReactPeerDependency,
  checkGitAvailable,
} = require('./checks');

log.heading = 'git-upgrade';

/**
 * Promisify the callback-based shelljs function exec
 * @param logOutput If true, log the stdout of the command.
 * @returns {Promise}
 */
function exec(command, logOutput) {
  return new Promise((resolve, reject) => {
    let stderr, stdout = '';
    const child = shell.exec(command, {async: true, silent: true});

    child.stdout.on('data', data => {
      stdout += data;
      if (logOutput) {
        process.stdout.write(data);
      }
    });

    child.stderr.on('data', data => {
      stderr += data;
      process.stderr.write(data);
    });

    child.on('exit', code => {
      (code === 0)
        ? resolve(stdout)
        : reject(new Error(`Command '${command}' exited with code ${code}:
stderr: ${stderr}
stdout: ${stdout}`));
    });
  });
}

function parseJsonFile(path, useYarn) {
  const installHint = useYarn ?
    'Make sure you ran "yarn" and that you are inside a React Native project.' :
    'Make sure you ran "npm install" and that you are inside a React Native project.';
  let fileContents;
  try {
    fileContents = fs.readFileSync(path, 'utf8');
  } catch (err) {
    throw new Error('Cannot find "' + path + '". ' + installHint);
  }
  try {
    return JSON.parse(fileContents);
  } catch (err) {
    throw new Error('Cannot parse "' + path + '": ' + err.message);
  }
}

function readPackageFiles(useYarn) {
  const reactNativeNodeModulesPakPath = path.resolve(
    process.cwd(), 'node_modules', 'react-native', 'package.json'
  );
  const reactNodeModulesPakPath = path.resolve(
    process.cwd(), 'node_modules', 'react', 'package.json'
  );
  const pakPath = path.resolve(
    process.cwd(), 'package.json'
  );
  return {
    reactNativeNodeModulesPak: parseJsonFile(reactNativeNodeModulesPakPath),
    reactNodeModulesPak: parseJsonFile(reactNodeModulesPakPath),
    pak: parseJsonFile(pakPath)
  };
}

function parseInformationJsonOutput(jsonOutput, requestedVersion) {
  try {
    const output = JSON.parse(jsonOutput);
    const newVersion = output.version;
    const peerDependencies = output.peerDependencies;
    const newReactVersionRange = peerDependencies.react;

    assert(semver.valid(newVersion));

    return {newVersion, newReactVersionRange};
  } catch (err) {
    throw new Error(
      'The specified version of React Native ' + requestedVersion + ' doesn\'t exist.\n' +
      'Re-run the react-native-git-upgrade command with an existing version,\n' +
      'for example: "react-native-git-upgrade 0.38.0",\n' +
      'or without arguments to upgrade to the latest: "react-native-git-upgrade".'
    );
  }
}

function setupWorkingDir(tmpDir) {
  return new Promise((resolve, reject) => {
    rimraf(tmpDir, err => {
      if (err) {
        reject(err);
      } else {
        fs.mkdirSync(tmpDir);
        resolve();
      }
    });
  });
}

function configureGitEnv(tmpDir) {
  /*
   * The workflow inits a temporary Git repository. We don't want to interfere
   * with an existing user's Git repository.
   * Thanks to Git env vars, we can make Git use a different directory for its ".git" folder.
   * See https://git-scm.com/book/tr/v2/Git-Internals-Environment-Variables
   */
  process.env.GIT_DIR = path.resolve(tmpDir, '.gitrn');
  process.env.GIT_WORK_TREE = '.';
}

function generateTemplates(generatorDir, appName, verbose) {
  try {
    const yeomanGeneratorEntryPoint = path.resolve(generatorDir, 'index.js');
    // Try requiring the index.js (entry-point of Yeoman generators)
    fs.accessSync(yeomanGeneratorEntryPoint);
    return runYeomanGenerators(generatorDir, appName, verbose);
  } catch (err) {
    return runCopyAndReplace(generatorDir, appName);
  }
}

function runCopyAndReplace(generatorDir, appName) {
  const copyProjectTemplateAndReplacePath = path.resolve(generatorDir, 'copyProjectTemplateAndReplace');
  /*
   * This module is required twice during the process: for both old and new version
   * of React Native.
   * This file could have changed between these 2 versions. When generating the new template,
   * we don't want to load the old version of the generator from the cache
   */
  delete require.cache[require.resolve(copyProjectTemplateAndReplacePath)];
  const copyProjectTemplateAndReplace = require(copyProjectTemplateAndReplacePath);
  copyProjectTemplateAndReplace(
    path.resolve(generatorDir, '..', 'templates', 'HelloWorld'),
    process.cwd(),
    appName,
    {upgrade: true, force: true}
  );
}

function runYeomanGenerators(generatorDir, appName, verbose) {
  if (!verbose) {
    // Yeoman output needs monkey-patching (no silent option)
    TerminalAdapter.prototype.log = () => {};
    TerminalAdapter.prototype.log.force = () => {};
    TerminalAdapter.prototype.log.create = () => {};
  }

  const env = yeoman.createEnv();
  env.register(generatorDir, 'react:app');
  const generatorArgs = ['react:app', appName];
  return new Promise((resolve) => env.run(generatorArgs, {upgrade: true, force: true}, resolve));
}

/**
 * If there's a newer version of react-native-git-upgrade in npm, suggest to the user to upgrade.
 */
async function checkForUpdates() {
  try {
    log.info('Check for updates');
    const lastGitUpgradeVersion = await exec('npm view react-native-git-upgrade@latest version');
    const current = require('./package').version;
    const latest = semver.clean(lastGitUpgradeVersion);
    if (semver.gt(latest, current)) {
      log.warn(
        'A more recent version of "react-native-git-upgrade" has been found.\n' +
        `Current: ${current}\n` +
        `Latest: ${latest}\n` +
        'Please run "npm install -g react-native-git-upgrade"'
      );
    }
  } catch (err) {
    log.warn('Check for latest version failed', err.message);
  }
}

/**
 * If true, use yarn instead of the npm client to upgrade the project.
 */
function shouldUseYarn(cliArgs, projectDir) {
  if (cliArgs && cliArgs.npm) {
    return false;
  }
  const yarnVersion = yarn.getYarnVersionIfAvailable();
  if (yarnVersion && yarn.isProjectUsingYarn(projectDir)) {
    log.info('Using yarn ' + yarnVersion);
    return true;
  }
  return false;
}

/**
 * @param requestedVersion The version argument, e.g. 'react-native-git-upgrade 0.38'.
 *                         `undefined` if no argument passed.
 * @param cliArgs Additional arguments parsed using minimist.
 */
async function run(requestedVersion, cliArgs) {
  const tmpDir = path.resolve(os.tmpdir(), 'react-native-git-upgrade');
  const generatorDir = path.resolve(process.cwd(), 'node_modules', 'react-native', 'local-cli', 'generator');
  let projectBackupCreated = false;

  try {
    await checkForUpdates();

    const useYarn = shouldUseYarn(cliArgs, path.resolve(process.cwd()));

    log.info('Read package.json files');
    const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles(useYarn);
    const appName = pak.name;
    const currentVersion = reactNativeNodeModulesPak.version;
    const currentReactVersion = reactNodeModulesPak.version;
    const declaredVersion = pak.dependencies['react-native'];
    const declaredReactVersion = pak.dependencies.react;

    const verbose = cliArgs.verbose;

    log.info('Check declared version');
    checkDeclaredVersion(declaredVersion);

    log.info('Check matching versions');
    checkMatchingVersions(currentVersion, declaredVersion, useYarn);

    log.info('Check React peer dependency');
    checkReactPeerDependency(currentVersion, declaredReactVersion);

    log.info('Check that Git is installed');
    checkGitAvailable();

    log.info('Get information from NPM registry');
    const viewCommand = 'npm view react-native@' + (requestedVersion || 'latest') + ' --json';
    const jsonOutput = await exec(viewCommand, verbose);
    const {newVersion, newReactVersionRange} = parseInformationJsonOutput(jsonOutput, requestedVersion);
    // Print which versions we're upgrading to
    log.info('Upgrading to React Native ' + newVersion + (newReactVersionRange ? ', React ' + newReactVersionRange : ''));

    log.info('Setup temporary working directory');
    await setupWorkingDir(tmpDir);

    log.info('Configure Git environment');
    configureGitEnv(tmpDir);

    log.info('Init Git repository');
    await exec('git init', verbose);

    log.info('Add all files to commit');
    await exec('git add .', verbose);

    log.info('Commit current project sources');
    await exec('git commit -m "Project snapshot"', verbose);

    log.info('Create a tag before updating sources');
    await exec('git tag project-snapshot', verbose);
    projectBackupCreated = true;

    log.info('Generate old version template');
    await generateTemplates(generatorDir, appName, verbose);

    log.info('Add updated files to commit');
    await exec('git add .', verbose);

    log.info('Commit old version template');
    await exec('git commit -m "Old version" --allow-empty', verbose);

    log.info('Install the new version');
    let installCommand;
    if (useYarn) {
      installCommand = 'yarn add';
    } else {
      installCommand = 'npm install --save --color=always';
    }
    installCommand += ' react-native@' + newVersion;
    if (newReactVersionRange && !semver.satisfies(currentReactVersion, newReactVersionRange)) {
      // Install React as well to avoid unmet peer dependency
      installCommand += ' react@' + newReactVersionRange;
    }
    await exec(installCommand, verbose);

    log.info('Generate new version template');
    await generateTemplates(generatorDir, appName, verbose);

    log.info('Add updated files to commit');
    await exec('git add .', verbose);

    log.info('Commit new version template');
    await exec('git commit -m "New version" --allow-empty', verbose);

    log.info('Generate the patch between the 2 versions');
    const diffOutput = await exec('git diff --binary --no-color HEAD~1 HEAD', verbose);

    log.info('Save the patch in temp directory');
    const patchPath = path.resolve(tmpDir, `upgrade_${currentVersion}_${newVersion}.patch`);
    fs.writeFileSync(patchPath, diffOutput);

    log.info('Reset the 2 temporary commits');
    await exec('git reset HEAD~2 --hard', verbose);

    try {
      log.info('Apply the patch');
      await exec(`git apply --3way ${patchPath}`, true);
    } catch (err) {
      log.warn(
        'The upgrade process succeeded but there might be conflicts to be resolved. ' +
        'See above for the list of files that have merge conflicts.');
    } finally {
      log.info('Upgrade done');
      if (cliArgs.verbose) {
        log.info(`Temporary working directory: ${tmpDir}`);
      }
    }

  } catch (err) {
    log.error('An error occurred during upgrade:');
    log.error(err.stack);
    if (projectBackupCreated) {
      log.error('Restore initial sources');
      await exec('git checkout project-snapshot', true);
    }
  }
}

module.exports = {
  run: run,
};
<commit_msg>Disable git hooks for the upgrade process<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

const fs = require('fs');
const os = require('os');
const assert = require('assert');
const path = require('path');
const shell = require('shelljs');
const Promise = require('promise');
const yeoman = require('yeoman-environment');
const TerminalAdapter = require('yeoman-environment/lib/adapter');
const log = require('npmlog');
const rimraf = require('rimraf');
const semver = require('semver');
const yarn = require('./yarn');

const {
  checkDeclaredVersion,
  checkMatchingVersions,
  checkReactPeerDependency,
  checkGitAvailable,
} = require('./checks');

log.heading = 'git-upgrade';

/**
 * Promisify the callback-based shelljs function exec
 * @param logOutput If true, log the stdout of the command.
 * @returns {Promise}
 */
function exec(command, logOutput) {
  return new Promise((resolve, reject) => {
    let stderr, stdout = '';
    const child = shell.exec(command, {async: true, silent: true});

    child.stdout.on('data', data => {
      stdout += data;
      if (logOutput) {
        process.stdout.write(data);
      }
    });

    child.stderr.on('data', data => {
      stderr += data;
      process.stderr.write(data);
    });

    child.on('exit', code => {
      (code === 0)
        ? resolve(stdout)
        : reject(new Error(`Command '${command}' exited with code ${code}:
stderr: ${stderr}
stdout: ${stdout}`));
    });
  });
}

function parseJsonFile(path, useYarn) {
  const installHint = useYarn ?
    'Make sure you ran "yarn" and that you are inside a React Native project.' :
    'Make sure you ran "npm install" and that you are inside a React Native project.';
  let fileContents;
  try {
    fileContents = fs.readFileSync(path, 'utf8');
  } catch (err) {
    throw new Error('Cannot find "' + path + '". ' + installHint);
  }
  try {
    return JSON.parse(fileContents);
  } catch (err) {
    throw new Error('Cannot parse "' + path + '": ' + err.message);
  }
}

function readPackageFiles(useYarn) {
  const reactNativeNodeModulesPakPath = path.resolve(
    process.cwd(), 'node_modules', 'react-native', 'package.json'
  );
  const reactNodeModulesPakPath = path.resolve(
    process.cwd(), 'node_modules', 'react', 'package.json'
  );
  const pakPath = path.resolve(
    process.cwd(), 'package.json'
  );
  return {
    reactNativeNodeModulesPak: parseJsonFile(reactNativeNodeModulesPakPath),
    reactNodeModulesPak: parseJsonFile(reactNodeModulesPakPath),
    pak: parseJsonFile(pakPath)
  };
}

function parseInformationJsonOutput(jsonOutput, requestedVersion) {
  try {
    const output = JSON.parse(jsonOutput);
    const newVersion = output.version;
    const peerDependencies = output.peerDependencies;
    const newReactVersionRange = peerDependencies.react;

    assert(semver.valid(newVersion));

    return {newVersion, newReactVersionRange};
  } catch (err) {
    throw new Error(
      'The specified version of React Native ' + requestedVersion + ' doesn\'t exist.\n' +
      'Re-run the react-native-git-upgrade command with an existing version,\n' +
      'for example: "react-native-git-upgrade 0.38.0",\n' +
      'or without arguments to upgrade to the latest: "react-native-git-upgrade".'
    );
  }
}

function setupWorkingDir(tmpDir) {
  return new Promise((resolve, reject) => {
    rimraf(tmpDir, err => {
      if (err) {
        reject(err);
      } else {
        fs.mkdirSync(tmpDir);
        resolve();
      }
    });
  });
}

function configureGitEnv(tmpDir) {
  /*
   * The workflow inits a temporary Git repository. We don't want to interfere
   * with an existing user's Git repository.
   * Thanks to Git env vars, we can make Git use a different directory for its ".git" folder.
   * See https://git-scm.com/book/tr/v2/Git-Internals-Environment-Variables
   */
  process.env.GIT_DIR = path.resolve(tmpDir, '.gitrn');
  process.env.GIT_WORK_TREE = '.';
}

function generateTemplates(generatorDir, appName, verbose) {
  try {
    const yeomanGeneratorEntryPoint = path.resolve(generatorDir, 'index.js');
    // Try requiring the index.js (entry-point of Yeoman generators)
    fs.accessSync(yeomanGeneratorEntryPoint);
    return runYeomanGenerators(generatorDir, appName, verbose);
  } catch (err) {
    return runCopyAndReplace(generatorDir, appName);
  }
}

function runCopyAndReplace(generatorDir, appName) {
  const copyProjectTemplateAndReplacePath = path.resolve(generatorDir, 'copyProjectTemplateAndReplace');
  /*
   * This module is required twice during the process: for both old and new version
   * of React Native.
   * This file could have changed between these 2 versions. When generating the new template,
   * we don't want to load the old version of the generator from the cache
   */
  delete require.cache[require.resolve(copyProjectTemplateAndReplacePath)];
  const copyProjectTemplateAndReplace = require(copyProjectTemplateAndReplacePath);
  copyProjectTemplateAndReplace(
    path.resolve(generatorDir, '..', 'templates', 'HelloWorld'),
    process.cwd(),
    appName,
    {upgrade: true, force: true}
  );
}

function runYeomanGenerators(generatorDir, appName, verbose) {
  if (!verbose) {
    // Yeoman output needs monkey-patching (no silent option)
    TerminalAdapter.prototype.log = () => {};
    TerminalAdapter.prototype.log.force = () => {};
    TerminalAdapter.prototype.log.create = () => {};
  }

  const env = yeoman.createEnv();
  env.register(generatorDir, 'react:app');
  const generatorArgs = ['react:app', appName];
  return new Promise((resolve) => env.run(generatorArgs, {upgrade: true, force: true}, resolve));
}

/**
 * If there's a newer version of react-native-git-upgrade in npm, suggest to the user to upgrade.
 */
async function checkForUpdates() {
  try {
    log.info('Check for updates');
    const lastGitUpgradeVersion = await exec('npm view react-native-git-upgrade@latest version');
    const current = require('./package').version;
    const latest = semver.clean(lastGitUpgradeVersion);
    if (semver.gt(latest, current)) {
      log.warn(
        'A more recent version of "react-native-git-upgrade" has been found.\n' +
        `Current: ${current}\n` +
        `Latest: ${latest}\n` +
        'Please run "npm install -g react-native-git-upgrade"'
      );
    }
  } catch (err) {
    log.warn('Check for latest version failed', err.message);
  }
}

/**
 * If true, use yarn instead of the npm client to upgrade the project.
 */
function shouldUseYarn(cliArgs, projectDir) {
  if (cliArgs && cliArgs.npm) {
    return false;
  }
  const yarnVersion = yarn.getYarnVersionIfAvailable();
  if (yarnVersion && yarn.isProjectUsingYarn(projectDir)) {
    log.info('Using yarn ' + yarnVersion);
    return true;
  }
  return false;
}

/**
 * @param requestedVersion The version argument, e.g. 'react-native-git-upgrade 0.38'.
 *                         `undefined` if no argument passed.
 * @param cliArgs Additional arguments parsed using minimist.
 */
async function run(requestedVersion, cliArgs) {
  const tmpDir = path.resolve(os.tmpdir(), 'react-native-git-upgrade');
  const generatorDir = path.resolve(process.cwd(), 'node_modules', 'react-native', 'local-cli', 'generator');
  let projectBackupCreated = false;

  try {
    await checkForUpdates();

    const useYarn = shouldUseYarn(cliArgs, path.resolve(process.cwd()));

    log.info('Read package.json files');
    const {reactNativeNodeModulesPak, reactNodeModulesPak, pak} = readPackageFiles(useYarn);
    const appName = pak.name;
    const currentVersion = reactNativeNodeModulesPak.version;
    const currentReactVersion = reactNodeModulesPak.version;
    const declaredVersion = pak.dependencies['react-native'];
    const declaredReactVersion = pak.dependencies.react;

    const verbose = cliArgs.verbose;

    log.info('Check declared version');
    checkDeclaredVersion(declaredVersion);

    log.info('Check matching versions');
    checkMatchingVersions(currentVersion, declaredVersion, useYarn);

    log.info('Check React peer dependency');
    checkReactPeerDependency(currentVersion, declaredReactVersion);

    log.info('Check that Git is installed');
    checkGitAvailable();

    log.info('Get information from NPM registry');
    const viewCommand = 'npm view react-native@' + (requestedVersion || 'latest') + ' --json';
    const jsonOutput = await exec(viewCommand, verbose);
    const {newVersion, newReactVersionRange} = parseInformationJsonOutput(jsonOutput, requestedVersion);
    // Print which versions we're upgrading to
    log.info('Upgrading to React Native ' + newVersion + (newReactVersionRange ? ', React ' + newReactVersionRange : ''));

    log.info('Setup temporary working directory');
    await setupWorkingDir(tmpDir);

    log.info('Configure Git environment');
    configureGitEnv(tmpDir);

    log.info('Init Git repository');
    await exec('git init', verbose);

    log.info('Add all files to commit');
    await exec('git add .', verbose);

    log.info('Commit current project sources');
    await exec('git commit -m "Project snapshot" --no-verify', verbose);

    log.info('Create a tag before updating sources');
    await exec('git tag project-snapshot', verbose);
    projectBackupCreated = true;

    log.info('Generate old version template');
    await generateTemplates(generatorDir, appName, verbose);

    log.info('Add updated files to commit');
    await exec('git add .', verbose);

    log.info('Commit old version template');
    await exec('git commit -m "Old version" --allow-empty --no-verify', verbose);

    log.info('Install the new version');
    let installCommand;
    if (useYarn) {
      installCommand = 'yarn add';
    } else {
      installCommand = 'npm install --save --color=always';
    }
    installCommand += ' react-native@' + newVersion;
    if (newReactVersionRange && !semver.satisfies(currentReactVersion, newReactVersionRange)) {
      // Install React as well to avoid unmet peer dependency
      installCommand += ' react@' + newReactVersionRange;
    }
    await exec(installCommand, verbose);

    log.info('Generate new version template');
    await generateTemplates(generatorDir, appName, verbose);

    log.info('Add updated files to commit');
    await exec('git add .', verbose);

    log.info('Commit new version template');
    await exec('git commit -m "New version" --allow-empty --no-verify', verbose);

    log.info('Generate the patch between the 2 versions');
    const diffOutput = await exec('git diff --binary --no-color HEAD~1 HEAD', verbose);

    log.info('Save the patch in temp directory');
    const patchPath = path.resolve(tmpDir, `upgrade_${currentVersion}_${newVersion}.patch`);
    fs.writeFileSync(patchPath, diffOutput);

    log.info('Reset the 2 temporary commits');
    await exec('git reset HEAD~2 --hard', verbose);

    try {
      log.info('Apply the patch');
      await exec(`git apply --3way ${patchPath}`, true);
    } catch (err) {
      log.warn(
        'The upgrade process succeeded but there might be conflicts to be resolved. ' +
        'See above for the list of files that have merge conflicts.');
    } finally {
      log.info('Upgrade done');
      if (cliArgs.verbose) {
        log.info(`Temporary working directory: ${tmpDir}`);
      }
    }

  } catch (err) {
    log.error('An error occurred during upgrade:');
    log.error(err.stack);
    if (projectBackupCreated) {
      log.error('Restore initial sources');
      await exec('git checkout project-snapshot --no-verify', true);
    }
  }
}

module.exports = {
  run: run,
};
<|endoftext|>