<commit_before>import './styles.scss';

import { clamp } from 'utils';
import { Component } from 'react';
import { on, off } from 'dom-events';
import debounce from 'lodash.debounce';
import SplitText from 'vendors/splitText.js';

/**
 * HomeSlider class
 */
class HomeSlider extends Component {

  state = {
    canDrag: false
  }

  componentWillMount() {
    this.debounceWindowResize = debounce( this.onWindowResize, 200 );
  }

  componentDidMount() {
    this.generateTimelineMax();
    this.generateRepeatTimelineMax();
    this.enterAnimation();

    this.svg = {
      offsetTop: this.refs.svg.getBoundingClientRect().top + 10,
      height: this.refs.svg.getBoundingClientRect().height,
      dragProgression: 0,
      dragComplete: false
    };

    this.onMouseUp = ::this.onMouseUp;
    this.onMouseMove = ::this.onMouseMove;
    this.debounceWindowResize = ::this.debounceWindowResize;

    this.svg.offsetTop = this.refs.svg.getBoundingClientRect().top + 10;
  }

  componentWillUnmount() {
    this.removeListeners();
    this.loadingAnimTl.clear();
  }

  addListeners() {
    on( document, 'mouseup', this.onMouseUp );
    on( document, 'mousemove', this.onMouseMove );
    on( window, 'resize', this.debounceWindowResize );
  }

  removeListeners() {
    off( document, 'mouseup', this.onMouseUp );
    off( document, 'mousemove', this.onMouseMove );
    off( window, 'resize', this.debounceWindowResize );
  }

  enterAnimation() {

    this.enterTl = new TimelineMax({
      onComplete: ()=> {
        this.svg.offsetTop = this.refs.svg.getBoundingClientRect().top + 10;
        this.addListeners();
        this.innerCircleLoopTl.play();
        this.bigCircleLoopTl.play();
      }
    });

    const enterTlConfig = {
      progress: 1
    };

    this.grabberDragTl.progress( 1 );

    this.enterTl
      .from( this.refs.bigCircle, 1.5, { opacity: 0, y: '100%', scale: 0.8, ease: Expo.easeOut }, 1 )
      .from( this.refs.innerGrabberCircle, 0.5, { opacity: 0, ease: Expo.easeOut }, "-=0.4" )
      .from( this.refs.outerGrabberCircle, 0.5, { opacity: 0, ease: Expo.easeOut }, "-=0.3" )
      .fromTo( enterTlConfig, 1, { progress: 1 }, { progress: 0, ease: Expo.easeOut, onUpdate: () => {
        this.grabberDragTl.progress( enterTlConfig.progress );
      } })
      .staggerFromTo( this.instructionsSplited.chars, 0.5, { opacity: 0, scale: 0.8, y: '60%' }, { opacity: 1, scale: 1, y: '0%', ease: Back.easeOut.config( 3 ) }, 0.1, 0.5 );

  }

  generateRepeatTimelineMax() {

    this.innerCircleLoopTl = new TimelineMax({ paused: true, repeat: -1, yoyo: true });
    this.bigCircleLoopTl = new TimelineMax({ paused: true, repeat: -1 });

    this.innerCircleLoopTl.fromTo( this.refs.innerGrabberCircle, 0.8, { transformOrigin: "center center", scale: 1 }, { scale: 0.95, ease: Expo.easeOut });
    this.bigCircleLoopTl.fromTo( this.refs.bigCircle, 15, { transformOrigin: "center center", rotation: 0 }, { rotation: 360 });

  }

  generateTimelineMax() {

    this.instructionsSplited = new SplitText( this.refs.instructions, {
      type: 'chars'
    });

    this.loadingSplited = new SplitText( this.refs.loadingMsg, {
      type: 'chars'
    });

    this.instructionsSplited.reverseChars = this.instructionsSplited.chars.slice( 0 ).reverse();

    this.loadingAnimTl = new TimelineMax({ paused: true, repeat: -1 });

    this.grabberPressTl = new TimelineMax({
      paused: true,
      onReverseComplete: ()=> {
        this.innerCircleLoopTl.play( 0 );
      }
    });

    this.grabberDragTl = new TimelineMax({ paused: true });

    this.loadingAnimTl
      .fromTo( this.refs.loadingMsg, 0.5, { opacity: 0 }, { opacity: 1, ease: Expo.easeOut })
      .staggerTo( this.loadingSplited.chars, 0.7, { opacity: 0, scale: 0.6, y: '-30%', ease: Back.easeOut.config( 3 ) }, 0.1 );

    this.exitDragAnimationTl = new TimelineMax({
      paused: true,
      onComplete: ()=> {
        this.svg.dragComplete = true;
        this.grabberDragTl.progress( 1 );
        this.props.onDragComplete();

        TweenMax.to( this.refs.slider, 4, { top: '165vh', ease: Expo.easeOut, onComplete: ()=> {

          this.grabberDragTl.progress( 1 );
          this.removeListeners();

          this.refs.instructions.style.display = 'none';
          this.refs.loadingMsg.style.display= 'block';

          this.loadingAnimTl.play();
        } });

      }
    });

    this.grabberPressTl
      .fromTo( this.refs.innerGrabberCircle, 0.2, { scale: 1, transformOrigin: "center center" }, { scale: 0.8, stroke: '#61DAFF', ease: Back.easeOut })
      .to( this.refs.bigCircle, 0.2, { stroke: '#61DAFF', ease: Expo.easeOut }, 0 );

    this.grabberDragTl
      .to( this.refs.grabber, 1, { y: '312%' }, 0 )
      .fromTo( this.refs.line, 0.6, { scaleY: 1, transformOrigin: "bottom" }, { scaleY: 0 }, 0 )
      .fromTo( this.refs.bigCircle, 0.4, { transformOrigin: "center center" }, { scale: 1.15, ease: Expo.easeOut }, 0.5 );

    this.exitDragAnimationTl
      .staggerTo( this.instructionsSplited.reverseChars, 1, { opacity: 0, scale: 0.6, y: '-60%', ease: Back.easeOut.config( 3 ) }, 0.1 );

  }

  /**
   *  ---- Events ----
   */

  onWindowResize() {
    this.svg.offsetTop = this.refs.svg.getBoundingClientRect().top + 10;
  }

  onMouseUp() {

    if( this.state.canDrag ) {
      this.setState({
        canDrag: false
      });
    }

    if( !this.svg.dragComplete ) {
      TweenMax.to( this.svg, 1, { dragProgression: 0, ease: Expo.easeOut, onUpdate: () => {
        this.updateTimeline();
      } });

      this.grabberPressTl.reverse();
    } else {
      this.svg.dragProgression = 1;
      this.props.onProgress( this.svg.dragProgression );
    }

  }

  onGrabberMouseDown() {
    if( this.state.canDrag ) return;

    this.setState({
      canDrag: true
    });

    this.grabberPressTl.play();
  }

  onMouseMove( ev ) {

    if( this.state.canDrag ) {
      const val = ( ev.clientY - this.svg.offsetTop ) / this.svg.height;
      this.svg.dragProgression = clamp( 0, 1, val );
    }

    this.updateTimeline();
  }

  updateTimeline() {
    this.props.onProgress( this.svg.dragProgression );
    this.grabberDragTl.progress( this.svg.dragProgression );
    this.exitDragAnimationTl.progress( this.svg.dragProgression );
  }

  /**
   *  ---- Grabber events ----
   */

  onGrabberMouseEnter() {
    this.innerCircleLoopTl.pause();

  }

  onGrabberMouseLeave() {

  }

  render() {

    return (

      <div
        className="home-slider"
        ref="slider"
      >

        <svg
          className="home-slider__svg"
          ref="svg"
          viewBox="0 0 39.5 137.3"
        >

          <line
            className="home-slider__svg-line"
            ref="line"
            x1="19.6"
            y1="98.2"
            x2="19.6"
            y2="34.2"
          />

          <circle
            className="home-slider__svg-big-circle"
            ref="bigCircle"
            cx="19.7"
            cy="117.2"
            r="19.2"
          />

          <g
            className="home-slider__grabber"
            ref="grabber"
            onMouseEnter={::this.onGrabberMouseEnter}
            onMouseLeave={::this.onGrabberMouseLeave}
            onMouseDown={::this.onGrabberMouseDown}
          >

            <circle
              className="home-slider__svg-inner-grabber"
              ref="innerGrabberCircle"
              cx="19.5"
              cy="17.2"
              r="11.1"
            />

            <circle
              className="home-slider__svg-outer-grabber"
              ref="outerGrabberCircle"
              cx="19.7"
              cy="17.2"
              r="16"
            />

          </g>

        </svg>

        <p
          className="home-slider__instructions"
          ref="instructions"
        >
          Drag to dive
        </p>
        <p
          className="home-slider__loading-message"
          ref="loadingMsg"
        >
          Loading
        </p>

      </div>

    );
  }
}

export default HomeSlider;
<commit_msg>HomeSlider - Fix disappear bug during home transition<commit_after>import './styles.scss';

import { clamp } from 'utils';
import { Component } from 'react';
import { on, off } from 'dom-events';
import debounce from 'lodash.debounce';
import SplitText from 'vendors/splitText.js';

/**
 * HomeSlider class
 */
class HomeSlider extends Component {

  state = {
    canDrag: false
  }

  componentWillMount() {
    this.debounceWindowResize = debounce( this.onWindowResize, 200 );
  }

  componentDidMount() {
    this.generateTimelineMax();
    this.generateRepeatTimelineMax();
    this.enterAnimation();

    this.svg = {
      offsetTop: this.refs.svg.getBoundingClientRect().top + 10,
      height: this.refs.svg.getBoundingClientRect().height,
      dragProgression: 0,
      dragComplete: false
    };

    this.onMouseUp = ::this.onMouseUp;
    this.onMouseMove = ::this.onMouseMove;
    this.debounceWindowResize = ::this.debounceWindowResize;

    this.svg.offsetTop = this.refs.svg.getBoundingClientRect().top + 10;
  }

  componentWillUnmount() {
    this.removeListeners();
    this.loadingAnimTl.clear();
  }

  addListeners() {
    on( document, 'mouseup', this.onMouseUp );
    on( document, 'mousemove', this.onMouseMove );
    on( window, 'resize', this.debounceWindowResize );
  }

  removeListeners() {
    off( document, 'mouseup', this.onMouseUp );
    off( document, 'mousemove', this.onMouseMove );
    off( window, 'resize', this.debounceWindowResize );
  }

  enterAnimation() {

    this.enterTl = new TimelineMax({
      onComplete: ()=> {
        this.svg.offsetTop = this.refs.svg.getBoundingClientRect().top + 10;
        this.addListeners();
        this.innerCircleLoopTl.play();
        this.bigCircleLoopTl.play();
      }
    });

    const enterTlConfig = {
      progress: 1
    };

    this.grabberDragTl.progress( 1 );

    this.enterTl
      .from( this.refs.bigCircle, 1.5, { opacity: 0, y: '100%', scale: 0.8, ease: Expo.easeOut }, 1 )
      .from( this.refs.innerGrabberCircle, 0.5, { opacity: 0, ease: Expo.easeOut }, "-=0.4" )
      .from( this.refs.outerGrabberCircle, 0.5, { opacity: 0, ease: Expo.easeOut }, "-=0.3" )
      .fromTo( enterTlConfig, 1, { progress: 1 }, { progress: 0, ease: Expo.easeOut, onUpdate: () => {
        this.grabberDragTl.progress( enterTlConfig.progress );
      } })
      .staggerFromTo( this.instructionsSplited.chars, 0.5, { opacity: 0, scale: 0.8, y: '60%' }, { opacity: 1, scale: 1, y: '0%', ease: Back.easeOut.config( 3 ) }, 0.1, 0.5 );

  }

  generateRepeatTimelineMax() {

    this.innerCircleLoopTl = new TimelineMax({ paused: true, repeat: -1, yoyo: true });
    this.bigCircleLoopTl = new TimelineMax({ paused: true, repeat: -1 });

    this.innerCircleLoopTl.fromTo( this.refs.innerGrabberCircle, 0.8, { transformOrigin: "center center", scale: 1 }, { scale: 0.95, ease: Expo.easeOut });
    this.bigCircleLoopTl.fromTo( this.refs.bigCircle, 15, { transformOrigin: "center center", rotation: 0 }, { rotation: 360 });

  }

  generateTimelineMax() {

    this.instructionsSplited = new SplitText( this.refs.instructions, {
      type: 'chars'
    });

    this.loadingSplited = new SplitText( this.refs.loadingMsg, {
      type: 'chars'
    });

    this.instructionsSplited.reverseChars = this.instructionsSplited.chars.slice( 0 ).reverse();

    this.loadingAnimTl = new TimelineMax({ paused: true, repeat: -1 });

    this.grabberPressTl = new TimelineMax({
      paused: true,
      onReverseComplete: ()=> {
        this.innerCircleLoopTl.play( 0 );
      }
    });

    this.grabberDragTl = new TimelineMax({ paused: true });

    this.loadingAnimTl
      .fromTo( this.refs.loadingMsg, 0.5, { opacity: 0 }, { opacity: 1, ease: Expo.easeOut })
      .staggerTo( this.loadingSplited.chars, 0.7, { opacity: 0, scale: 0.6, y: '-30%', ease: Back.easeOut.config( 3 ) }, 0.1 );

    this.exitDragAnimationTl = new TimelineMax({
      paused: true,
      onComplete: ()=> {
        this.svg.dragComplete = true;
        this.grabberDragTl.progress( 1 );
        this.props.onDragComplete();

        TweenMax.to( this.refs.slider, 4, { top: '165vh', ease: Expo.easeOut, onComplete: ()=> {

          this.grabberDragTl.progress( 1 );
          this.removeListeners();

          this.refs.slider.style.top = '65vh';
          this.refs.instructions.style.display = 'none';
          this.refs.loadingMsg.style.display= 'block';

          this.loadingAnimTl.play();
        } });

      }
    });

    this.grabberPressTl
      .fromTo( this.refs.innerGrabberCircle, 0.2, { scale: 1, transformOrigin: "center center" }, { scale: 0.8, stroke: '#61DAFF', ease: Back.easeOut })
      .to( this.refs.bigCircle, 0.2, { stroke: '#61DAFF', ease: Expo.easeOut }, 0 );

    this.grabberDragTl
      .to( this.refs.grabber, 1, { y: '312%' }, 0 )
      .fromTo( this.refs.line, 0.6, { scaleY: 1, transformOrigin: "bottom" }, { scaleY: 0 }, 0 )
      .fromTo( this.refs.bigCircle, 0.4, { transformOrigin: "center center" }, { scale: 1.15, ease: Expo.easeOut }, 0.5 );

    this.exitDragAnimationTl
      .staggerTo( this.instructionsSplited.reverseChars, 1, { opacity: 0, scale: 0.6, y: '-60%', ease: Back.easeOut.config( 3 ) }, 0.1 );

  }

  /**
   *  ---- Events ----
   */

  onWindowResize() {
    this.svg.offsetTop = this.refs.svg.getBoundingClientRect().top + 10;
  }

  onMouseUp() {

    if( this.state.canDrag ) {
      this.setState({
        canDrag: false
      });
    }

    if( !this.svg.dragComplete ) {
      TweenMax.to( this.svg, 1, { dragProgression: 0, ease: Expo.easeOut, onUpdate: () => {
        this.updateTimeline();
      } });

      this.grabberPressTl.reverse();
    } else {
      this.svg.dragProgression = 1;
      this.props.onProgress( this.svg.dragProgression );
    }

  }

  onGrabberMouseDown() {
    if( this.state.canDrag ) return;

    this.setState({
      canDrag: true
    });

    this.grabberPressTl.play();
  }

  onMouseMove( ev ) {

    if( this.state.canDrag ) {
      const val = ( ev.clientY - this.svg.offsetTop ) / this.svg.height;
      this.svg.dragProgression = clamp( 0, 1, val );
    }

    this.updateTimeline();
  }

  updateTimeline() {
    this.props.onProgress( this.svg.dragProgression );
    this.grabberDragTl.progress( this.svg.dragProgression );
    this.exitDragAnimationTl.progress( this.svg.dragProgression );
  }

  /**
   *  ---- Grabber events ----
   */

  onGrabberMouseEnter() {
    this.innerCircleLoopTl.pause();

  }

  onGrabberMouseLeave() {

  }

  render() {

    return (

      <div
        className="home-slider"
        ref="slider"
      >

        <svg
          className="home-slider__svg"
          ref="svg"
          viewBox="0 0 39.5 137.3"
        >

          <line
            className="home-slider__svg-line"
            ref="line"
            x1="19.6"
            y1="98.2"
            x2="19.6"
            y2="34.2"
          />

          <circle
            className="home-slider__svg-big-circle"
            ref="bigCircle"
            cx="19.7"
            cy="117.2"
            r="19.2"
          />

          <g
            className="home-slider__grabber"
            ref="grabber"
            onMouseEnter={::this.onGrabberMouseEnter}
            onMouseLeave={::this.onGrabberMouseLeave}
            onMouseDown={::this.onGrabberMouseDown}
          >

            <circle
              className="home-slider__svg-inner-grabber"
              ref="innerGrabberCircle"
              cx="19.5"
              cy="17.2"
              r="11.1"
            />

            <circle
              className="home-slider__svg-outer-grabber"
              ref="outerGrabberCircle"
              cx="19.7"
              cy="17.2"
              r="16"
            />

          </g>

        </svg>

        <p
          className="home-slider__instructions"
          ref="instructions"
        >
          Drag to dive
        </p>
        <p
          className="home-slider__loading-message"
          ref="loadingMsg"
        >
          Loading
        </p>

      </div>

    );
  }
}

export default HomeSlider;
<|endoftext|>


<commit_before>/*jslint nomen: true, browser: true*/
/*global define*/
define([
    'jquery',
    'underscore',
    'd3'
], function ($, _, d3) {
    'use strict';

    function drawDag(links, domElement) {
        var colorGen, nodes, w, h, force, path, linkLabel, svg, circle, text;

        // Use elliptical arc path segments to doubly-encode directionality.
        function tick() {
            path.attr("d", function (d) {
                var num, dx, dy, dr, a, da, b;

                dx = d.target.x - d.source.x;
                dy = d.target.y - d.source.y;
                dr = Math.sqrt(dx * dx + dy * dy);
                num = d.type;

                if (d.target.name === d.source.name) {
                    a = Math.atan2(dx, dy);
                    da = 0.4;
                    b = 1;
                    return "M" + d.target.x + "," + d.target.y + "q0,45 30,30"
                        //"q" + b*Math.sin(a) + "," + b*Math.cos(a) + " " + b*Math.sin(a+da) + "," + b*Math.cos(a+da)
                        + " " + " T " + d.target.x + "," + d.target.y;
                }

                // TODO: don't hardcode '2'
                // multiple edges from one node for multiple tags
                if (num === '2') {
                    return "M" + d.source.x + "," + d.source.y + "A" + dr + ","
                        + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
                }

                // TODO: don't use num
                return "M" + d.source.x + "," + d.source.y +
                    "q0," + 5 * num + " " + 5 * num + ",0 " +
                    " T " + d.target.x + "," + d.target.y;

            });

            circle.attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

            text.attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
        }

        // green, blue, orange, pink, teal, red,
        colorGen = _.generator(['#009933', '#0000FF', '#FF9933', '#FF4422', '#00FFFF', '#FF0000']);

        // Select all instances of a process (the nodes/circles) in the tree on hover
        // Give selections with different tids different colors
        function addSelection(p) {
            d3.select(domElement).selectAll("circle").style("fill", function (d, i) {
                if (p.pname === d.pname) {
                    return colorGen.getWith(i);
                }
                d3.select(this).style('opacity', '0.15');
                return 'black';
            });
        }

        function removeSelection() {
            d3.select(domElement).selectAll("circle")
                .style("fill", function (d) {
                    return "white";
                })
                .style('opacity', '1');
        }

        nodes = {};

        // Compute the distinct nodes from the links.
        _.each(links, function (link) {
            if (nodes[link.source]) {
                link.source = nodes[link.source];
            } else {
                link.source = nodes[link.source] = {
                    name: link.source,
                    pname: link.sourceName
                };
            }

            if (nodes[link.target]) {
                link.target = nodes[link.target];
            } else {
                link.target = nodes[link.target] = {
                    name: link.target,
                    pname: link.targetName
                };
            }
        });

        w = 860;
        h = 600;

        force = d3.layout.force()
            .nodes(d3.values(nodes))
            .links(links)
            .size([w, h])           // available layout size - affects the gravitational center & init rand pos
            .linkDistance(267)
            .linkStrength(0.30)
            .charge(-150)            // -ve: node repultion; +ve: node attraction
            .friction(0.1)
            .gravity(0.02)
            .on("tick", tick)
            .start();

        svg = d3.select(domElement).append("svg:svg")
            .attr("width", w)
            .attr("height", h);

        // TODO: make marker color correspond to edge color
        svg.append("svg:defs").selectAll("marker")
                .data(["dir"])
            .enter().append("svg:marker")
                .attr("id", String)
                .attr("viewBox", "0 -5 10 10")
                .attr("refX", 15)
                .attr("refY", -1.5)
                .attr("markerWidth", 6)
                .attr("markerHeight", 6)
                .attr("orient", "auto")
            .append("svg:path")
                .attr("d", "M0,-5L10,0L0,5");

        path = svg.append("svg:g").selectAll("path")
            .data(force.links())
            .enter().append("svg:path")
            .attr("class", function (d) { return "link tag" + d.type; })
            .attr("marker-end", function (d) { return "url(#dir)"; })
            .attr("id", function (d, i) { return "p" + i; });

        linkLabel = svg.append("svg:g")
            .selectAll("text")
            .data(force.links())
            .enter().append("svg:text")
            .attr("class", function (d) {
                return "link-label-tag" + d.type;
            })
            .attr("font-size", 10)
            .attr("text-anchor", "middle")
            .append("svg:textPath")
            .attr("startOffset", "50%")
            .attr("xlink:xlink:href", function (d, i) {
                return "#p" + i;
            })
            .text(function (d) { return d.occurrenceNumber; });

        circle = svg.append("svg:g").selectAll("circle")
            .data(force.nodes())
            .enter().append("svg:circle")
            .attr("r", 6)
            .call(force.drag)
            .on("mouseover", addSelection)
            .on("mouseout", removeSelection);

        text = svg.append("svg:g").selectAll("text")
            .data(force.nodes())
            .enter().append("svg:text")
            .attr("x", 10)
            .attr("y", ".35em")
            .text(function (d) { return d.pname; });

    }

    return { draw: drawDag };

});

<commit_msg>change highlight color to blue only for dag<commit_after>/*jslint nomen: true, browser: true*/
/*global define*/
define([
    'jquery',
    'underscore',
    'd3'
], function ($, _, d3) {
    'use strict';

    function drawDag(links, domElement) {
        var colorGen, nodes, w, h, force, path, linkLabel, svg, circle, text;

        // Use elliptical arc path segments to doubly-encode directionality.
        function tick() {
            path.attr("d", function (d) {
                var num, dx, dy, dr, a, da, b;

                dx = d.target.x - d.source.x;
                dy = d.target.y - d.source.y;
                dr = Math.sqrt(dx * dx + dy * dy);
                num = d.type;

                if (d.target.name === d.source.name) {
                    a = Math.atan2(dx, dy);
                    da = 0.4;
                    b = 1;
                    return "M" + d.target.x + "," + d.target.y + "q0,45 30,30"
                        //"q" + b*Math.sin(a) + "," + b*Math.cos(a) + " " + b*Math.sin(a+da) + "," + b*Math.cos(a+da)
                        + " " + " T " + d.target.x + "," + d.target.y;
                }

                // TODO: don't hardcode '2'
                // multiple edges from one node for multiple tags
                if (num === '2') {
                    return "M" + d.source.x + "," + d.source.y + "A" + dr + ","
                        + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
                }

                // TODO: don't use num
                return "M" + d.source.x + "," + d.source.y +
                    "q0," + 5 * num + " " + 5 * num + ",0 " +
                    " T " + d.target.x + "," + d.target.y;

            });

            circle.attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

            text.attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
        }

        // green, blue, orange, pink, teal, red,
        //colorGen = _.generator(['#009933', '#0000FF', '#FF9933', '#FF4422', '#00FFFF', '#FF0000']);
        // blue
        colorGen = _.generator(['#0000FF']);

        // Select all instances of a process (the nodes/circles) in the tree on hover
        // Give selections with different tids different colors
        function addSelection(p) {
            d3.select(domElement).selectAll("circle").style("fill", function (d, i) {
                if (p.pname === d.pname) {
                    return colorGen.getWith(i);
                }
                d3.select(this).style('opacity', '0.15');
                return 'black';
            });
        }

        function removeSelection() {
            d3.select(domElement).selectAll("circle")
                .style("fill", function (d) {
                    return "white";
                })
                .style('opacity', '1');
        }

        nodes = {};

        // Compute the distinct nodes from the links.
        _.each(links, function (link) {
            if (nodes[link.source]) {
                link.source = nodes[link.source];
            } else {
                link.source = nodes[link.source] = {
                    name: link.source,
                    pname: link.sourceName
                };
            }

            if (nodes[link.target]) {
                link.target = nodes[link.target];
            } else {
                link.target = nodes[link.target] = {
                    name: link.target,
                    pname: link.targetName
                };
            }
        });

        w = 860;
        h = 600;

        force = d3.layout.force()
            .nodes(d3.values(nodes))
            .links(links)
            .size([w, h])           // available layout size - affects the gravitational center & init rand pos
            .linkDistance(267)
            .linkStrength(0.30)
            .charge(-150)            // -ve: node repultion; +ve: node attraction
            .friction(0.1)
            .gravity(0.02)
            .on("tick", tick)
            .start();

        svg = d3.select(domElement).append("svg:svg")
            .attr("width", w)
            .attr("height", h);

        // TODO: make marker color correspond to edge color
        svg.append("svg:defs").selectAll("marker")
                .data(["dir"])
            .enter().append("svg:marker")
                .attr("id", String)
                .attr("viewBox", "0 -5 10 10")
                .attr("refX", 15)
                .attr("refY", -1.5)
                .attr("markerWidth", 6)
                .attr("markerHeight", 6)
                .attr("orient", "auto")
            .append("svg:path")
                .attr("d", "M0,-5L10,0L0,5");

        path = svg.append("svg:g").selectAll("path")
            .data(force.links())
            .enter().append("svg:path")
            .attr("class", function (d) { return "link tag" + d.type; })
            .attr("marker-end", function (d) { return "url(#dir)"; })
            .attr("id", function (d, i) { return "p" + i; });

        linkLabel = svg.append("svg:g")
            .selectAll("text")
            .data(force.links())
            .enter().append("svg:text")
            .attr("class", function (d) {
                return "link-label-tag" + d.type;
            })
            .attr("font-size", 10)
            .attr("text-anchor", "middle")
            .append("svg:textPath")
            .attr("startOffset", "50%")
            .attr("xlink:xlink:href", function (d, i) {
                return "#p" + i;
            })
            .text(function (d) { return d.occurrenceNumber; });

        circle = svg.append("svg:g").selectAll("circle")
            .data(force.nodes())
            .enter().append("svg:circle")
            .attr("r", 6)
            .call(force.drag)
            .on("mouseover", addSelection)
            .on("mouseout", removeSelection);

        text = svg.append("svg:g").selectAll("text")
            .data(force.nodes())
            .enter().append("svg:text")
            .attr("x", 10)
            .attr("y", ".35em")
            .text(function (d) { return d.pname; });

    }

    return { draw: drawDag };

});

<|endoftext|>


<commit_before>/* @flow */

import { ZalgoPromise } from 'zalgo-promise/src';
import * as xcomponent from 'xcomponent/src';
import * as $logger from 'beaver-logger/client';

import { Checkout, enableCheckoutIframe } from '../checkout';
import { config, USERS, SOURCE, ENV, FPTI } from '../../config';
import { redirect as redir, hasMetaViewPort, setLogLevel, forceIframe, getBrowserLocale, getCommonSessionID, request, checkpoint, isIEIntranet, getPageRenderTime, isEligible, getSessionState } from '../../lib';
import { rest } from '../../api';

import { getPopupBridgeOpener, awaitPopupBridgeOpener } from '../checkout/popupBridge';
import { containerTemplate, componentTemplate } from './templates';
import { validateButtonLocale, validateButtonStyle } from './templates/component/validate';
import { awaitBraintreeClient, type BraintreePayPalClient } from './braintree';

getSessionState(session => {
    session.buttonClicked = false;
    session.buttonCancelled = false;
});

export let Button = xcomponent.create({

    tag: 'paypal-button',
    name: 'ppbutton',

    buildUrl(props) : string {
        let env = props.env || config.env;

        return config.buttonUrls[env];
    },

    contexts: {
        iframe: true,
        popup: false
    },

    scrolling: false,

    containerTemplate,
    componentTemplate({ props, htmlDom } : { props : Object, htmlDom : Function }) : HTMLElement {

        let template = htmlDom(componentTemplate({ props }));

        template.addEventListener('click', () => {
            $logger.warn('button_pre_template_click');
        });

        return template;
    },

    sacrificialComponentTemplate: true,

    get version() : string {
        return config.ppobjects ? __FILE_VERSION__ : __MINOR_VERSION__;
    },

    get domain() : Object {
        return config.paypalDomains;
    },

    autoResize: {
        width: false,
        height: true,
        element: 'body'
    },

    validate() {
        if (isIEIntranet()) {
            throw new Error(`Can not render button in IE intranet mode`);
        }

        if (!isEligible()) {
            throw new Error(`Can not render PayPal button in ineligible browser`);
        }
    },

    props: {

        uid: {
            type: 'string',
            value: getCommonSessionID(),
            def() : string {
                return getCommonSessionID();
            },
            queryParam: true
        },

        env: {
            type: 'string',
            required: false,
            queryParam: true,

            def() : string {
                return config.env;
            },

            validate(env) {
                if (!config.paypalUrls[env]) {
                    throw new Error(`Invalid env: ${env}`);
                }
            }
        },

        client: {
            type: 'object',
            required: false,
            def() : Object {
                return {};
            },
            sendToChild: false,

            validate(client, props) {
                let env = props.env || config.env;

                if (!client[env]) {
                    throw new Error(`Client ID not found for env: ${env}`);
                }

                if (client[env].match(/^(.)\1+$/)) {
                    throw new Error(`Invalid client ID: ${client[env]}`);
                }
            }
        },

        source: {
            type: 'string',
            required: false,
            def() : string {
                return SOURCE.MANUAL;
            }
        },

        prefetchLogin: {
            type: 'boolean',
            required: false
        },

        stage: {
            type: 'string',
            required: false,
            queryParam: true,

            def(props) : ?string {
                let env = props.env || config.env;

                if (env === ENV.STAGE || env === ENV.LOCAL) {
                    return config.stage;
                }
            }
        },

        braintree: {
            type: 'object',
            required: false,
            validate(braintree, props) {

                if (!braintree.paypalCheckout) {
                    throw new Error(`Expected Braintree paypal-checkout component to be loaded`);
                }

                if (!props.client) {
                    throw new Error(`Expected client prop to be passed with Braintree authorization keys`);
                }
            },
            decorate(braintree, props) : ?ZalgoPromise<BraintreePayPalClient> {

                if (!braintree) {
                    return;
                }

                let env = props.env || config.env;
                let authorization = props.client[env];

                return awaitBraintreeClient(braintree, authorization);
            }
        },

        payment: {
            type: 'function',
            required: true,
            memoize: false,
            timeout: __TEST__ ? 500 : 10 * 1000,
            alias: 'billingAgreement',

            decorate(original) : Function {
                return function payment() : ZalgoPromise<string> {
                    return new ZalgoPromise((resolve, reject) => {

                        let data = resolve;
                        let actions = reject;

                        data.payment = actions.payment = {
                            create: (options, experience) => {
                                return rest.payment.create(this.props.env, this.props.client, options, experience);
                            }
                        };

                        data.braintree = actions.braintree = {
                            create: (options) => {
                                if (!this.props.braintree) {
                                    throw new Error(`Can not create using Braintree - no braintree client provided`);
                                }

                                return this.props.braintree.then(client => {
                                    return client.createPayment(options);
                                });
                            }
                        };

                        actions.request = request;

                        let context = {
                            props: {
                                env: this.props.env,
                                client: this.props.client
                            }
                        };

                        let result;

                        try {
                            result = original.call(context, data, actions);
                        } catch (err) {
                            return reject(err);
                        }

                        if (result && typeof result.then === 'function') {
                            return result.then(resolve, reject);
                        }

                        if (result !== undefined) {
                            return resolve(result);
                        }

                        let timeout = __TEST__ ? 500 : 10 * 1000;

                        setTimeout(() => {
                            reject(`Timed out waiting ${timeout}ms for payment`);
                        }, timeout);

                    }).then(result => {

                        if (!result) {
                            throw new Error(`No value passed to payment`);
                        }

                        return result;
                    });
                };
            }
        },

        commit: {
            type: 'boolean',
            required: false
        },

        onRender: {
            type: 'function',
            promisify: true,
            required: false,
            decorate(original) : Function {
                return function() : mixed {
                    checkpoint('render_iframe_button', { version: true });
                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.LOAD,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.BUTTON_RENDER,
                        [ FPTI.KEY.BUTTON_TYPE ]: FPTI.BUTTON_TYPE.IFRAME,
                        [ FPTI.KEY.BUTTON_SOURCE ]: this.props.source
                    });
                    $logger.flush();
                    if (original) {
                        return original.apply(this, arguments);
                    }
                };
            }
        },

        onAuth: {
            type: 'function',
            required: false,

            value() {
                this.onRemember = this.onRemember || new ZalgoPromise();
                this.onRemember.resolve();

                enableCheckoutIframe();
            }
        },

        onRemembered: {
            type: 'function',
            required: false,

            value() {
                this.onRemember = this.onRemember || new ZalgoPromise();
                this.onRemember.resolve();
            }
        },

        onDisplay: {
            type: 'function',
            required: false,

            decorate(original) : Function {
                return function() : ZalgoPromise<void> {
                    return ZalgoPromise.try(() => {

                        this.onRemember = this.onRemember || new ZalgoPromise();

                        if (this.props.displayTo === USERS.REMEMBERED) {
                            $logger.info(`button_render_wait_for_remembered_user`);

                            return this.onRemember.then(() => {
                                $logger.info(`button_render_got_remembered_user`);
                            });
                        }

                    }).then(() => {

                        if (original) {
                            return original.apply(this, arguments);
                        }
                    });
                };
            }
        },

        onAuthorize: {
            type: 'function',
            required: true,

            decorate(original) : Function {
                return function(data, actions) : void | ZalgoPromise<void> {

                    $logger.info('checkout_authorize');

                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.CHECKOUT,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.CHECKOUT_AUTHORIZE
                    });

                    $logger.flush();

                    if (this.props.braintree) {
                        return this.props.braintree.then(client => {
                            return client.tokenizePayment(data).then(res => {
                                return original.call(this, { nonce: res.nonce });
                            });
                        });
                    }

                    let redirect = (win, url) => {
                        return ZalgoPromise.all([
                            redir(win || window.top, url || data.returnUrl),
                            actions.close()
                        ]);
                    };

                    return ZalgoPromise.try(() => {
                        return original.call(this, data, { ...actions, redirect });
                    }).catch(err => {
                        return this.error(err);
                    });
                };
            }
        },

        onCancel: {
            type: 'function',
            required: false,
            noop: true,

            decorate(original) : Function {
                return function(data, actions) : void | ZalgoPromise<void> {

                    $logger.info('checkout_cancel');

                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.CHECKOUT,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.CHECKOUT_CANCEL
                    });

                    $logger.flush();

                    getSessionState(session => {
                        session.buttonCancelled = true;
                    });

                    let redirect = (win, url) => {
                        return ZalgoPromise.all([
                            redir(win || window.top, url || data.cancelUrl),
                            actions.close()
                        ]);
                    };

                    if (original) {
                        return original.call(this, data, { ...actions, redirect });
                    }
                };
            }
        },

        onClick: {
            type: 'function',
            required: false,
            decorate(original) : Function {
                return function() : void {

                    $logger.info('button_click');

                    if (getSessionState(session => session.buttonClicked)) {
                        $logger.info('button_click_multiple');
                    }

                    if (getSessionState(session => session.buttonCancelled)) {
                        $logger.info('button_click_after_cancel');
                    }

                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.BUTTON,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.BUTTON_CLICK,
                        [ FPTI.KEY.BUTTON_TYPE ]: FPTI.BUTTON_TYPE.IFRAME
                    });

                    $logger.flush();

                    getSessionState(session => {
                        session.buttonClicked = true;
                    });

                    if (original) {
                        return original.apply(this, arguments);
                    }
                };
            }
        },

        locale: {
            type: 'string',
            required: false,
            queryParam: 'locale.x',

            def() : string {
                let { lang, country } = getBrowserLocale();
                return `${lang}_${country}`;
            },

            validate: validateButtonLocale
        },

        style: {
            type: 'object',
            required: false,
            queryParam: true,
            alias: 'buttonStyle',

            def() : Object {
                return {
                    color: 'gold',
                    shape: 'pill',
                    size:  'small',
                    label: 'checkout',
                    fundingicons: false
                };
            },

            validate: validateButtonStyle
        },

        displayTo: {
            type: 'string',
            required: false,
            def() : string {
                return USERS.ALL;
            }
        },

        disableLightbox: {
            type: 'boolean',
            required: false,

            get value() : boolean {
                return !hasMetaViewPort();
            }
        },

        validate: {
            type: 'function',
            required: false
        },

        logLevel: {
            type: 'string',
            required: false,
            get value() : string {
                return config.logLevel;
            }
        },

        popupBridge: {
            type: 'object',
            required: false,
            get value() : Object {
                return {
                    open: getPopupBridgeOpener(),
                    awaitOpener: awaitPopupBridgeOpener
                };
            }
        },

        test: {
            type: 'object',
            required: false,
            def() : Object {
                return { action: 'checkout' };
            }
        }
    }
});

if (Button.isChild()) {

    getPageRenderTime().then(pageRenderTime => {
        $logger.track({
            [ FPTI.KEY.STATE ]: FPTI.STATE.BUTTON,
            [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.BUTTON_LOAD,
            [ FPTI.KEY.BUTTON_TYPE ]: FPTI.BUTTON_TYPE.IFRAME,
            [ FPTI.KEY.PAGE_LOAD_TIME ]: pageRenderTime
        });

        $logger.flush();
    });

    if (forceIframe()) {
        $logger.info('force_enable_iframe');
        enableCheckoutIframe({ force: true, time: 30 * 60 * 1000 });
    }

    if (window.xprops.logLevel) {
        setLogLevel(window.xprops.logLevel);
    }

    awaitPopupBridgeOpener();

    if (window.xprops.validate) {

        let enabled = true;

        window.xprops.validate({
            enable() {
                enabled = true;
            },

            disable() {
                enabled = false;
            }
        });

        let renderTo = Checkout.renderTo;

        Checkout.renderTo = function() : ?Promise<Object> {
            if (enabled) {
                return renderTo.apply(this, arguments);
            }
        };
    }
}
<commit_msg>Log warning for ineligible button render<commit_after>/* @flow */

import { ZalgoPromise } from 'zalgo-promise/src';
import * as xcomponent from 'xcomponent/src';
import * as $logger from 'beaver-logger/client';

import { Checkout, enableCheckoutIframe } from '../checkout';
import { config, USERS, SOURCE, ENV, FPTI } from '../../config';
import { redirect as redir, hasMetaViewPort, setLogLevel, forceIframe, getBrowserLocale, getCommonSessionID, request, checkpoint, isIEIntranet, getPageRenderTime, isEligible, getSessionState } from '../../lib';
import { rest } from '../../api';

import { getPopupBridgeOpener, awaitPopupBridgeOpener } from '../checkout/popupBridge';
import { containerTemplate, componentTemplate } from './templates';
import { validateButtonLocale, validateButtonStyle } from './templates/component/validate';
import { awaitBraintreeClient, type BraintreePayPalClient } from './braintree';

getSessionState(session => {
    session.buttonClicked = false;
    session.buttonCancelled = false;
});

export let Button = xcomponent.create({

    tag: 'paypal-button',
    name: 'ppbutton',

    buildUrl(props) : string {
        let env = props.env || config.env;

        return config.buttonUrls[env];
    },

    contexts: {
        iframe: true,
        popup: false
    },

    scrolling: false,

    containerTemplate,
    componentTemplate({ props, htmlDom } : { props : Object, htmlDom : Function }) : HTMLElement {

        let template = htmlDom(componentTemplate({ props }));

        template.addEventListener('click', () => {
            $logger.warn('button_pre_template_click');
        });

        return template;
    },

    sacrificialComponentTemplate: true,

    get version() : string {
        return config.ppobjects ? __FILE_VERSION__ : __MINOR_VERSION__;
    },

    get domain() : Object {
        return config.paypalDomains;
    },

    autoResize: {
        width: false,
        height: true,
        element: 'body'
    },

    validate() {
        if (isIEIntranet()) {
            throw new Error(`Can not render button in IE intranet mode`);
        }

        if (!isEligible()) {
            $logger.warn('button_render_ineligible');
        }
    },

    props: {

        uid: {
            type: 'string',
            value: getCommonSessionID(),
            def() : string {
                return getCommonSessionID();
            },
            queryParam: true
        },

        env: {
            type: 'string',
            required: false,
            queryParam: true,

            def() : string {
                return config.env;
            },

            validate(env) {
                if (!config.paypalUrls[env]) {
                    throw new Error(`Invalid env: ${env}`);
                }
            }
        },

        client: {
            type: 'object',
            required: false,
            def() : Object {
                return {};
            },
            sendToChild: false,

            validate(client, props) {
                let env = props.env || config.env;

                if (!client[env]) {
                    throw new Error(`Client ID not found for env: ${env}`);
                }

                if (client[env].match(/^(.)\1+$/)) {
                    throw new Error(`Invalid client ID: ${client[env]}`);
                }
            }
        },

        source: {
            type: 'string',
            required: false,
            def() : string {
                return SOURCE.MANUAL;
            }
        },

        prefetchLogin: {
            type: 'boolean',
            required: false
        },

        stage: {
            type: 'string',
            required: false,
            queryParam: true,

            def(props) : ?string {
                let env = props.env || config.env;

                if (env === ENV.STAGE || env === ENV.LOCAL) {
                    return config.stage;
                }
            }
        },

        braintree: {
            type: 'object',
            required: false,
            validate(braintree, props) {

                if (!braintree.paypalCheckout) {
                    throw new Error(`Expected Braintree paypal-checkout component to be loaded`);
                }

                if (!props.client) {
                    throw new Error(`Expected client prop to be passed with Braintree authorization keys`);
                }
            },
            decorate(braintree, props) : ?ZalgoPromise<BraintreePayPalClient> {

                if (!braintree) {
                    return;
                }

                let env = props.env || config.env;
                let authorization = props.client[env];

                return awaitBraintreeClient(braintree, authorization);
            }
        },

        payment: {
            type: 'function',
            required: true,
            memoize: false,
            timeout: __TEST__ ? 500 : 10 * 1000,
            alias: 'billingAgreement',

            decorate(original) : Function {
                return function payment() : ZalgoPromise<string> {
                    return new ZalgoPromise((resolve, reject) => {

                        let data = resolve;
                        let actions = reject;

                        data.payment = actions.payment = {
                            create: (options, experience) => {
                                return rest.payment.create(this.props.env, this.props.client, options, experience);
                            }
                        };

                        data.braintree = actions.braintree = {
                            create: (options) => {
                                if (!this.props.braintree) {
                                    throw new Error(`Can not create using Braintree - no braintree client provided`);
                                }

                                return this.props.braintree.then(client => {
                                    return client.createPayment(options);
                                });
                            }
                        };

                        actions.request = request;

                        let context = {
                            props: {
                                env: this.props.env,
                                client: this.props.client
                            }
                        };

                        let result;

                        try {
                            result = original.call(context, data, actions);
                        } catch (err) {
                            return reject(err);
                        }

                        if (result && typeof result.then === 'function') {
                            return result.then(resolve, reject);
                        }

                        if (result !== undefined) {
                            return resolve(result);
                        }

                        let timeout = __TEST__ ? 500 : 10 * 1000;

                        setTimeout(() => {
                            reject(`Timed out waiting ${timeout}ms for payment`);
                        }, timeout);

                    }).then(result => {

                        if (!result) {
                            throw new Error(`No value passed to payment`);
                        }

                        return result;
                    });
                };
            }
        },

        commit: {
            type: 'boolean',
            required: false
        },

        onRender: {
            type: 'function',
            promisify: true,
            required: false,
            decorate(original) : Function {
                return function() : mixed {
                    checkpoint('render_iframe_button', { version: true });
                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.LOAD,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.BUTTON_RENDER,
                        [ FPTI.KEY.BUTTON_TYPE ]: FPTI.BUTTON_TYPE.IFRAME,
                        [ FPTI.KEY.BUTTON_SOURCE ]: this.props.source
                    });
                    $logger.flush();
                    if (original) {
                        return original.apply(this, arguments);
                    }
                };
            }
        },

        onAuth: {
            type: 'function',
            required: false,

            value() {
                this.onRemember = this.onRemember || new ZalgoPromise();
                this.onRemember.resolve();

                enableCheckoutIframe();
            }
        },

        onRemembered: {
            type: 'function',
            required: false,

            value() {
                this.onRemember = this.onRemember || new ZalgoPromise();
                this.onRemember.resolve();
            }
        },

        onDisplay: {
            type: 'function',
            required: false,

            decorate(original) : Function {
                return function() : ZalgoPromise<void> {
                    return ZalgoPromise.try(() => {

                        this.onRemember = this.onRemember || new ZalgoPromise();

                        if (this.props.displayTo === USERS.REMEMBERED) {
                            $logger.info(`button_render_wait_for_remembered_user`);

                            return this.onRemember.then(() => {
                                $logger.info(`button_render_got_remembered_user`);
                            });
                        }

                    }).then(() => {

                        if (original) {
                            return original.apply(this, arguments);
                        }
                    });
                };
            }
        },

        onAuthorize: {
            type: 'function',
            required: true,

            decorate(original) : Function {
                return function(data, actions) : void | ZalgoPromise<void> {

                    $logger.info('checkout_authorize');

                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.CHECKOUT,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.CHECKOUT_AUTHORIZE
                    });

                    $logger.flush();

                    if (this.props.braintree) {
                        return this.props.braintree.then(client => {
                            return client.tokenizePayment(data).then(res => {
                                return original.call(this, { nonce: res.nonce });
                            });
                        });
                    }

                    let redirect = (win, url) => {
                        return ZalgoPromise.all([
                            redir(win || window.top, url || data.returnUrl),
                            actions.close()
                        ]);
                    };

                    return ZalgoPromise.try(() => {
                        return original.call(this, data, { ...actions, redirect });
                    }).catch(err => {
                        return this.error(err);
                    });
                };
            }
        },

        onCancel: {
            type: 'function',
            required: false,
            noop: true,

            decorate(original) : Function {
                return function(data, actions) : void | ZalgoPromise<void> {

                    $logger.info('checkout_cancel');

                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.CHECKOUT,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.CHECKOUT_CANCEL
                    });

                    $logger.flush();

                    getSessionState(session => {
                        session.buttonCancelled = true;
                    });

                    let redirect = (win, url) => {
                        return ZalgoPromise.all([
                            redir(win || window.top, url || data.cancelUrl),
                            actions.close()
                        ]);
                    };

                    if (original) {
                        return original.call(this, data, { ...actions, redirect });
                    }
                };
            }
        },

        onClick: {
            type: 'function',
            required: false,
            decorate(original) : Function {
                return function() : void {

                    $logger.info('button_click');

                    if (getSessionState(session => session.buttonClicked)) {
                        $logger.info('button_click_multiple');
                    }

                    if (getSessionState(session => session.buttonCancelled)) {
                        $logger.info('button_click_after_cancel');
                    }

                    $logger.track({
                        [ FPTI.KEY.STATE ]: FPTI.STATE.BUTTON,
                        [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.BUTTON_CLICK,
                        [ FPTI.KEY.BUTTON_TYPE ]: FPTI.BUTTON_TYPE.IFRAME
                    });

                    $logger.flush();

                    getSessionState(session => {
                        session.buttonClicked = true;
                    });

                    if (original) {
                        return original.apply(this, arguments);
                    }
                };
            }
        },

        locale: {
            type: 'string',
            required: false,
            queryParam: 'locale.x',

            def() : string {
                let { lang, country } = getBrowserLocale();
                return `${lang}_${country}`;
            },

            validate: validateButtonLocale
        },

        style: {
            type: 'object',
            required: false,
            queryParam: true,
            alias: 'buttonStyle',

            def() : Object {
                return {
                    color: 'gold',
                    shape: 'pill',
                    size:  'small',
                    label: 'checkout',
                    fundingicons: false
                };
            },

            validate: validateButtonStyle
        },

        displayTo: {
            type: 'string',
            required: false,
            def() : string {
                return USERS.ALL;
            }
        },

        disableLightbox: {
            type: 'boolean',
            required: false,

            get value() : boolean {
                return !hasMetaViewPort();
            }
        },

        validate: {
            type: 'function',
            required: false
        },

        logLevel: {
            type: 'string',
            required: false,
            get value() : string {
                return config.logLevel;
            }
        },

        popupBridge: {
            type: 'object',
            required: false,
            get value() : Object {
                return {
                    open: getPopupBridgeOpener(),
                    awaitOpener: awaitPopupBridgeOpener
                };
            }
        },

        test: {
            type: 'object',
            required: false,
            def() : Object {
                return { action: 'checkout' };
            }
        }
    }
});

if (Button.isChild()) {

    getPageRenderTime().then(pageRenderTime => {
        $logger.track({
            [ FPTI.KEY.STATE ]: FPTI.STATE.BUTTON,
            [ FPTI.KEY.TRANSITION ]: FPTI.TRANSITION.BUTTON_LOAD,
            [ FPTI.KEY.BUTTON_TYPE ]: FPTI.BUTTON_TYPE.IFRAME,
            [ FPTI.KEY.PAGE_LOAD_TIME ]: pageRenderTime
        });

        $logger.flush();
    });

    if (forceIframe()) {
        $logger.info('force_enable_iframe');
        enableCheckoutIframe({ force: true, time: 30 * 60 * 1000 });
    }

    if (window.xprops.logLevel) {
        setLogLevel(window.xprops.logLevel);
    }

    awaitPopupBridgeOpener();

    if (window.xprops.validate) {

        let enabled = true;

        window.xprops.validate({
            enable() {
                enabled = true;
            },

            disable() {
                enabled = false;
            }
        });

        let renderTo = Checkout.renderTo;

        Checkout.renderTo = function() : ?Promise<Object> {
            if (enabled) {
                return renderTo.apply(this, arguments);
            }
        };
    }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';

import AddSkillForm from '../AddSkillForm';
import Button from '../Button';

import { Page, Row } from '../../theme/grid';
import { colors, typography } from '../../theme/variables';
import { mediaMin } from '../../theme/style-utils';

const SkillsRow = styled(Row)`
  display: flex;
  flex-wrap: wrap;
`;

const SkillsColumn = styled.ul`
  flex: 1;
  padding: 0;
  margin: 0 0 1em;
  list-style: none;
  min-width: 200px;

  h3,
  li {
    text-align: center;
  }

  h3 {
    color: ${colors.blue};
    padding: 0.5em 0;
    margin: 0 0 0.75em 0;
    border-bottom: 2px solid ${colors.blue};
  }

  li {
    font-family: ${typography.monospace};
    line-height: 1.5;
    padding: 0 1em;
    margin: 1em 0;
    font-size: 0.85rem;

    ${mediaMin.phone`
      font-size: 0.75rem;
    `};

    &:last-child {
      margin-bottom: 0;
    }
  }
`;

class About extends Component {
  constructor(props) {
    super(props);
    this.renderList = this.renderList.bind(this);
    this.removeSkill = this.removeSkill.bind(this);
  }

  removeSkill(key) {
    this.props.removeSkill(key);
  }

  renderList(category) {
    return Object.keys(this.props.skills).map((key) => {
      if (this.props.skills[key].category === category) {
        return (

            {this.props.skills[key].name}
            {this.props.uid ? (
              <Button
                to="#"
                type="delete"
                onClick={() => this.removeSkill(key)}
              />
            ) : null}

        );
      }
      return null;
    });
  }

  render() {
    return (
      <Page title="About">
        <Row>

About, Skills and Interests

          <SkillsRow child>
            <SkillsColumn>

Core

              {this.renderList('core')}
            </SkillsColumn>
            <SkillsColumn>

Libraries

              {this.renderList('library')}
            </SkillsColumn>
            <SkillsColumn>

Design

              {this.renderList('design')}
            </SkillsColumn>
          </SkillsRow>
          {this.props.uid ? (
            <Row child>
              <AddSkillForm addSkill={this.props.addSkill} />
            </Row>
          ) : null}
        </Row>
        <Row>

A bit about me

            Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat
            consequatur labore voluptate ut voluptatum porro ex doloribus ad,
            molestias sed! Ducimus molestias cum incidunt eaque cupiditate,
            mollitia delectus asperiores voluptatum ipsum quibusdam, similique
            beatae ea facere praesentium libero provident placeat.

        </Row>
      </Page>
    );
  }
}

About.propTypes = {
  uid: PropTypes.string,
  skills: PropTypes.object.isRequired,
  addSkill: PropTypes.func.isRequired,
  removeSkill: PropTypes.func.isRequired,
};

About.defaultProps = {
  uid: null,
};

export default About;
<commit_msg>About me paragraph<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import moment from 'moment';

import AddSkillForm from '../AddSkillForm';
import Button from '../Button';
import CloudImage from '../CloudImage';

import { Page, Row } from '../../theme/grid';
import { colors, typography } from '../../theme/variables';
import { mediaMin } from '../../theme/style-utils';

const SkillsRow = styled(Row)`
  display: flex;
  flex-wrap: wrap;
`;

const SkillsColumn = styled.ul`
  flex: 1;
  padding: 0;
  margin: 0 0 1em;
  list-style: none;
  min-width: 200px;

  h3,
  li {
    text-align: center;
  }

  h3 {
    color: ${colors.blue};
    padding: 0.5em 0;
    margin: 0 0 0.75em 0;
    border-bottom: 2px solid ${colors.blue};
  }

  li {
    font-family: ${typography.monospace};
    line-height: 1.5;
    padding: 0 1em;
    margin: 1em 0;
    font-size: 0.85rem;

    ${mediaMin.phone`
      font-size: 0.75rem;
    `};

    &:last-child {
      margin-bottom: 0;
    }
  }
`;

class About extends Component {
  constructor(props) {
    super(props);
    this.renderList = this.renderList.bind(this);
    this.removeSkill = this.removeSkill.bind(this);
  }

  removeSkill(key) {
    this.props.removeSkill(key);
  }

  renderList(category) {
    return Object.keys(this.props.skills).map((key) => {
      if (this.props.skills[key].category === category) {
        return (

            {this.props.skills[key].name}
            {this.props.uid ? (
              <Button
                to="#"
                type="delete"
                onClick={() => this.removeSkill(key)}
              />
            ) : null}

        );
      }
      return null;
    });
  }

  render() {
    return (
      <Page title="About">
        <Row>

About, Skills and Interests

          <SkillsRow child>
            <SkillsColumn>

Core

              {this.renderList('core')}
            </SkillsColumn>
            <SkillsColumn>

Libraries

              {this.renderList('library')}
            </SkillsColumn>
            <SkillsColumn>

Design

              {this.renderList('design')}
            </SkillsColumn>
          </SkillsRow>
          {this.props.uid ? (
            <Row child>
              <AddSkillForm addSkill={this.props.addSkill} />
            </Row>
          ) : null}
        </Row>
        <Row>

A bit about me...

            <CloudImage
              publicId="Xander_with_finger_paint"
              name="My son Xander finger painting with our dog Gizmo in the background."
              align="right"
              width="200"
              link
            />
            I'm really passionate about design and making everything I touch on
            the web better. I'm never satisfied with my skillset and am
            constantly striving to be better and learn as much as I can when the
            time allows. There isn't time to learn everything though. My wife
            Lee and I had our son Xander{' '}
            {moment(20160216, 'YYYYMMDD').fromNow()} and watching him grow and
            play with our shiba inu Gizmo has been my greatest joy in life.

            Recently I've dived head first into learning React.js and am loving
            it. So much so I decided to build this portfolio with it to
            challenge myself. Do you need React to build a simple{' '}
            portfolio site? Of course not. Is actually building
            something in production the best way to learn something?{' '}
            ¯\_(ツ)_/¯

You can find more of what I'm learning by{' '}

              inspecting my GitHub profile
            .

            My worklow:
            I have used many systems, programs and workflows over the years,
            jumping back forth between Windows and Linux. Currently, I'm most
            comfortable using{' '}

              VS Code
            , complimented by{' '}

              Hyperterm
            {' '}
            with zsh.

            In my spare time I enjoy being the biggest nerd
            possible. I build PCs, build home automation systems, manage linux
            and media servers. Mostly, though, I play PC games. If you'd like to
            add me on{' '}

              Steam, here's my community page
            . I'm also obsessed with making the best coffee possible; my
            favorite brewing methods are the Chemex, Kalita Wave and my{' '}

              SCA
            {' '}
            certified Behmor Brazen Plus drip machine (like I said, obsessed).

        </Row>
        <Row>

Work History

        </Row>
      </Page>
    );
  }
}

About.propTypes = {
  uid: PropTypes.string,
  skills: PropTypes.object.isRequired,
  addSkill: PropTypes.func.isRequired,
  removeSkill: PropTypes.func.isRequired,
};

About.defaultProps = {
  uid: null,
};

export default About;
<|endoftext|>


<commit_before>export default class Matrix {
  constructor (id, matrix, locus, orientation, measures) {
    this.id = id;
    this.matrix = matrix;
    this.locus = locus;
    this.measures = measures;
    this.orientation = orientation;

    this.orientationX = this.orientation.strand1 === 'coding' ? 1 : -1;
    this.orientationY = this.orientation.strand2 === 'coding' ? 1 : -1;

    this.dim = matrix.length;
  }

  /**
   * Flip the raw matix's x axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {number} Orientation.
   */
  flipX () {
    Matrix.flipX(this.matrix);
    this.orientationX *= -1;
  }
  /**
   * Flip the raw matix's x axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {number} Orientation.
   */
  flipY () {
    Matrix.flipY(this.matrix);
    this.orientationY *= -1;
  }

  /**
   * Flip the raw matix's x axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {number} Orientation.
   */
  static flipX (matrix) {
    matrix.map(row => row.reverse());
  }

  /**
   * Flip the raw matix's y axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {object} Orientation.
   */
  static flipY (matrix) {
    matrix.reverse();
  }

  /**
   * Flatten  a 2D array.
   *
   * @param {array} matrix - 2D matrix to be flattened.
   * @return {array} 1D arrays.
   */
  static flatten (matrix) {
    return matrix.reduce((a, b) => a.concat(b), []);
  }
}
<commit_msg>Make matrices visible by default<commit_after>export default class Matrix {
  constructor (id, matrix, locus, orientation, measures) {
    this.id = id;
    this.matrix = matrix;
    this.locus = locus;
    this.measures = measures;
    this.orientation = orientation;

    this.orientationX = this.orientation.strand1 === 'coding' ? 1 : -1;
    this.orientationY = this.orientation.strand2 === 'coding' ? 1 : -1;

    this.dim = matrix.length;

    this.visible = true;
  }

  /**
   * Flip the raw matix's x axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {number} Orientation.
   */
  flipX () {
    Matrix.flipX(this.matrix);
    this.orientationX *= -1;
  }
  /**
   * Flip the raw matix's x axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {number} Orientation.
   */
  flipY () {
    Matrix.flipY(this.matrix);
    this.orientationY *= -1;
  }

  /**
   * Flip the raw matix's x axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {number} Orientation.
   */
  static flipX (matrix) {
    matrix.map(row => row.reverse());
  }

  /**
   * Flip the raw matix's y axis in-place.
   *
   * @param {array} matrix - 2D matrix to be flipped.
   * @return {object} Orientation.
   */
  static flipY (matrix) {
    matrix.reverse();
  }

  /**
   * Flatten  a 2D array.
   *
   * @param {array} matrix - 2D matrix to be flattened.
   * @return {array} 1D arrays.
   */
  static flatten (matrix) {
    return matrix.reduce((a, b) => a.concat(b), []);
  }
}
<|endoftext|>


<commit_before>'use strict'

module.exports = function(
  getSelectedWidgets,
  getInitialWidgetPosition,
  mouseMoveWidgetUpdateFunction,
  mouseUpWidgetUpdateFunction
) {
  return {
    componentDidMount: function() {
      this.mouseDownHdlrs && this.mouseDownHdlrs.push(this.onDraggableMouseDown)
    },
    componentWillUnmount: function() {
      document.removeEventListener('mousemove', this.onDraggableMouseMove)
      document.removeEventListener('touchmove', this.onDraggableMouseMove)
      document.removeEventListener('mouseup', this.onDraggableMouseUp)
      document.removeEventListener('touchend', this.onDraggableMouseUp)
    },
    onDraggableMouseDown: function(ev) {
      // only left mouse button or touchstart
      if (ev.button !== 0 && ev.type !== 'touchstart') return
      if (!this.state.draggable) return
      document.addEventListener('touchmove', this.onDraggableMouseMove)
      document.addEventListener('touchend', this.onDraggableMouseUp)
      document.addEventListener('mousemove', this.onDraggableMouseMove)
      document.addEventListener('mouseup', this.onDraggableMouseUp)
      document.body.style.WebkitUserSelect = 'none'
      document.body.style.MozUserSelect = 'none'
      document.body.style.MsUserSelect = 'none'

      this._draggable = {}
      this._draggable.drags = []
      this._draggable.dragged = false
      this._draggable.ox = ev.targetTouches
        ? ev.targetTouches[0].pageX
        : ev.pageX
      this._draggable.oy = ev.targetTouches
        ? ev.targetTouches[0].pageY
        : ev.pageY

      let selectedWidgets = getSelectedWidgets.bind(this)()
      selectedWidgets.forEach((e, i) => {
        let draggable = {}
        let p = getInitialWidgetPosition.bind(this, e, i)()
        draggable.oleft = p.x
        draggable.otop = p.y
        draggable.oz = p.z
        this._draggable.drags[i] = draggable
      })
      ev.stopPropagation && ev.stopPropagation()
    },
    onDraggableMouseMove: function(ev) {
      ev.preventDefault && ev.preventDefault()
      ev.stopPropagation && ev.stopPropagation()
      let pageX = ev.targetTouches ? ev.targetTouches[0].pageX : ev.pageX
      let pageY = ev.targetTouches ? ev.targetTouches[0].pageY : ev.pageY
      let pageOX = this._draggable.ox
      let pageOY = this._draggable.oy
      // skip initial drags
      if (
        Math.sqrt(Math.pow(pageOX - pageX, 2) + Math.pow(pageOY - pageY, 2)) < 3
      ) {
        return
      }

      let scale = this.state.scale || 1
      let perspective = this.props.deck && this.props.deck.perspective
      this._draggable.dragged = true
      let selectedWidgets = getSelectedWidgets.bind(this)()
      selectedWidgets.forEach((e, i) => {
        let zScale = 1
        if (perspective) {
          let scaledPerspective = perspective / scale
          let depth = scaledPerspective - this._draggable.drags[i].oz
          zScale = depth === 0 ? Infinity : scaledPerspective / depth
        }
        let updatedProps = {}
        if (this.state.draggable === 'z') {
          updatedProps.z =
            this._draggable.drags[i].oz +
            Math.round((pageX - this._draggable.ox) / scale)
          if (ev.ctrlKey) {
            updatedProps.z = 10 * Math.round(updatedProps.z / 10)
          }
        } else {
          if (this.state.draggable === 'x' || this.state.draggable === true) {
            updatedProps.x =
              this._draggable.drags[i].oleft +
              Math.round((pageX - this._draggable.ox) / scale / zScale)
          }
          if (this.state.draggable === 'y' || this.state.draggable === true) {
            updatedProps.y =
              this._draggable.drags[i].otop +
              Math.round((pageY - this._draggable.oy) / scale / zScale)
          }
          if (ev.ctrlKey) {
            updatedProps.x = 10 * Math.round(updatedProps.x / 10)
            updatedProps.y = 10 * Math.round(updatedProps.y / 10)
          }
        }
        mouseMoveWidgetUpdateFunction &&
          mouseMoveWidgetUpdateFunction.bind(this, e, updatedProps)()
      })
    },
    onDraggableMouseUp: function(ev) {
      if (ev.type === 'touchend' && ev.targetTouches.length > 0) {
        return
      }
      let pageX = ev.changedTouches ? ev.changedTouches[0].pageX : ev.pageX
      let pageY = ev.changedTouches ? ev.changedTouches[0].pageY : ev.pageY
      document.body.style.WebkitUserSelect = ''
      document.body.style.MozUserSelect = ''
      document.body.style.MsUserSelect = ''
      document.removeEventListener('mousemove', this.onDraggableMouseMove)
      document.removeEventListener('touchmove', this.onDraggableMouseMove)
      document.removeEventListener('mouseup', this.onDraggableMouseUp)
      document.removeEventListener('touchend', this.onDraggableMouseUp)
      if (!this._draggable.dragged) return
      let scale = this.state.scale || 1
      let perspective = this.props.deck && this.props.deck.perspective
      let selectedWidgets = getSelectedWidgets.bind(this)()
      selectedWidgets.forEach((e, i) => {
        let zScale = 1
        if (perspective) {
          let scaledPerspective = perspective / scale
          let depth = scaledPerspective - this._draggable.drags[i].oz
          zScale = depth === 0 ? Infinity : scaledPerspective / depth
        }
        let updatedProps = {}
        if (this.state.draggable === 'z') {
          updatedProps.z =
            this._draggable.drags[i].oz +
            Math.round((pageX - this._draggable.ox) / scale)
          if (ev.ctrlKey) {
            updatedProps.z = 10 * Math.round(updatedProps.z / 10)
          }
        } else {
          if (this.state.draggable === 'x' || this.state.draggable === true) {
            updatedProps.x =
              this._draggable.drags[i].oleft +
              Math.round((pageX - this._draggable.ox) / scale / zScale)
          }
          if (this.state.draggable === 'y' || this.state.draggable === true) {
            updatedProps.y =
              this._draggable.drags[i].otop +
              Math.round((pageY - this._draggable.oy) / scale / zScale)
          }
          if (ev.ctrlKey) {
            updatedProps.x = 10 * Math.round(updatedProps.x / 10)
            updatedProps.y = 10 * Math.round(updatedProps.y / 10)
          }
        }
        mouseUpWidgetUpdateFunction &&
          mouseUpWidgetUpdateFunction.bind(this, e, updatedProps)()
      })
      this.setState({ draggable: true })
      ev.stopPropagation && ev.stopPropagation()
      ev.preventDefault && ev.preventDefault()
    }
  }
}

module.exports.draggableMixin = (
  Base,
  getSelectedWidgets,
  getInitialWidgetPosition,
  mouseMoveWidgetUpdateFunction,
  mouseUpWidgetUpdateFunction
) =>
  class extends Base {
    constructor(props) {
      super(props)
      this.prot = module.exports(
        getSelectedWidgets,
        getInitialWidgetPosition,
        mouseMoveWidgetUpdateFunction,
        mouseUpWidgetUpdateFunction
      )
      this.onDraggableMouseDown = this.prot.onDraggableMouseDown
      this.onDraggableMouseMove = this.prot.onDraggableMouseMove.bind(this)
      this.onDraggableMouseUp = this.prot.onDraggableMouseUp.bind(this)
    }

    componentWillUnmount() {
      super.componentWillUnmount && super.componentWillUnmount()
      this.prot.componentWillUnmount.apply(this)
    }

    componentDidMount() {
      super.componentDidMount && super.componentDidMount()
      this.prot.componentDidMount.apply(this)
    }
  }
<commit_msg>fix chrome warnings about touchmove event passive and not cancellable.<commit_after>'use strict'

module.exports = function(
  getSelectedWidgets,
  getInitialWidgetPosition,
  mouseMoveWidgetUpdateFunction,
  mouseUpWidgetUpdateFunction
) {
  return {
    componentDidMount: function() {
      this.mouseDownHdlrs && this.mouseDownHdlrs.push(this.onDraggableMouseDown)
    },
    componentWillUnmount: function() {
      document.removeEventListener('mousemove', this.onDraggableMouseMove)
      document.removeEventListener('touchmove', this.onDraggableMouseMove)
      document.removeEventListener('mouseup', this.onDraggableMouseUp)
      document.removeEventListener('touchend', this.onDraggableMouseUp)
    },
    onDraggableMouseDown: function(ev) {
      // only left mouse button or touchstart
      if (ev.button !== 0 && ev.type !== 'touchstart') return
      if (!this.state.draggable) return
      document.addEventListener('touchmove', this.onDraggableMouseMove, {
        passive: false
      })
      document.addEventListener('touchend', this.onDraggableMouseUp, {
        passive: false
      })
      document.addEventListener('mousemove', this.onDraggableMouseMove)
      document.addEventListener('mouseup', this.onDraggableMouseUp)
      document.body.style.WebkitUserSelect = 'none'
      document.body.style.MozUserSelect = 'none'
      document.body.style.MsUserSelect = 'none'

      this._draggable = {}
      this._draggable.drags = []
      this._draggable.dragged = false
      this._draggable.ox = ev.targetTouches
        ? ev.targetTouches[0].pageX
        : ev.pageX
      this._draggable.oy = ev.targetTouches
        ? ev.targetTouches[0].pageY
        : ev.pageY

      let selectedWidgets = getSelectedWidgets.bind(this)()
      selectedWidgets.forEach((e, i) => {
        let draggable = {}
        let p = getInitialWidgetPosition.bind(this, e, i)()
        draggable.oleft = p.x
        draggable.otop = p.y
        draggable.oz = p.z
        this._draggable.drags[i] = draggable
      })
      ev.stopPropagation && ev.stopPropagation()
    },
    onDraggableMouseMove: function(ev) {
      !ev.targetTouches && ev.preventDefault && ev.preventDefault()
      ev.stopPropagation && ev.stopPropagation()
      let pageX = ev.targetTouches ? ev.targetTouches[0].pageX : ev.pageX
      let pageY = ev.targetTouches ? ev.targetTouches[0].pageY : ev.pageY
      let pageOX = this._draggable.ox
      let pageOY = this._draggable.oy
      // skip initial drags
      if (
        Math.sqrt(Math.pow(pageOX - pageX, 2) + Math.pow(pageOY - pageY, 2)) < 3
      ) {
        return
      }

      let scale = this.state.scale || 1
      let perspective = this.props.deck && this.props.deck.perspective
      this._draggable.dragged = true
      let selectedWidgets = getSelectedWidgets.bind(this)()
      selectedWidgets.forEach((e, i) => {
        let zScale = 1
        if (perspective) {
          let scaledPerspective = perspective / scale
          let depth = scaledPerspective - this._draggable.drags[i].oz
          zScale = depth === 0 ? Infinity : scaledPerspective / depth
        }
        let updatedProps = {}
        if (this.state.draggable === 'z') {
          updatedProps.z =
            this._draggable.drags[i].oz +
            Math.round((pageX - this._draggable.ox) / scale)
          if (ev.ctrlKey) {
            updatedProps.z = 10 * Math.round(updatedProps.z / 10)
          }
        } else {
          if (this.state.draggable === 'x' || this.state.draggable === true) {
            updatedProps.x =
              this._draggable.drags[i].oleft +
              Math.round((pageX - this._draggable.ox) / scale / zScale)
          }
          if (this.state.draggable === 'y' || this.state.draggable === true) {
            updatedProps.y =
              this._draggable.drags[i].otop +
              Math.round((pageY - this._draggable.oy) / scale / zScale)
          }
          if (ev.ctrlKey) {
            updatedProps.x = 10 * Math.round(updatedProps.x / 10)
            updatedProps.y = 10 * Math.round(updatedProps.y / 10)
          }
        }
        mouseMoveWidgetUpdateFunction &&
          mouseMoveWidgetUpdateFunction.bind(this, e, updatedProps)()
      })
      return true
    },
    onDraggableMouseUp: function(ev) {
      if (ev.type === 'touchend' && ev.targetTouches.length > 0) {
        return
      }
      let pageX = ev.changedTouches ? ev.changedTouches[0].pageX : ev.pageX
      let pageY = ev.changedTouches ? ev.changedTouches[0].pageY : ev.pageY
      document.body.style.WebkitUserSelect = ''
      document.body.style.MozUserSelect = ''
      document.body.style.MsUserSelect = ''
      document.removeEventListener('mousemove', this.onDraggableMouseMove)
      document.removeEventListener('touchmove', this.onDraggableMouseMove)
      document.removeEventListener('mouseup', this.onDraggableMouseUp)
      document.removeEventListener('touchend', this.onDraggableMouseUp)
      if (!this._draggable.dragged) return
      let scale = this.state.scale || 1
      let perspective = this.props.deck && this.props.deck.perspective
      let selectedWidgets = getSelectedWidgets.bind(this)()
      selectedWidgets.forEach((e, i) => {
        let zScale = 1
        if (perspective) {
          let scaledPerspective = perspective / scale
          let depth = scaledPerspective - this._draggable.drags[i].oz
          zScale = depth === 0 ? Infinity : scaledPerspective / depth
        }
        let updatedProps = {}
        if (this.state.draggable === 'z') {
          updatedProps.z =
            this._draggable.drags[i].oz +
            Math.round((pageX - this._draggable.ox) / scale)
          if (ev.ctrlKey) {
            updatedProps.z = 10 * Math.round(updatedProps.z / 10)
          }
        } else {
          if (this.state.draggable === 'x' || this.state.draggable === true) {
            updatedProps.x =
              this._draggable.drags[i].oleft +
              Math.round((pageX - this._draggable.ox) / scale / zScale)
          }
          if (this.state.draggable === 'y' || this.state.draggable === true) {
            updatedProps.y =
              this._draggable.drags[i].otop +
              Math.round((pageY - this._draggable.oy) / scale / zScale)
          }
          if (ev.ctrlKey) {
            updatedProps.x = 10 * Math.round(updatedProps.x / 10)
            updatedProps.y = 10 * Math.round(updatedProps.y / 10)
          }
        }
        mouseUpWidgetUpdateFunction &&
          mouseUpWidgetUpdateFunction.bind(this, e, updatedProps)()
      })
      this.setState({ draggable: true })
      ev.stopPropagation && ev.stopPropagation()
      ev.preventDefault && ev.preventDefault()
    }
  }
}

module.exports.draggableMixin = (
  Base,
  getSelectedWidgets,
  getInitialWidgetPosition,
  mouseMoveWidgetUpdateFunction,
  mouseUpWidgetUpdateFunction
) =>
  class extends Base {
    constructor(props) {
      super(props)
      this.prot = module.exports(
        getSelectedWidgets,
        getInitialWidgetPosition,
        mouseMoveWidgetUpdateFunction,
        mouseUpWidgetUpdateFunction
      )
      this.onDraggableMouseDown = this.prot.onDraggableMouseDown
      this.onDraggableMouseMove = this.prot.onDraggableMouseMove.bind(this)
      this.onDraggableMouseUp = this.prot.onDraggableMouseUp.bind(this)
    }

    componentWillUnmount() {
      super.componentWillUnmount && super.componentWillUnmount()
      this.prot.componentWillUnmount.apply(this)
    }

    componentDidMount() {
      super.componentDidMount && super.componentDidMount()
      this.prot.componentDidMount.apply(this)
    }
  }
<|endoftext|>


<commit_before>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import styled from 'styled-components';

import { Text } from '~components/primitives/Text';
import { Label } from '~components/text/Label';
import { Tooltip } from '~components/text/Tooltip';
import { Error } from '~components/text/Error';

// ─────────────────────────────────────────────────────────────────────────────
// helpers
// ─────────────────────────────────────────────────────────────────────────────

export const Input = styled(Text)`
  --shadow: 0 1px 0 0 hsla(var(--hsl-text), 0.5);
  --shadow-light: 0 1px 0 0 hsla(var(--hsl-text), 0.25);

  appearance: none;
  border: none;
  outline: none;
  resize: none;
  background-color: transparent;

  display: block;
  width: 100%;

  box-shadow: var(--shadow-light);
  border-radius: 0;
  padding: 2.5rem 0 1.5rem;

  & ~ ${Tooltip} {
    visibility: hidden;
    opacity: 0;
    transform: translateY(1rem);
  }

  &::placeholder {
    opacity: 0.25;
  }

  &:not(:read-only):not(:disabled) {
    :hover {
      box-shadow: var(--shadow);
      ::placeholder {
        opacity: 0.5;
      }
    }
    &:focus {
      box-shadow: 0 1px 0 0 var(--color-info);
      ::placeholder {
        opacity: 0.5;
      }
    }
    &:hover,
    &:focus {
      & ~ svg {
        fill: var(--color-info);
      }
      & ~ ${Tooltip} {
        visibility: visible;
        opacity: 1;
        transform: translateY(0);
      }
    }
  }

  &:not(:placeholder-shown) {
    padding: 3rem 0 1rem;
  }
  &:placeholder-shown ~ ${Label} {
    display: none;
  }

  & ~ ${Error} {
    visibility: hidden;
    opacity: 0;
    transform: translateY(1rem);
  }

  /**
   * position svg chevron for select and search inputs
   */

  & ~ svg {
    position: absolute;
    bottom: 1rem;
    right: 0;
    pointer-events: none;
    fill: hsla(var(--hsl-text), 0.25);
  }

  /**
   * errors only shown on required inputs when blurred and with value
   * 1. show error sibling
   * 2. hide tooltip sibling (would crash into error)
   */

  &:required:not(:focus):not(:placeholder-shown) {
    &:invalid {
      color: var(--color-danger);
      box-shadow: 0 1px 0 0 var(--color-danger);

      & ~ ${Error} {
        visibility: visible; /* 1 */
        opacity: 1;
        transform: translateY(0);
      }

      & ~ ${Tooltip} {
        visibility: hidden; /* 2 */
        opacity: 0;
      }
    }
  }

  /**
   * remove autofill yellow background in webkit
   * 1. override text colour, inherit doesn't work
   * 2. transition very long, background-color doesn't work
   */

  &:-webkit-autofill {
    -webkit-text-fill-color: var(--color-text); /* 1 */
    transition: background-color 50000s ease-in-out; /* 2 */
  }

  /**
   * remove search input additional styling in webkit
   */

  &::-webkit-calendar-picker-indicator,
  &::-webkit-search-cancel-button {
    display: none;
  }
`;

Input.defaultProps = {
  as: 'input',
};
<commit_msg>add temporal inputs styles<commit_after>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import styled from 'styled-components';

import { Text } from '~components/primitives/Text';
import { Label } from '~components/text/Label';
import { Tooltip } from '~components/text/Tooltip';
import { Error } from '~components/text/Error';

// ─────────────────────────────────────────────────────────────────────────────
// helpers
// ─────────────────────────────────────────────────────────────────────────────

export const Input = styled(Text)`
  --shadow: 0 1px 0 0 hsla(var(--hsl-text), 0.5);
  --shadow-light: 0 1px 0 0 hsla(var(--hsl-text), 0.25);

  appearance: none;
  border: none;
  outline: none;
  resize: none;
  background-color: transparent;

  display: block;
  width: 100%;

  box-shadow: var(--shadow-light);
  border-radius: 0;
  padding: 2.5rem 0 1.5rem;

  & ~ ${Tooltip} {
    visibility: hidden;
    opacity: 0;
    transform: translateY(1rem);
  }

  &::placeholder {
    opacity: 0.25;
  }

  &:not(:read-only):not(:disabled) {
    :hover {
      box-shadow: var(--shadow);
      ::placeholder {
        opacity: 0.5;
      }
    }
    &:focus {
      box-shadow: 0 1px 0 0 var(--color-info);
      ::placeholder {
        opacity: 0.5;
      }
    }
    &:hover,
    &:focus {
      & ~ svg {
        fill: var(--color-info);
      }
      & ~ ${Tooltip} {
        visibility: visible;
        opacity: 1;
        transform: translateY(0);
      }
    }
  }

  &:not(:placeholder-shown) {
    padding: 3rem 0 1rem;
  }
  &:placeholder-shown ~ ${Label} {
    display: none;
  }

  & ~ ${Error} {
    visibility: hidden;
    opacity: 0;
    transform: translateY(1rem);
  }

  /**
   * position svg chevron for select and search inputs
   */

  & ~ svg {
    position: absolute;
    bottom: 1rem;
    right: 0;
    pointer-events: none;
    fill: hsla(var(--hsl-text), 0.25);
  }

  /**
   * errors only shown on required inputs when blurred and with value
   * 1. show error sibling
   * 2. hide tooltip sibling (would crash into error)
   */

  &:required:not(:focus):not(:placeholder-shown) {
    &:invalid:not([type*='date']):not([type='time']):not([type='month']) {
      color: var(--color-danger);
      box-shadow: 0 1px 0 0 var(--color-danger);

      & ~ ${Error} {
        visibility: visible; /* 1 */
        opacity: 1;
        transform: translateY(0);
      }

      & ~ ${Tooltip} {
        visibility: hidden; /* 2 */
        opacity: 0;
      }
    }
  }

  /**
   * remove autofill yellow background in webkit
   * 1. override text colour, inherit doesn't work
   * 2. transition very long, background-color doesn't work
   */

  &:-webkit-autofill {
    -webkit-text-fill-color: var(--color-text); /* 1 */
    transition: background-color 50000s ease-in-out; /* 2 */
  }

  /**
   * remove search input additional styling in webkit
   */

  &[type='search']::-webkit-calendar-picker-indicator,
  &[type='search']::-webkit-search-cancel-button {
    display: none;
  }

  /**
   * add temporal fields placeholder, doesn't work in Firefox
   */

  &[type*='date'],
  &[type='time'],
  &[type='month'] {
    &::before {
      content: attr(placeholder) ':';
      opacity: 0.25;
      margin: 0 1rem 0 0;
    }

    &::-webkit-calendar-picker-indicator {
      color: var(--color-info);
    }
    &::-webkit-datetime-edit-text {
      opacity: 0.5;
    }
  }
`;

Input.defaultProps = {
  as: 'input',
};
<|endoftext|>


<commit_before>import React from 'react'
import './ScreenBar.css'
import '../../variables.css'

const ScreenBar = ({ children, backgroundColor, className }) => (
  <div
    className={`ScreenBar ${className}`}
    style={{backgroundColor: backgroundColor}}
  >
    {children}
  </div>
)

ScreenBar.defaultProps = {
  backgroundColor: 'var(--primary)'
}

export default ScreenBar
<commit_msg>Add parentClass prop to ScreenBar<commit_after>import React from 'react'
import './ScreenBar.css'
import '../../variables.css'

const ScreenBar = ({ children, backgroundColor, className }) => (
  <div
    className={`ScreenBar ${className}`}
    style={{backgroundColor: backgroundColor}}
  >
    {children}
  </div>
)

ScreenBar.defaultProps = {
  backgroundColor: 'var(--primary)',
  parentClass: ""
}

export default ScreenBar
<|endoftext|>