<commit_before>import { drag } from "d3-drag";
import { scalePoint, scaleQuantize } from "d3-scale";
import { mouse, select, selectAll } from "d3-selection";

const d3 = {
  select,
  selectAll,
  scalePoint,
  scaleQuantize,
  drag,
  mouse
};

export class VisSlider {
  constructor(divId, data) {
    this.container = divId;
    $(this.container).html("");
    this.data = data;

    var currentYear = d3.select("body").attr("data-year");
    var maxYear = parseInt(d3.select("body").attr("data-max-year"));
    var years = [];
    Object.keys(this.data[0].values).forEach(function(year) {
      year = parseInt(year);
      if (year <= maxYear) years.push(year);
    });

    var margin = {
      right: 20,
      left: 10
    };
    var width =
      parseInt(d3.select(this.container).style("width")) -
      margin.left -
      margin.right;
    var height = 130;

    var svg = d3
      .select(this.container)
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + 0 + ")");

    // scalePoint derives from the ordinal scale, but allows a continuous range
    var x = d3
      .scalePoint()
      .domain(years)
      .range([0, width]);

    // Invert ordinal scale, from: https://gist.github.com/shimizu/808e0f5cadb6a63f28bb00082dc8fe3f
    x.invert = (function() {
      var domain = x.domain();
      var range = x.range();
      var scale = d3
        .scaleQuantize()
        .domain(range)
        .range(domain);

      return function(x) {
        return scale(x);
      };
    })();

    var slider = svg
      .append("g")
      .attr("class", "slider")
      .attr("transform", "translate(0," + height / 4 + ")");

    slider
      .append("line")
      .attr("class", "track")
      .attr("x1", x.range()[0])
      .attr("x2", x.range()[1])
      .select(function() {
        return this.parentNode.appendChild(this.cloneNode(true));
      })
      .attr("class", "track-inset")
      .select(function() {
        return this.parentNode.appendChild(this.cloneNode(true));
      })
      .attr("class", "track-overlay")
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    slider
      .append("g")
      .attr("class", "year-circles")
      .attr("transform", "translate(0," + 0 + ")")
      .selectAll("circle")
      .data(years)
      .enter()
      .append("circle")
      .attr("cx", d => x(d))
      .attr("r", 9)
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    slider
      .append("g")
      .attr("class", "ticks")
      .attr("transform", "translate(0," + 30 + ")")
      .selectAll("text")
      .data(years)
      .enter()
      .append("text")
      .attr("x", d => x(d))
      .text(year => year)
      .classed("active", d => d == currentYear)
      .attr("dx", d => {
        if (d == maxYear) {
          return 10;
        } else if (d == 2010) {
          return -10;
        } else {
          return 0;
        }
      })
      .attr("text-anchor", d => {
        if (d == maxYear) {
          return "end";
        } else if (d == 2010) {
          return "start";
        } else {
          return "middle";
        }
      })
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    var handle = slider
      .append("circle")
      .attr("class", "handle")
      .attr("cx", x(currentYear))
      .attr("r", 9)
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    function dragged(e) {
      var year = x.invert(d3.mouse(this)[0]);
      handle.attr("cx", x(year));

      slider
        .select(".ticks")
        .selectAll("text")
        .classed("active", d => d === year);
    }

    function endDrag(e) {
      var year = x.invert(d3.mouse(this)[0]);
      $(document).trigger("visSlider:yearChanged", year);
    }
  }
}
<commit_msg>year range based on the larger amount of years<commit_after>import { drag } from "d3-drag";
import { scalePoint, scaleQuantize } from "d3-scale";
import { mouse, select, selectAll } from "d3-selection";

const d3 = {
  select,
  selectAll,
  scalePoint,
  scaleQuantize,
  drag,
  mouse
};

export class VisSlider {
  constructor(divId, data) {
    this.container = divId;
    $(this.container).html("");
    this.data = data;

    var currentYear = d3.select("body").attr("data-year");
    var maxYear = parseInt(d3.select("body").attr("data-max-year"));
    var years = this.data
      .reduce((acc, { values }) => {
        // add the keys, if they're not included, up to maxYear
        Object.keys(values).forEach(x => {
          if (!acc.includes(+x) && +x <= maxYear) acc.push(+x);
        });
        return acc;
      }, [])
      .sort();

    var margin = {
      right: 20,
      left: 10
    };
    var width =
      parseInt(d3.select(this.container).style("width")) -
      margin.left -
      margin.right;
    var height = 130;

    var svg = d3
      .select(this.container)
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + 0 + ")");

    // scalePoint derives from the ordinal scale, but allows a continuous range
    var x = d3
      .scalePoint()
      .domain(years)
      .range([0, width]);

    // Invert ordinal scale, from: https://gist.github.com/shimizu/808e0f5cadb6a63f28bb00082dc8fe3f
    x.invert = (function() {
      var domain = x.domain();
      var range = x.range();
      var scale = d3
        .scaleQuantize()
        .domain(range)
        .range(domain);

      return function(x) {
        return scale(x);
      };
    })();

    var slider = svg
      .append("g")
      .attr("class", "slider")
      .attr("transform", "translate(0," + height / 4 + ")");

    slider
      .append("line")
      .attr("class", "track")
      .attr("x1", x.range()[0])
      .attr("x2", x.range()[1])
      .select(function() {
        return this.parentNode.appendChild(this.cloneNode(true));
      })
      .attr("class", "track-inset")
      .select(function() {
        return this.parentNode.appendChild(this.cloneNode(true));
      })
      .attr("class", "track-overlay")
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    slider
      .append("g")
      .attr("class", "year-circles")
      .attr("transform", "translate(0," + 0 + ")")
      .selectAll("circle")
      .data(years)
      .enter()
      .append("circle")
      .attr("cx", d => x(d))
      .attr("r", 9)
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    slider
      .append("g")
      .attr("class", "ticks")
      .attr("transform", "translate(0," + 30 + ")")
      .selectAll("text")
      .data(years)
      .enter()
      .append("text")
      .attr("x", d => x(d))
      .text(year => year)
      .classed("active", d => d == currentYear)
      .attr("dx", d => {
        if (d == maxYear) {
          return 10;
        } else if (d == 2010) {
          return -10;
        } else {
          return 0;
        }
      })
      .attr("text-anchor", d => {
        if (d == maxYear) {
          return "end";
        } else if (d == 2010) {
          return "start";
        } else {
          return "middle";
        }
      })
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    var handle = slider
      .append("circle")
      .attr("class", "handle")
      .attr("cx", x(currentYear))
      .attr("r", 9)
      .call(
        d3
          .drag()
          .on("start.interrupt", () => {
            slider.interrupt();
          })
          .on("start drag", dragged)
          .on("end", endDrag)
      );

    function dragged() {
      var year = x.invert(d3.mouse(this)[0]);
      handle.attr("cx", x(year));

      slider
        .select(".ticks")
        .selectAll("text")
        .classed("active", d => d === year);
    }

    function endDrag() {
      var year = x.invert(d3.mouse(this)[0]);
      $(document).trigger("visSlider:yearChanged", year);
    }
  }
}
<|endoftext|>


<commit_before>
define(["/partials/equation_editor/menu_symbol.html"], function(templateUrl) {

    return [function() {

        return {
            scope: {
                symbol: "=",
                mousePressed: "=",
            },
            restrict: "A",
            templateUrl: templateUrl,
            link: function(scope, element, attrs) {
                scope.name = "MENUSYMBOL"

                scope.$watch("symbol.menu.label", function(newLabel) {
                    if (newLabel && scope.symbol.menu.texLabel)
                        katex.render(scope.symbol.menu.label, element.find(".symbol-label")[0]);
                });

                scope.dragging = false;

                let editor = $(".equation-editor");

                let grabLocalX, grabLocalY;

                let lastPageX = 0;
                let lastPageY = 0;
                let grab = function(pageX, pageY, e) {

                    scope.dragging = true;

                    element.addClass("dragging");
                    scope.$apply();

                    let offset = element.offset();
                    grabLocalX = pageX - offset.left;
                    grabLocalY = pageY - offset.top;

                    lastPageX = pageX;
                    lastPageY = pageY;

                    scope.firstX = pageX;
                    scope.firstY = pageY;

                    $("body").on("mouseup", mouseup);
                    $("body").on("mousemove", mousemove);
                    $("body").on("touchend", touchend);
                    $("body").on("touchmove", touchmove);

                }

                let drag = function(pageX, pageY) {
                    let pageScroll = editor.offset().top;

                    pageX = pageX || lastPageX;
                    pageY = pageY || lastPageY;

                    if ("lockVertical" in attrs)
                        pageY = lastPageY;

                    let requiredPageLeft = pageX - grabLocalX;
                    let requiredPageTop = pageY - grabLocalY;

                    let offset = element.offset();

                    let pX = pageX - offset.top;
                    let pY = pageY - offset.left;

                    // Tell our parents that we've moved.

                    scope.$emit("symbolDrag", scope.symbol, requiredPageLeft, requiredPageTop - pageScroll, pageX - lastPageX, pageY - pageScroll - lastPageY, pageX, pageY - pageScroll);
                    lastPageX = pageX;
                    lastPageY = pageY;

                    // Parent may have moved. Recompute our position based on (potentially) new origin.
                    element.css("left", 0);
                    element.css("top", 0);
                    let originOffset = element.offset();

                    element.css("left", requiredPageLeft - originOffset.left);
                    element.css("top", requiredPageTop - originOffset.top);

                }

                let drop = function(pageX, pageY, e) {

                    let token = element.find(".symbol-token");
                    let tokenOffset = token.offset();

                    element.css("left", 0);
                    element.css("top", 0);

                    // fixes bug involving being able to drag symbols outside of the visible canvas,
                    let width = $(window).width();
                    let height = $(window).height();
                    let offCanvas = (e.clientX*100/width < 5 || e.clientY*100/height < 10) ? true : false;

                    // This ensures new symbols can be selected.
                    scope.$emit("symbolDrop", scope.symbol, pageX, pageY, pageX, pageY, offCanvas);

                    // This drags the hexagons around.
                    $("body").off("mouseup", mouseup);
                    $("body").off("mousemove", mousemove);
                    $("body").off("touchend", touchend);
                    $("body").off("touchmove", touchmove);

                    scope.dragging = false;
                    element.removeClass("dragging");
                    scope.$apply();
                }

                let mousedown = function(e) {
                    grab(e.pageX, e.pageY, e);
                    e.stopPropagation();
                    e.preventDefault();
                }

                let mouseup = function(e) {
                  if(scope.firstX == e.pageX && scope.firstY == e.pageY) {
                    let clicked = true;
                    scope.$emit("clicked", clicked);
                    let num = attrs.value;
                    scope.$emit("numberClicked", num);
                  }
                      drop(e.pageX, e.pageY, e);
                      clicked = false;
                      scope.$emit("clicked", clicked);
                      e.stopPropagation();
                      e.preventDefault();
                }

                let mousemove = function(e) {
                    drag(e.pageX, e.pageY);

                    e.stopPropagation();
                    e.preventDefault();
                }

                let touchstart = function(e) {
                    let ts = e.originalEvent.touches;
                    console.log(ts);
                    scope.mobileX = ts[0].pageX;
                    scope.mobileY = ts[0].pageY;
                    grab(ts[0].pageX, ts[0].pageY, e);

                    e.stopPropagation();
                    e.preventDefault();
                }

                let touchend = function(e) {
                  let ts = e.originalEvent.changedTouches;
                    console.debug(scope.mobileX + " " + ts[0].pageX);

                    console.log(ts, e.originalEvent);

                    if (scope.mobileX == ts[0].pageX && scope.mobileY == ts[0].pageY) {
                        let clicked = true;
                        scope.$emit("clicked", clicked);
                        console.debug("Registered as click");
                        let num = attrs.value;
                        scope.$emit("numberClicked", num);
                    }

                    drop(ts[0].pageX, ts[0].pageY, e);
                    clicked = false;
                    scope.$emit("clicked", clicked);
                    e.stopPropagation();
                    e.preventDefault();
                }

                let touchmove = function(e) {
                    let ts = e.originalEvent.touches;
                    drag(ts[0].pageX, ts[0].pageY, e);

                    e.stopPropagation();
                    e.preventDefault();
                }

                element.on("mousedown", mousedown);
                element.on("touchstart", touchstart);

                scope.$on("menuMoved", function() {
                    if (scope.dragging)
                        drag();
                })
            },
        };
    }];
});
<commit_msg>Fix an undefined variable that was getting in the way of the equation editor's menu clicks being served correctly.<commit_after>
define(["/partials/equation_editor/menu_symbol.html"], function(templateUrl) {

    return [function() {

        return {
            scope: {
                symbol: "=",
                mousePressed: "=",
            },
            restrict: "A",
            templateUrl: templateUrl,
            link: function(scope, element, attrs) {
                scope.name = "MENUSYMBOL"

                scope.$watch("symbol.menu.label", function(newLabel) {
                    if (newLabel && scope.symbol.menu.texLabel)
                        katex.render(scope.symbol.menu.label, element.find(".symbol-label")[0]);
                });

                scope.dragging = false;

                let editor = $(".equation-editor");

                let grabLocalX, grabLocalY;

                let lastPageX = 0;
                let lastPageY = 0;
                let grab = function(pageX, pageY, e) {

                    scope.dragging = true;

                    element.addClass("dragging");
                    scope.$apply();

                    let offset = element.offset();
                    grabLocalX = pageX - offset.left;
                    grabLocalY = pageY - offset.top;

                    lastPageX = pageX;
                    lastPageY = pageY;

                    scope.firstX = pageX;
                    scope.firstY = pageY;

                    $("body").on("mouseup", mouseup);
                    $("body").on("mousemove", mousemove);
                    $("body").on("touchend", touchend);
                    $("body").on("touchmove", touchmove);

                }

                let drag = function(pageX, pageY) {
                    let pageScroll = editor.offset().top;

                    pageX = pageX || lastPageX;
                    pageY = pageY || lastPageY;

                    if ("lockVertical" in attrs)
                        pageY = lastPageY;

                    let requiredPageLeft = pageX - grabLocalX;
                    let requiredPageTop = pageY - grabLocalY;

                    let offset = element.offset();

                    let pX = pageX - offset.top;
                    let pY = pageY - offset.left;

                    // Tell our parents that we've moved.

                    scope.$emit("symbolDrag", scope.symbol, requiredPageLeft, requiredPageTop - pageScroll, pageX - lastPageX, pageY - pageScroll - lastPageY, pageX, pageY - pageScroll);
                    lastPageX = pageX;
                    lastPageY = pageY;

                    // Parent may have moved. Recompute our position based on (potentially) new origin.
                    element.css("left", 0);
                    element.css("top", 0);
                    let originOffset = element.offset();

                    element.css("left", requiredPageLeft - originOffset.left);
                    element.css("top", requiredPageTop - originOffset.top);

                }

                let drop = function(pageX, pageY, e) {

                    let token = element.find(".symbol-token");
                    let tokenOffset = token.offset();

                    element.css("left", 0);
                    element.css("top", 0);

                    // fixes bug involving being able to drag symbols outside of the visible canvas,
                    let width = $(window).width();
                    let height = $(window).height();
                    let offCanvas = (e.clientX*100/width < 5 || e.clientY*100/height < 10) ? true : false;

                    // This ensures new symbols can be selected.
                    scope.$emit("symbolDrop", scope.symbol, pageX, pageY, pageX, pageY, offCanvas);

                    // This drags the hexagons around.
                    $("body").off("mouseup", mouseup);
                    $("body").off("mousemove", mousemove);
                    $("body").off("touchend", touchend);
                    $("body").off("touchmove", touchmove);

                    scope.dragging = false;
                    element.removeClass("dragging");
                    scope.$apply();
                }

                let mousedown = function(e) {
                    grab(e.pageX, e.pageY, e);
                    e.stopPropagation();
                    e.preventDefault();
                }

                let clicked = false;

                let mouseup = function(e) {
                    if(scope.firstX == e.pageX && scope.firstY == e.pageY) {
                        clicked = true;
                        scope.$emit("clicked", clicked);
                        let num = attrs.value;
                        scope.$emit("numberClicked", num);
                    }
                    drop(e.pageX, e.pageY, e);
                    scope.$emit("clicked", clicked);
                    e.stopPropagation();
                    e.preventDefault();
                }

                let mousemove = function(e) {
                    drag(e.pageX, e.pageY);

                    e.stopPropagation();
                    e.preventDefault();
                }

                let touchstart = function(e) {
                    let ts = e.originalEvent.touches;
                    console.log(ts);
                    scope.mobileX = ts[0].pageX;
                    scope.mobileY = ts[0].pageY;
                    grab(ts[0].pageX, ts[0].pageY, e);

                    e.stopPropagation();
                    e.preventDefault();
                }

                let touchend = function(e) {
                  let ts = e.originalEvent.changedTouches;
                    console.debug(scope.mobileX + " " + ts[0].pageX);

                    console.log(ts, e.originalEvent);

                    let clicked = false;

                    if (scope.mobileX == ts[0].pageX && scope.mobileY == ts[0].pageY) {
                        clicked = true;
                        scope.$emit("clicked", clicked);
                        console.debug("Registered as click");
                        let num = attrs.value;
                        scope.$emit("numberClicked", num);
                    }

                    drop(ts[0].pageX, ts[0].pageY, e);
                    scope.$emit("clicked", clicked);
                    e.stopPropagation();
                    e.preventDefault();
                }

                let touchmove = function(e) {
                    let ts = e.originalEvent.touches;
                    drag(ts[0].pageX, ts[0].pageY, e);

                    e.stopPropagation();
                    e.preventDefault();
                }

                element.on("mousedown", mousedown);
                element.on("touchstart", touchstart);

                scope.$on("menuMoved", function() {
                    if (scope.dragging)
                        drag();
                })
            },
        };
    }];
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import classnames from 'classnames'

import * as Paths from '../../../Paths'
import { OverlayWidget } from '../../components'
import { PressureForm, TargetList, PressureCount } from './components'
import { TellAFriend } from '../../../components'
import { fillWidget } from '../../actions'

/* TODO: Change static content by props
 * - title
 * - bgColor
 */
export class PressureWidget extends Component {
  constructor(props, context) {
    super(props, context)
    this.state = { filled: false }
  }

  componentWillReceiveProps(nextProps) {
    this.setState({ filled: this.props.saving && !nextProps.saving })
  }

  getTargetList() {
    const { targets } = this.props.widget.settings || { targets: '' }
    return targets && targets.split(';').filter(target => !!target.trim())
  }

  getEmailTarget(target) {
    const targetSplit = target.split('<')
    return targetSplit[1].replace('>', '')
  }

  handleSubmit(data) {
    const { widget, fill } = this.props
    const payload = {
      activist: {
        firstname: data.name,
        lastname: data.lastname,
        email: data.email
      },
      mail: {
        cc: this.getTargetList().map(target => this.getEmailTarget(target)),
        subject: data.subject,
        body: data.body
      }
    }
    fill(widget.id, payload)
  }

  handleOverlayOnClick(e) {
    const { mobilization, widget, editable } = this.props
    if (editable) {
      if (e) e.preventDefault()
      this.context.router.transitionTo(
        Paths.formPressureWidget(mobilization.id, widget.id)
      )
    }
  }

  render() {
    const { widget, editable, saving, mobilization: { header_font: headerFont } } = this.props
    const {
      main_color,
      title_text,
      button_text,
      reply_email,
      show_counter,
      count_text,
      pressure_subject,
      pressure_body
    } = widget.settings || {
      main_color: '#f23392',
      title_text: 'Envie um e-mail para quem pode tomar essa decisão',
      button_text: 'Enviar e-mail'
    }

    return (
      <OverlayWidget
        editable={editable}
        onClick={::this.handleOverlayOnClick}
        text="Clique para configurar o formulário de pressão direta"
      >
        {(widget.filled ?
          <TellAFriend {...this.props}
            message="E-mail enviado"
            href={window.location.origin}
          />
        :

              {title_text}

            <TargetList targets={::this.getTargetList() || []} />
            <PressureForm
              buttonText={(saving && !editable ? 'Enviando...' : button_text)}
              buttonColor={main_color}
              subject={pressure_subject}
              body={pressure_body}
              onSubmit={::this.handleSubmit}
            >
              {
                !show_counter || show_counter !== 'true' ? null : (
                  <PressureCount
                    value={widget.count || 0}
                    color={main_color}
                    text={count_text}
                  />
                )
              }
            </PressureForm>

        )}
      </OverlayWidget>
    )
  }
}

PressureWidget.propTypes = {
  editable: PropTypes.bool,
  mobilization: PropTypes.object.isRequired,
  widget: PropTypes.object.isRequired,
  // Use in fillWidget
  saving: PropTypes.bool,
  filled: PropTypes.bool,
  fill: PropTypes.func
}

PressureWidget.contextTypes = {
  router: PropTypes.object.isRequired,
}

const mapStateToProps = (globalState) => {
  return {
    saving: globalState.widgets.saving,
  }
}

const mapActionsToProps = {
  fill: fillWidget,
}

export default connect(mapStateToProps, mapActionsToProps)(PressureWidget)
<commit_msg>Change PressureWidget post-pressure sharing title message. #184<commit_after>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import classnames from 'classnames'

import * as Paths from '../../../Paths'
import { OverlayWidget } from '../../components'
import { PressureForm, TargetList, PressureCount } from './components'
import { TellAFriend } from '../../../components'
import { fillWidget } from '../../actions'

/* TODO: Change static content by props
 * - title
 * - bgColor
 */
export class PressureWidget extends Component {
  constructor(props, context) {
    super(props, context)
    this.state = { filled: false }
  }

  componentWillReceiveProps(nextProps) {
    this.setState({ filled: this.props.saving && !nextProps.saving })
  }

  getTargetList() {
    const { targets } = this.props.widget.settings || { targets: '' }
    return targets && targets.split(';').filter(target => !!target.trim())
  }

  getEmailTarget(target) {
    const targetSplit = target.split('<')
    return targetSplit[1].replace('>', '')
  }

  handleSubmit(data) {
    const { widget, fill } = this.props
    const payload = {
      activist: {
        firstname: data.name,
        lastname: data.lastname,
        email: data.email
      },
      mail: {
        cc: this.getTargetList().map(target => this.getEmailTarget(target)),
        subject: data.subject,
        body: data.body
      }
    }
    fill(widget.id, payload)
  }

  handleOverlayOnClick(e) {
    const { mobilization, widget, editable } = this.props
    if (editable) {
      if (e) e.preventDefault()
      this.context.router.transitionTo(
        Paths.formPressureWidget(mobilization.id, widget.id)
      )
    }
  }

  render() {
    const { widget, editable, saving, mobilization: { header_font: headerFont } } = this.props
    const {
      main_color,
      title_text,
      button_text,
      reply_email,
      show_counter,
      count_text,
      pressure_subject,
      pressure_body
    } = widget.settings || {
      main_color: '#f23392',
      title_text: 'Envie um e-mail para quem pode tomar essa decisão',
      button_text: 'Enviar e-mail'
    }

    return (
      <OverlayWidget
        editable={editable}
        onClick={::this.handleOverlayOnClick}
        text="Clique para configurar o formulário de pressão direta"
      >
        {(widget.filled ?
          <TellAFriend {...this.props}
            message="Pressão enviada"
            href={window.location.origin}
          />
        :

              {title_text}

            <TargetList targets={::this.getTargetList() || []} />
            <PressureForm
              buttonText={(saving && !editable ? 'Enviando...' : button_text)}
              buttonColor={main_color}
              subject={pressure_subject}
              body={pressure_body}
              onSubmit={::this.handleSubmit}
            >
              {
                !show_counter || show_counter !== 'true' ? null : (
                  <PressureCount
                    value={widget.count || 0}
                    color={main_color}
                    text={count_text}
                  />
                )
              }
            </PressureForm>

        )}
      </OverlayWidget>
    )
  }
}

PressureWidget.propTypes = {
  editable: PropTypes.bool,
  mobilization: PropTypes.object.isRequired,
  widget: PropTypes.object.isRequired,
  // Use in fillWidget
  saving: PropTypes.bool,
  filled: PropTypes.bool,
  fill: PropTypes.func
}

PressureWidget.contextTypes = {
  router: PropTypes.object.isRequired,
}

const mapStateToProps = (globalState) => {
  return {
    saving: globalState.widgets.saving,
  }
}

const mapActionsToProps = {
  fill: fillWidget,
}

export default connect(mapStateToProps, mapActionsToProps)(PressureWidget)
<|endoftext|>


<commit_before>var projects = {

	"smart facebook birthday assistant": {
		"image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
		"team":"lannisters sucks",
		"link":"https://github.com/sameer2800/IIITA_HACKS"

	},

	"<project-name>":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"IIIT-A Hacks",
                      "link":"https://github.com/iiitahacks"
                  },
         "Gather":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"checkmate",
                      "link":"https://github.com/magician03/gather"
                  },
          "KrayZ":{
                      "image":"https://openclipart.org/image/800px/svg_to_png/227918/Video-Game-Controller-Icon.png",
                      "team":"Krayz",
                      "link":"https://github.com/indreshattri/KrayZ"
                  },
	"voix": {
			"image":"http://images.clipartpanda.com/simple-microphone-vector-microphone-voice_318-26622.jpg",
			"team":"HopeLess",
			"link":"https://github.com/SaptakS/voix.git"
		},
           "Khana Khazana":{
                      "image":"http://www.quirkybyte.com/wp-content/uploads/2016/08/non-veg-dishes.jpg",
                      "team":"bits please",
                      "link":"https://github.com/akanshugupta9/khana-khazana"
                  },
                   "IIITA Cart":{
                      "image":"http://www.iconsplace.com/icons/preview/orange/shoping-cart-filled-256.png",
                      "team":"fellow hackers",
                      "link":"https://github.com/fellowhacker/iiitacart"
                  },
                  "Bunk Manager":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Error:404",
                      "link":"https://github.com/prankuragarwal/Error-404-Attendance_manager"
                  },

         "Smart Locator":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Droid Fueled",
                      "link":"https://github.com/betterclever/Smart-Locator"
		},
                   "Kisan Mandi":{
                      "image":"http://www.commondreams.org/sites/default/files/styles/cd_large/public/views-article/world_bank_farming.jpg?itok=jPw38H4p",
                      "team":"Android Geeks",
                      "link":"https://github.com/rajatm005/Kisan-Mandi"
                  },
                  "smiplify" : {
                  	"image": "http://www.simplifynow.org/wp-content/uploads/2014/10/simplify-fb-logo1.jpg",
                  	"team" : "and_the_winner_is",
                  	"link" : "https://github.com/adeora7/simplify"

                  },
         	 "Tweelyser":{
         		"image":"https://s3.amazonaws.com/images.seroundtable.com/twitter-logo-anlytics-1371211804.png",
         		"team":"daemons",
         		"link":"https//github.com/himanshub16/tweelyser"
         	},
         	"MoodSplash":{
         		"image":"http://data.whicdn.com/images/32772812/original.gif",
         		"team":"Gallifreyans",
         		"link":"https://github.com/sashafierce/MoodSplash"
         	},
         	"smartSwitch":{
                      "image":"http://cdn.toptenreviews.com/rev/prod/ce/61046-ankuoo-neo-wi-fi-light-switch-box.jpg",
                      "team":"MeetYourMakers",
                      "link":"https://github.com/sw4p/Smart-Switch"
                  },

}
<commit_msg>Update project.js<commit_after>var projects = {

	"smart facebook birthday assistant": {
		"image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
		"team":"lannisters sucks",
		"link":"https://github.com/sameer2800/IIITA_HACKS"

	},

	"<project-name>":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"IIIT-A Hacks",
                      "link":"https://github.com/iiitahacks"
                  },
          "Augumented Reality based pokemon fight game": {
          	"image": "http://orig03.deviantart.net/d3c1/f/2015/187/5/0/pikachu_icon_by_tooncito-d907bf9.jpg",
          	"team" : "noobGod",
          	"link" : "https://github.com/abshk111/PokemonApp"
          },
         "Gather":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"checkmate",
                      "link":"https://github.com/magician03/gather"
                  },
          "KrayZ":{
                      "image":"https://openclipart.org/image/800px/svg_to_png/227918/Video-Game-Controller-Icon.png",
                      "team":"Krayz",
                      "link":"https://github.com/indreshattri/KrayZ"
                  },
	"voix": {
			"image":"http://images.clipartpanda.com/simple-microphone-vector-microphone-voice_318-26622.jpg",
			"team":"HopeLess",
			"link":"https://github.com/SaptakS/voix.git"
		},
           "Khana Khazana":{
                      "image":"http://www.quirkybyte.com/wp-content/uploads/2016/08/non-veg-dishes.jpg",
                      "team":"bits please",
                      "link":"https://github.com/akanshugupta9/khana-khazana"
                  },
                   "IIITA Cart":{
                      "image":"http://www.iconsplace.com/icons/preview/orange/shoping-cart-filled-256.png",
                      "team":"fellow hackers",
                      "link":"https://github.com/fellowhacker/iiitacart"
                  },
                  "Bunk Manager":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Error:404",
                      "link":"https://github.com/prankuragarwal/Error-404-Attendance_manager"
                  },

         "Smart Locator":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Droid Fueled",
                      "link":"https://github.com/betterclever/Smart-Locator"
		},
                   "Kisan Mandi":{
                      "image":"http://www.commondreams.org/sites/default/files/styles/cd_large/public/views-article/world_bank_farming.jpg?itok=jPw38H4p",
                      "team":"Android Geeks",
                      "link":"https://github.com/rajatm005/Kisan-Mandi"
                  },
                  "smiplify" : {
                  	"image": "http://www.simplifynow.org/wp-content/uploads/2014/10/simplify-fb-logo1.jpg",
                  	"team" : "and_the_winner_is",
                  	"link" : "https://github.com/adeora7/simplify"

                  },
         	 "Tweelyser":{
         		"image":"https://s3.amazonaws.com/images.seroundtable.com/twitter-logo-anlytics-1371211804.png",
         		"team":"daemons",
         		"link":"https//github.com/himanshub16/tweelyser"
         	},
         	"MoodSplash":{
         		"image":"http://data.whicdn.com/images/32772812/original.gif",
         		"team":"Gallifreyans",
         		"link":"https://github.com/sashafierce/MoodSplash"
         	},
         	"smartSwitch":{
                      "image":"http://cdn.toptenreviews.com/rev/prod/ce/61046-ankuoo-neo-wi-fi-light-switch-box.jpg",
                      "team":"MeetYourMakers",
                      "link":"https://github.com/sw4p/Smart-Switch"
                  },

}
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var MinMaxCharSelect = {
    controller: MinMaxCharSelectCtrl,
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '='
    },
    template: [
      '
',
      '

',
      '
{{:: vm.activeCharacters[0].genderRace }}
',
      '
 {{ vm.activeCharacters[0].level }}
',
      '
{{ vm.activeCharacters[0].powerLevel }}
',
      '

',
      '
',
      '

'
    ].join('')
  };

  var MinMaxCharPopup = {
    controller: MinMaxCharSelectCtrl,
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '='
    },
    template: [
      '
',
      '

',
      '
{{:: char.genderRace }}
',
      '
 {{ char.level }}
',
      '
{{ char.powerLevel }}
',
      '
',
    ].join('')
  };

  angular.module('dimApp')
    .component('dimMinMaxCharSelect', MinMaxCharSelect)
    .component('dimMinMaxCharPopup', MinMaxCharPopup);

  MinMaxCharSelectCtrl.$inject = ['$scope', 'ngDialog'];

  function MinMaxCharSelectCtrl($scope, ngDialog) {
    var vm = this;
    var dialogResult = null;

    vm.selectCharacter = function selectCharacter(index) {
      vm.selectedCharacter = index;
      ngDialog.closeAll();
    };

    vm.openCharSelectPopup = function openCharSelectPopup(e) {
      e.stopPropagation();

      if (dialogResult === null) {
        ngDialog.closeAll();

        dialogResult = ngDialog.open({
          template: [
            '
',
            '  <dim-min-max-char-popup active-characters="vm.activeCharacters" selected-character="vm.selectedCharacter"></dim-min-max-char-popup>',
            '
'].join(''),
          plain: true,
          appendTo: 'div[id="char-select"]',
          overlay: false,
          className: 'char-popup',
          showClose: false,
          scope: $scope
        });

        dialogResult.closePromise.then(function() {
          dialogResult = null;
        });
      } else {
        dialogResult.close();
      }
    };
  }
})();
<commit_msg>Fix for selection of character not updating parent scope<commit_after>(function() {
  'use strict';

  var MinMaxCharSelect = {
    controller: MinMaxCharSelectCtrl,
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '='
    },
    template: [
      '
',
      '

',
      '
{{ vm.activeCharacters[vm.selectedCharacter].genderRace }}
',
      '
 {{ vm.activeCharacters[vm.selectedCharacter].level }}
',
      '
{{ vm.activeCharacters[vm.selectedCharacter].powerLevel }}
',
      '

',
      '
',
      '

'
    ].join('')
  };

  var MinMaxCharPopup = {
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '=',
      onSelectedChange: '&'
    },
    template: [
      '
',
      '

',
      '
{{:: char.genderRace }}
',
      '
 {{ char.level }}
',
      '
{{ char.powerLevel }}
',
      '
',
    ].join('')
  };

  angular.module('dimApp')
    .component('dimMinMaxCharSelect', MinMaxCharSelect)
    .component('dimMinMaxCharPopup', MinMaxCharPopup);

  MinMaxCharSelectCtrl.$inject = ['$scope', 'ngDialog'];

  function MinMaxCharSelectCtrl($scope, ngDialog) {
    var vm = this;
    var dialogResult = null;

    vm.onSelectedChange = function onSelectedChange(idx) {
      vm.selectedCharacter = idx;
      ngDialog.closeAll();
    };

    vm.openCharSelectPopup = function openCharSelectPopup(e) {
      e.stopPropagation();

      if (dialogResult === null) {
        ngDialog.closeAll();

        dialogResult = ngDialog.open({
          template: [
            '
',
            '  <dim-min-max-char-popup active-characters="vm.activeCharacters" selected-character="vm.selectedCharacter" on-selected-change="vm.onSelectedChange(idx)"></dim-min-max-char-popup>',
            '
'].join(''),
          plain: true,
          appendTo: 'div[id="char-select"]',
          overlay: false,
          className: 'char-popup',
          showClose: false,
          scope: $scope
        });

        dialogResult.closePromise.then(function() {
          dialogResult = null;
        });
      } else {
        dialogResult.close();
      }
    };
  }
})();
<|endoftext|>


<commit_before>var projects = {

		"<project-name>":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"IIIT-A Hacks",
                      "link":"https://github.com/iiitahacks"
                },

          	"Augumented Reality based pokemon fight game": {
          		"image": "http://orig03.deviantart.net/d3c1/f/2015/187/5/0/pikachu_icon_by_tooncito-d907bf9.jpg",
          		"team" : "noobGod",
          		"link" : "https://github.com/abshk111/PokemonApp"
        	},

         	"Gather": {
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"checkmate",
                      "link":"https://github.com/magician03/gather"
                },

          	"KrayZ": {
                      "image":"https://openclipart.org/image/800px/svg_to_png/227918/Video-Game-Controller-Icon.png",
                      "team":"Krayz",
                      "link":"https://github.com/indreshattri/KrayZ"
                },

		"voix": {
			"image":"http://images.clipartpanda.com/simple-microphone-vector-microphone-voice_318-26622.jpg",
			"team":"HopeLess",
			"link":"https://github.com/SaptakS/voix.git"
		},

           	"Khana Khazana":{
                      "image":"http://www.quirkybyte.com/wp-content/uploads/2016/08/non-veg-dishes.jpg",
                      "team":"bits please",
                      "link":"https://github.com/akanshugupta9/khana-khazana"
                },

                "IIITA Cart": {
                      "image":"http://www.iconsplace.com/icons/preview/orange/shoping-cart-filled-256.png",
                      "team":"fellow hackers",
                      "link":"https://github.com/fellowhacker/iiitacart"
                },

                "Bunk Manager":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Error:404",
                      "link":"https://github.com/prankuragarwal/Error-404-Attendance_manager"
                },

         	"Smart Locator":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Droid Fueled",
                      "link":"https://github.com/betterclever/Smart-Locator"
		},

                "Kisan Mandi":{
                      "image":"http://www.commondreams.org/sites/default/files/styles/cd_large/public/views-article/world_bank_farming.jpg?itok=jPw38H4p",
                      "team":"Android Geeks",
                      "link":"https://github.com/rajatm005/Kisan-Mandi"
                },

                "smiplify" : {
                  	"image": "http://www.simplifynow.org/wp-content/uploads/2014/10/simplify-fb-logo1.jpg",
                  	"team" : "and_the_winner_is",
                  	"link" : "https://github.com/adeora7/simplify"

                },

         	 "Tweelyser":{
         		"image":"https://s3.amazonaws.com/images.seroundtable.com/twitter-logo-anlytics-1371211804.png",
         		"team":"daemons",
         		"link":"https//github.com/himanshub16/tweelyser"
         	},

         	"MoodSplash":{
         		"image":"http://data.whicdn.com/images/32772812/original.gif",
         		"team":"Gallifreyans",
         		"link":"https://github.com/sashafierce/MoodSplash"
         	},

         	"smartSwitch":{
                      "image":"http://cdn.toptenreviews.com/rev/prod/ce/61046-ankuoo-neo-wi-fi-light-switch-box.jpg",
                      "team":"MeetYourMakers",
                      "link":"https://github.com/sw4p/Smart-Switch"
                },

                "Weather-stat":{
                      "image":"https://github.com/ritikamotwani/iiitahacks.github.io/blob/master/projects/images/weather-icon.ico",
                      "team":"kuchbhi",
                      "link":"https://github.com/ritikamotwani/Weather-stat"
                },

                "Imsify":{
         		"image":"https://github.com/Trailblazerr1/iiita-hacks-musify/blob/master/app/static/img/party.jpg",
          		"team":"NullVectors",
          		"link":"https://github.com/Trailblazerr1/iiita-hacks-musify"
           	},

           	"smart facebook birthday assistant": {
		"image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
		"team":"lannisters sucks",
		"link":"https://github.com/sameer2800/IIITA_HACKS"
		},
		"No-Distractiton Atom plug-in":{
         		"image":"https://camo.githubusercontent.com/2c08678c355ef710338f4abf170cc999501b4093/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f36393136392f323239303235302f63333564383637612d613031372d313165332d383662652d6364376335626633666639622e676966",
          		"team":"Vimjas",
          		"link":"https://github.com/dewana-dewan/IIITA-Hacks"
           	},
           "Ungli": {
      "image":"https://raw.githubusercontent.com/BelieveC/Ungli/master/Preview/Ungli.png",
      "team":"corrupt_shooters",
      "link":"https://github.com/BelieveC/Ungli"
     }

}
<commit_msg>Add submission for team W.I.C.K.E.D.<commit_after>var projects = {

		"<project-name>":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"IIIT-A Hacks",
                      "link":"https://github.com/iiitahacks"
                },

          	"Augumented Reality based pokemon fight game": {
          		"image": "http://orig03.deviantart.net/d3c1/f/2015/187/5/0/pikachu_icon_by_tooncito-d907bf9.jpg",
          		"team" : "noobGod",
          		"link" : "https://github.com/abshk111/PokemonApp"
        	},

         	"Gather": {
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"checkmate",
                      "link":"https://github.com/magician03/gather"
                },

          	"KrayZ": {
                      "image":"https://openclipart.org/image/800px/svg_to_png/227918/Video-Game-Controller-Icon.png",
                      "team":"Krayz",
                      "link":"https://github.com/indreshattri/KrayZ"
                },

		"voix": {
			"image":"http://images.clipartpanda.com/simple-microphone-vector-microphone-voice_318-26622.jpg",
			"team":"HopeLess",
			"link":"https://github.com/SaptakS/voix.git"
		},

           	"Khana Khazana":{
                      "image":"http://www.quirkybyte.com/wp-content/uploads/2016/08/non-veg-dishes.jpg",
                      "team":"bits please",
                      "link":"https://github.com/akanshugupta9/khana-khazana"
                },

                "IIITA Cart": {
                      "image":"http://www.iconsplace.com/icons/preview/orange/shoping-cart-filled-256.png",
                      "team":"fellow hackers",
                      "link":"https://github.com/fellowhacker/iiitacart"
                },

                "Bunk Manager":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Error:404",
                      "link":"https://github.com/prankuragarwal/Error-404-Attendance_manager"
                },

         	"Smart Locator":{
                      "image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
                      "team":"Droid Fueled",
                      "link":"https://github.com/betterclever/Smart-Locator"
		},

                "Kisan Mandi":{
                      "image":"http://www.commondreams.org/sites/default/files/styles/cd_large/public/views-article/world_bank_farming.jpg?itok=jPw38H4p",
                      "team":"Android Geeks",
                      "link":"https://github.com/rajatm005/Kisan-Mandi"
                },

                "smiplify" : {
                  	"image": "http://www.simplifynow.org/wp-content/uploads/2014/10/simplify-fb-logo1.jpg",
                  	"team" : "and_the_winner_is",
                  	"link" : "https://github.com/adeora7/simplify"

                },

         	 "Tweelyser":{
         		"image":"https://s3.amazonaws.com/images.seroundtable.com/twitter-logo-anlytics-1371211804.png",
         		"team":"daemons",
         		"link":"https//github.com/himanshub16/tweelyser"
         	},

         	"MoodSplash":{
         		"image":"http://data.whicdn.com/images/32772812/original.gif",
         		"team":"Gallifreyans",
         		"link":"https://github.com/sashafierce/MoodSplash"
         	},

         	"smartSwitch":{
                      "image":"http://cdn.toptenreviews.com/rev/prod/ce/61046-ankuoo-neo-wi-fi-light-switch-box.jpg",
                      "team":"MeetYourMakers",
                      "link":"https://github.com/sw4p/Smart-Switch"
                },

                "Weather-stat":{
                      "image":"https://github.com/ritikamotwani/iiitahacks.github.io/blob/master/projects/images/weather-icon.ico",
                      "team":"kuchbhi",
                      "link":"https://github.com/ritikamotwani/Weather-stat"
                },

                "Imsify":{
         		"image":"https://github.com/Trailblazerr1/iiita-hacks-musify/blob/master/app/static/img/party.jpg",
          		"team":"NullVectors",
          		"link":"https://github.com/Trailblazerr1/iiita-hacks-musify"
           	},

           	"smart facebook birthday assistant": {
		"image":"https://avatars3.githubusercontent.com/u/22071699?v=3&s=200",
		"team":"lannisters sucks",
		"link":"https://github.com/sameer2800/IIITA_HACKS"
		},
		"No-Distractiton Atom plug-in":{
         		"image":"https://camo.githubusercontent.com/2c08678c355ef710338f4abf170cc999501b4093/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f36393136392f323239303235302f63333564383637612d613031372d313165332d383662652d6364376335626633666639622e676966",
          		"team":"Vimjas",
          		"link":"https://github.com/dewana-dewan/IIITA-Hacks"
           	},
           "Ungli": {
      "image":"https://raw.githubusercontent.com/BelieveC/Ungli/master/Preview/Ungli.png",
      "team":"corrupt_shooters",
      "link":"https://github.com/BelieveC/Ungli"
     },
     "Third Eye":{
 +                      "image":"https://raw.githubusercontent.com/RaghavTheCyberdude/ThirdEye/master/media/logo.png",
 +                      "team":"W.I.C.K.E.D.",
 +                      "link":"'https://github.com/RaghavTheCyberdude/ThirdEye"
 +                  }

}
<|endoftext|>


<commit_before>// @flow
import * as NOTIFICATION_TYPES from 'constants/subscriptions';
import { PAGE_SIZE } from 'constants/claim';
import * as MODALS from 'constants/modal_types';
import { ipcRenderer } from 'electron';
import { doOpenModal } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import { setSubscriptionLatest, doUpdateUnreadSubscriptions } from 'redux/actions/subscriptions';
import { makeSelectUnreadByChannel } from 'redux/selectors/subscriptions';
import { selectBadgeNumber } from 'redux/selectors/app';
import {
  ACTIONS,
  SETTINGS,
  Lbry,
  Lbryapi,
  buildURI,
  makeSelectCostInfoForUri,
  makeSelectFileInfoForUri,
  selectFileInfosByOutpoint,
  selectDownloadingByOutpoint,
  selectBalance,
  makeSelectChannelForClaimUri,
  parseURI,
  creditsToString,
  doError,
} from 'lbry-redux';
import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/selectors/settings';
import setBadge from 'util/set-badge';
import analytics from 'analytics';

const DOWNLOAD_POLL_INTERVAL = 250;

export function doUpdateLoadStatus(uri: string, outpoint: string) {
  return (dispatch, getState) => {
    const setNextStatusUpdate = () =>
      setTimeout(() => {
        // We need to check if outpoint still exists first because user are able to delete file (outpoint) while downloading.
        // If a file is already deleted, no point to still try update load status
        const byOutpoint = selectFileInfosByOutpoint(getState());
        if (byOutpoint[outpoint]) {
          dispatch(doUpdateLoadStatus(uri, outpoint));
        }
      }, DOWNLOAD_POLL_INTERVAL);
    Lbry.file_list({
      outpoint,
      full_status: true,
    }).then(([fileInfo]) => {
      if (!fileInfo || fileInfo.written_bytes === 0) {
        // download hasn't started yet
        setNextStatusUpdate();
      } else if (fileInfo.completed) {
        const state = getState();
        // TODO this isn't going to get called if they reload the client before
        // the download finished
        dispatch({
          type: ACTIONS.DOWNLOADING_COMPLETED,
          data: {
            uri,
            outpoint,
            fileInfo,
          },
        });

        const badgeNumber = selectBadgeNumber(state);
        setBadge(badgeNumber === 0 ? '' : `${badgeNumber}`);

        // Disabling this for now because it's confusing for new users that don't realize files are actually being downloaded
        // This should move inside of the app
        // const totalProgress = selectTotalDownloadProgress(state);
        // setProgressBar(totalProgress);

        const channelUri = makeSelectChannelForClaimUri(uri, true)(state);
        const { claimName: channelName } = parseURI(channelUri);

        const unreadForChannel = makeSelectUnreadByChannel(channelUri)(state);
        if (unreadForChannel && unreadForChannel.type === NOTIFICATION_TYPES.DOWNLOADING) {
          const count = unreadForChannel.uris.length;

          if (selectosNotificationsEnabled(state)) {
            const notif = new window.Notification(channelName, {
              body: `Posted ${fileInfo.metadata.title}${
                count > 1 && count < 10 ? ` and ${count - 1} other new items` : ''
              }${count > 9 ? ' and 9+ other new items' : ''}`,
              silent: false,
            });
            notif.onclick = () => {
              dispatch(
                doNavigate('/show', {
                  uri,
                })
              );
            };
          }

          dispatch(doUpdateUnreadSubscriptions(channelUri, null, NOTIFICATION_TYPES.DOWNLOADED));
        } else {
          // If notifications are disabled(false) just return
          if (!selectosNotificationsEnabled(getState())) return;

          const notif = new window.Notification('LBRY Download Complete', {
            body: fileInfo.metadata.title,
            silent: false,
          });
          notif.onclick = () => {
            ipcRenderer.send('focusWindow', 'main');
          };
        }
      } else {
        // ready to play
        const { total_bytes: totalBytes, written_bytes: writtenBytes } = fileInfo;
        const progress = (writtenBytes / totalBytes) * 100;

        dispatch({
          type: ACTIONS.DOWNLOADING_PROGRESSED,
          data: {
            uri,
            outpoint,
            fileInfo,
            progress,
          },
        });

        // const totalProgress = selectTotalDownloadProgress(getState());
        // setProgressBar(totalProgress);
        setNextStatusUpdate();
      }
    });
  };
}

export function doStartDownload(uri, outpoint) {
  return (dispatch, getState) => {
    const state = getState();

    if (!outpoint) {
      throw new Error('outpoint is required to begin a download');
    }

    const { downloadingByOutpoint = {} } = state.fileInfo;

    if (downloadingByOutpoint[outpoint]) return;

    Lbry.file_list({ outpoint, full_status: true }).then(([fileInfo]) => {
      dispatch({
        type: ACTIONS.DOWNLOADING_STARTED,
        data: {
          uri,
          outpoint,
          fileInfo,
        },
      });

      dispatch(doUpdateLoadStatus(uri, outpoint));
    });
  };
}

export function doDownloadFile(uri, streamInfo) {
  return dispatch => {
    dispatch(doStartDownload(uri, streamInfo.outpoint));
  };
}

export function doSetPlayingUri(uri) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_PLAYING_URI,
      data: { uri },
    });
  };
}

function handleLoadVideoError(uri, errorType = '') {
  return (dispatch, getState) => {
    // suppress error when another media is playing
    const { playingUri } = getState().content;
    if (playingUri && playingUri === uri) {
      dispatch({
        type: ACTIONS.LOADING_VIDEO_FAILED,
        data: { uri },
      });
      dispatch(doSetPlayingUri(null));
      // this is not working, but should be it's own separate modal in the future (https://github.com/lbryio/lbry-desktop/issues/892)
      if (errorType === 'timeout') {
        doOpenModal(MODALS.FILE_TIMEOUT, { uri });
      } else {
        dispatch(
          doError(
            `Failed to download ${uri}, please try again or see error details:\n${errorType}\n\nIf this problem persists, visit https://lbry.io/support for help. `
          )
        );
      }
    }
  };
}

export function doLoadVideo(uri, shouldRecordViewEvent) {
  return dispatch => {
    dispatch({
      type: ACTIONS.LOADING_VIDEO_STARTED,
      data: {
        uri,
      },
    });

    Lbry.get({ uri })
      .then(streamInfo => {
        // need error code from SDK to capture properly
        const timeout =
          streamInfo === null || typeof streamInfo !== 'object' || streamInfo.error === 'Timeout';

        if (timeout) {
          dispatch(handleLoadVideoError(uri, 'timeout'));
        } else {
          dispatch(doDownloadFile(uri, streamInfo));

          if (shouldRecordViewEvent) {
            analytics.apiLogView(
              `${streamInfo.claim_name}#${streamInfo.claim_id}`,
              streamInfo.outpoint,
              streamInfo.claim_id
            );
          }
        }
      })
      .catch(error => {
        dispatch(handleLoadVideoError(uri, error));
      });
  };
}

export function doPurchaseUri(uri, specificCostInfo, shouldRecordViewEvent) {
  return (dispatch, getState) => {
    const state = getState();
    const balance = selectBalance(state);
    const fileInfo = makeSelectFileInfoForUri(uri)(state);
    const downloadingByOutpoint = selectDownloadingByOutpoint(state);
    const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];

    function attemptPlay(cost, instantPurchaseMax = null) {
      if (cost > 0 && (!instantPurchaseMax || cost > instantPurchaseMax)) {
        dispatch(doOpenModal(MODALS.AFFIRM_PURCHASE, { uri }));
      } else {
        dispatch(doLoadVideo(uri, shouldRecordViewEvent));
      }
    }

    // we already fully downloaded the file.
    if (fileInfo && fileInfo.completed) {
      // If written_bytes is false that means the user has deleted/moved the
      // file manually on their file system, so we need to dispatch a
      // doLoadVideo action to reconstruct the file from the blobs
      if (!fileInfo.written_bytes) dispatch(doLoadVideo(uri, shouldRecordViewEvent));

      Promise.resolve();
      return;
    }

    // we are already downloading the file
    if (alreadyDownloading) {
      Promise.resolve();
      return;
    }

    const costInfo = makeSelectCostInfoForUri(uri)(state) || specificCostInfo;
    const { cost } = costInfo;

    if (cost > balance) {
      dispatch(doSetPlayingUri(null));
      dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
      Promise.resolve();
      return;
    }

    if (cost === 0 || !makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED)(state)) {
      attemptPlay(cost);
    } else {
      const instantPurchaseMax = makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state);
      if (instantPurchaseMax.currency === 'LBC') {
        attemptPlay(cost, instantPurchaseMax.amount);
      } else {
        // Need to convert currency of instant purchase maximum before trying to play
        Lbryapi.getExchangeRates().then(({ LBC_USD }) => {
          attemptPlay(cost, instantPurchaseMax.amount / LBC_USD);
        });
      }
    }
  };
}

export function doFetchClaimsByChannel(
  uri: string,
  page: number = 1,
  pageSize: number = PAGE_SIZE
) {
  return dispatch => {
    dispatch({
      type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
      data: { uri, page },
    });

    Lbry.claim_list_by_channel({ uri, page, page_size: pageSize }).then(result => {
      const claimResult = result[uri] || {};
      const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult;

      if (claimsInChannel && claimsInChannel.length) {
        if (page === 1) {
          const latest = claimsInChannel[0];
          dispatch(
            setSubscriptionLatest(
              {
                channelName: latest.channel_name,
                uri: buildURI(
                  {
                    contentName: latest.channel_name,
                    claimId: latest.value.publisherSignature.certificateId,
                  },
                  false
                ),
              },
              buildURI({ contentName: latest.name, claimId: latest.claim_id }, false)
            )
          );
        }
      }

      dispatch({
        type: ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED,
        data: {
          uri,
          claims: claimsInChannel || [],
          page: returnedPage || undefined,
        },
      });
    });
  };
}

export function doPlayUri(uri) {
  return dispatch => {
    dispatch(doSetPlayingUri(uri));
    dispatch(doPurchaseUri(uri));
  };
}

export function doFetchChannelListMine() {
  return dispatch => {
    dispatch({
      type: ACTIONS.FETCH_CHANNEL_LIST_STARTED,
    });

    const callback = channels => {
      dispatch({
        type: ACTIONS.FETCH_CHANNEL_LIST_COMPLETED,
        data: { claims: channels },
      });
    };

    Lbry.channel_list().then(callback);
  };
}

export function doCreateChannel(name: string, amount: number) {
  return dispatch => {
    dispatch({
      type: ACTIONS.CREATE_CHANNEL_STARTED,
    });

    return new Promise((resolve, reject) => {
      Lbry.channel_new({
        channel_name: name,
        amount: creditsToString(amount),
      }).then(
        newChannelClaim => {
          const channelClaim = newChannelClaim;
          channelClaim.name = name;
          dispatch({
            type: ACTIONS.CREATE_CHANNEL_COMPLETED,
            data: { channelClaim },
          });
          resolve(channelClaim);
        },
        error => {
          reject(error);
        }
      );
    });
  };
}

export function savePosition(claimId: string, outpoint: string, position: number) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_CONTENT_POSITION,
      data: { claimId, outpoint, position },
    });
  };
}

export function doSetContentHistoryItem(uri: string) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_CONTENT_LAST_VIEWED,
      data: { uri, lastViewed: Date.now() },
    });
  };
}

export function doClearContentHistoryUri(uri: string) {
  return dispatch => {
    dispatch({
      type: ACTIONS.CLEAR_CONTENT_HISTORY_URI,
      data: { uri },
    });
  };
}

export function doClearContentHistoryAll() {
  return dispatch => {
    dispatch({ type: ACTIONS.CLEAR_CONTENT_HISTORY_ALL });
  };
}

export function doSetHistoryPage(page) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_CONTENT_HISTORY_PAGE,
      data: { page },
    });
  };
}
<commit_msg>Add extra newline space on error message<commit_after>// @flow
import * as NOTIFICATION_TYPES from 'constants/subscriptions';
import { PAGE_SIZE } from 'constants/claim';
import * as MODALS from 'constants/modal_types';
import { ipcRenderer } from 'electron';
import { doOpenModal } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import { setSubscriptionLatest, doUpdateUnreadSubscriptions } from 'redux/actions/subscriptions';
import { makeSelectUnreadByChannel } from 'redux/selectors/subscriptions';
import { selectBadgeNumber } from 'redux/selectors/app';
import {
  ACTIONS,
  SETTINGS,
  Lbry,
  Lbryapi,
  buildURI,
  makeSelectCostInfoForUri,
  makeSelectFileInfoForUri,
  selectFileInfosByOutpoint,
  selectDownloadingByOutpoint,
  selectBalance,
  makeSelectChannelForClaimUri,
  parseURI,
  creditsToString,
  doError,
} from 'lbry-redux';
import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/selectors/settings';
import setBadge from 'util/set-badge';
import analytics from 'analytics';

const DOWNLOAD_POLL_INTERVAL = 250;

export function doUpdateLoadStatus(uri: string, outpoint: string) {
  return (dispatch, getState) => {
    const setNextStatusUpdate = () =>
      setTimeout(() => {
        // We need to check if outpoint still exists first because user are able to delete file (outpoint) while downloading.
        // If a file is already deleted, no point to still try update load status
        const byOutpoint = selectFileInfosByOutpoint(getState());
        if (byOutpoint[outpoint]) {
          dispatch(doUpdateLoadStatus(uri, outpoint));
        }
      }, DOWNLOAD_POLL_INTERVAL);
    Lbry.file_list({
      outpoint,
      full_status: true,
    }).then(([fileInfo]) => {
      if (!fileInfo || fileInfo.written_bytes === 0) {
        // download hasn't started yet
        setNextStatusUpdate();
      } else if (fileInfo.completed) {
        const state = getState();
        // TODO this isn't going to get called if they reload the client before
        // the download finished
        dispatch({
          type: ACTIONS.DOWNLOADING_COMPLETED,
          data: {
            uri,
            outpoint,
            fileInfo,
          },
        });

        const badgeNumber = selectBadgeNumber(state);
        setBadge(badgeNumber === 0 ? '' : `${badgeNumber}`);

        // Disabling this for now because it's confusing for new users that don't realize files are actually being downloaded
        // This should move inside of the app
        // const totalProgress = selectTotalDownloadProgress(state);
        // setProgressBar(totalProgress);

        const channelUri = makeSelectChannelForClaimUri(uri, true)(state);
        const { claimName: channelName } = parseURI(channelUri);

        const unreadForChannel = makeSelectUnreadByChannel(channelUri)(state);
        if (unreadForChannel && unreadForChannel.type === NOTIFICATION_TYPES.DOWNLOADING) {
          const count = unreadForChannel.uris.length;

          if (selectosNotificationsEnabled(state)) {
            const notif = new window.Notification(channelName, {
              body: `Posted ${fileInfo.metadata.title}${
                count > 1 && count < 10 ? ` and ${count - 1} other new items` : ''
              }${count > 9 ? ' and 9+ other new items' : ''}`,
              silent: false,
            });
            notif.onclick = () => {
              dispatch(
                doNavigate('/show', {
                  uri,
                })
              );
            };
          }

          dispatch(doUpdateUnreadSubscriptions(channelUri, null, NOTIFICATION_TYPES.DOWNLOADED));
        } else {
          // If notifications are disabled(false) just return
          if (!selectosNotificationsEnabled(getState())) return;

          const notif = new window.Notification('LBRY Download Complete', {
            body: fileInfo.metadata.title,
            silent: false,
          });
          notif.onclick = () => {
            ipcRenderer.send('focusWindow', 'main');
          };
        }
      } else {
        // ready to play
        const { total_bytes: totalBytes, written_bytes: writtenBytes } = fileInfo;
        const progress = (writtenBytes / totalBytes) * 100;

        dispatch({
          type: ACTIONS.DOWNLOADING_PROGRESSED,
          data: {
            uri,
            outpoint,
            fileInfo,
            progress,
          },
        });

        // const totalProgress = selectTotalDownloadProgress(getState());
        // setProgressBar(totalProgress);
        setNextStatusUpdate();
      }
    });
  };
}

export function doStartDownload(uri, outpoint) {
  return (dispatch, getState) => {
    const state = getState();

    if (!outpoint) {
      throw new Error('outpoint is required to begin a download');
    }

    const { downloadingByOutpoint = {} } = state.fileInfo;

    if (downloadingByOutpoint[outpoint]) return;

    Lbry.file_list({ outpoint, full_status: true }).then(([fileInfo]) => {
      dispatch({
        type: ACTIONS.DOWNLOADING_STARTED,
        data: {
          uri,
          outpoint,
          fileInfo,
        },
      });

      dispatch(doUpdateLoadStatus(uri, outpoint));
    });
  };
}

export function doDownloadFile(uri, streamInfo) {
  return dispatch => {
    dispatch(doStartDownload(uri, streamInfo.outpoint));
  };
}

export function doSetPlayingUri(uri) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_PLAYING_URI,
      data: { uri },
    });
  };
}

function handleLoadVideoError(uri, errorType = '') {
  return (dispatch, getState) => {
    // suppress error when another media is playing
    const { playingUri } = getState().content;
    if (playingUri && playingUri === uri) {
      dispatch({
        type: ACTIONS.LOADING_VIDEO_FAILED,
        data: { uri },
      });
      dispatch(doSetPlayingUri(null));
      // this is not working, but should be it's own separate modal in the future (https://github.com/lbryio/lbry-desktop/issues/892)
      if (errorType === 'timeout') {
        doOpenModal(MODALS.FILE_TIMEOUT, { uri });
      } else {
        dispatch(
          doError(
            `Failed to download ${uri}, please try again or see error details:\n\n${errorType}\n\nIf this problem persists, visit https://lbry.io/support for help. `
          )
        );
      }
    }
  };
}

export function doLoadVideo(uri, shouldRecordViewEvent) {
  return dispatch => {
    dispatch({
      type: ACTIONS.LOADING_VIDEO_STARTED,
      data: {
        uri,
      },
    });

    Lbry.get({ uri })
      .then(streamInfo => {
        // need error code from SDK to capture properly
        const timeout =
          streamInfo === null || typeof streamInfo !== 'object' || streamInfo.error === 'Timeout';

        if (timeout) {
          dispatch(handleLoadVideoError(uri, 'timeout'));
        } else {
          dispatch(doDownloadFile(uri, streamInfo));

          if (shouldRecordViewEvent) {
            analytics.apiLogView(
              `${streamInfo.claim_name}#${streamInfo.claim_id}`,
              streamInfo.outpoint,
              streamInfo.claim_id
            );
          }
        }
      })
      .catch(error => {
        dispatch(handleLoadVideoError(uri, error));
      });
  };
}

export function doPurchaseUri(uri, specificCostInfo, shouldRecordViewEvent) {
  return (dispatch, getState) => {
    const state = getState();
    const balance = selectBalance(state);
    const fileInfo = makeSelectFileInfoForUri(uri)(state);
    const downloadingByOutpoint = selectDownloadingByOutpoint(state);
    const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];

    function attemptPlay(cost, instantPurchaseMax = null) {
      if (cost > 0 && (!instantPurchaseMax || cost > instantPurchaseMax)) {
        dispatch(doOpenModal(MODALS.AFFIRM_PURCHASE, { uri }));
      } else {
        dispatch(doLoadVideo(uri, shouldRecordViewEvent));
      }
    }

    // we already fully downloaded the file.
    if (fileInfo && fileInfo.completed) {
      // If written_bytes is false that means the user has deleted/moved the
      // file manually on their file system, so we need to dispatch a
      // doLoadVideo action to reconstruct the file from the blobs
      if (!fileInfo.written_bytes) dispatch(doLoadVideo(uri, shouldRecordViewEvent));

      Promise.resolve();
      return;
    }

    // we are already downloading the file
    if (alreadyDownloading) {
      Promise.resolve();
      return;
    }

    const costInfo = makeSelectCostInfoForUri(uri)(state) || specificCostInfo;
    const { cost } = costInfo;

    if (cost > balance) {
      dispatch(doSetPlayingUri(null));
      dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
      Promise.resolve();
      return;
    }

    if (cost === 0 || !makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED)(state)) {
      attemptPlay(cost);
    } else {
      const instantPurchaseMax = makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state);
      if (instantPurchaseMax.currency === 'LBC') {
        attemptPlay(cost, instantPurchaseMax.amount);
      } else {
        // Need to convert currency of instant purchase maximum before trying to play
        Lbryapi.getExchangeRates().then(({ LBC_USD }) => {
          attemptPlay(cost, instantPurchaseMax.amount / LBC_USD);
        });
      }
    }
  };
}

export function doFetchClaimsByChannel(
  uri: string,
  page: number = 1,
  pageSize: number = PAGE_SIZE
) {
  return dispatch => {
    dispatch({
      type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
      data: { uri, page },
    });

    Lbry.claim_list_by_channel({ uri, page, page_size: pageSize }).then(result => {
      const claimResult = result[uri] || {};
      const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult;

      if (claimsInChannel && claimsInChannel.length) {
        if (page === 1) {
          const latest = claimsInChannel[0];
          dispatch(
            setSubscriptionLatest(
              {
                channelName: latest.channel_name,
                uri: buildURI(
                  {
                    contentName: latest.channel_name,
                    claimId: latest.value.publisherSignature.certificateId,
                  },
                  false
                ),
              },
              buildURI({ contentName: latest.name, claimId: latest.claim_id }, false)
            )
          );
        }
      }

      dispatch({
        type: ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED,
        data: {
          uri,
          claims: claimsInChannel || [],
          page: returnedPage || undefined,
        },
      });
    });
  };
}

export function doPlayUri(uri) {
  return dispatch => {
    dispatch(doSetPlayingUri(uri));
    dispatch(doPurchaseUri(uri));
  };
}

export function doFetchChannelListMine() {
  return dispatch => {
    dispatch({
      type: ACTIONS.FETCH_CHANNEL_LIST_STARTED,
    });

    const callback = channels => {
      dispatch({
        type: ACTIONS.FETCH_CHANNEL_LIST_COMPLETED,
        data: { claims: channels },
      });
    };

    Lbry.channel_list().then(callback);
  };
}

export function doCreateChannel(name: string, amount: number) {
  return dispatch => {
    dispatch({
      type: ACTIONS.CREATE_CHANNEL_STARTED,
    });

    return new Promise((resolve, reject) => {
      Lbry.channel_new({
        channel_name: name,
        amount: creditsToString(amount),
      }).then(
        newChannelClaim => {
          const channelClaim = newChannelClaim;
          channelClaim.name = name;
          dispatch({
            type: ACTIONS.CREATE_CHANNEL_COMPLETED,
            data: { channelClaim },
          });
          resolve(channelClaim);
        },
        error => {
          reject(error);
        }
      );
    });
  };
}

export function savePosition(claimId: string, outpoint: string, position: number) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_CONTENT_POSITION,
      data: { claimId, outpoint, position },
    });
  };
}

export function doSetContentHistoryItem(uri: string) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_CONTENT_LAST_VIEWED,
      data: { uri, lastViewed: Date.now() },
    });
  };
}

export function doClearContentHistoryUri(uri: string) {
  return dispatch => {
    dispatch({
      type: ACTIONS.CLEAR_CONTENT_HISTORY_URI,
      data: { uri },
    });
  };
}

export function doClearContentHistoryAll() {
  return dispatch => {
    dispatch({ type: ACTIONS.CLEAR_CONTENT_HISTORY_ALL });
  };
}

export function doSetHistoryPage(page) {
  return dispatch => {
    dispatch({
      type: ACTIONS.SET_CONTENT_HISTORY_PAGE,
      data: { page },
    });
  };
}
<|endoftext|>


<commit_before>const Transform = require('stream').Transform
const Writable = require('stream').Writable
const _ = require('lodash')
const debug = require('debug')('signalk:simple')
const N2kAnalyzer = require('./n2kAnalyzer')
const FromJson = require('./from_json')
const MultiplexedLog = require('./multiplexedlog')
const nmea0183_signalk = require('./nmea0183-signalk')
const N2kToSignalK = require('./n2k-signalk')
const Log = require('./log')
const Liner = require('./liner')
const execute = require('./execute')
const Udp = require('./udp')
const Tcp = require('./tcp')
const FileStream = require('./filestream')
const Throttle = require('./throttle')
const TimestampThrottle = require('./timestamp-throttle')
const CanboatJs = require('./canboatjs')

function Simple (options) {
  Transform.call(this, { objectMode: true })

  const dataType = options.subOptions.dataType || options.type
  if (!dataType) {
    throw new Error(`Unknown data type for ${options.type}`)
  }

  if (!pipeStartByType[options.type]) {
    throw new Error(`Invalid input type: ${options.type}`)
  }
  if (!dataTypeMapping[dataType]) {
    throw new Error(`Unknown data type: ${dataType}`)
  }
  if (dataType !== 'Multiplexed' && !discriminatorByDataType[dataType]) {
    throw new Error(`No discriminator for: ${dataType}`)
  }

  options.subOptions.app = options.app

  const mappingType =
    options.type == 'NMEA2000' &&
    options.subOptions &&
    options.subOptions.type == 'ngt-1-canboatjs'
      ? 'NMEA2000JS'
      : dataType

  const pipeline = [].concat(
    pipeStartByType[options.type](options.subOptions),
    getLogger(options.app, options.logging, discriminatorByDataType[dataType]),
    dataTypeMapping[mappingType](options)
  )

  for (var i = pipeline.length - 2; i >= 0; i--) {
    pipeline[i].pipe(pipeline[i + 1])
  }
  pipeline[pipeline.length - 1].pipe(this)

  this.pipeline = pipeline
}

require('util').inherits(Simple, Transform)

Simple.prototype._transform = function (msg, encoding, done) {
  this.push(msg)
  done()
}

Simple.prototype.end = function () {
  this.pipeline[0].end()
}

module.exports = Simple

const getLogger = (app, logging, discriminator) =>
  logging
    ? [
      new Log({
        app: app,
        discriminator
      })
    ]
    : []

const discriminatorByDataType = {
  NMEA2000JS: 'A',
  NMEA2000: 'A',
  NMEA0183: 'N',
  SignalK: 'I'
}

const dataTypeMapping = {
  SignalK: options =>
    options.subOptions.type != 'wss' && options.subOptions.type != 'ws'
      ? [new FromJson(options.subOptions)]
      : [],
  NMEA0183: options => [
    new Throttle({
      rate: options.subOptions.throttleRate || 1000,
      app: options.app
    }),
    new nmea0183_signalk(options.subOptions)
  ],
  NMEA2000: options => {
    const result = [new N2kAnalyzer(options.subOptions)]
    if (options.type === 'FileStream') {
      result.push(new TimestampThrottle())
    }
    return result.concat([new N2kToSignalK(options.subOptions)])
  },
  NMEA2000JS: options => {
    const result = [new CanboatJs(options.subOptions)]
    if (options.type === 'FileStream') {
      result.push(new TimestampThrottle())
    }
    return result.concat([new N2kToSignalK(options.subOptions)])
  },
  Multiplexed: options => [new MultiplexedLog(options.subOptions)]
}

const pipeStartByType = {
  NMEA2000: nmea2000input,
  NMEA0183: nmea0183input,
  Execute: executeInput,
  FileStream: fileInput,
  SignalK: signalKInput
}

function nmea2000input (subOptions) {
  if (subOptions.type === 'ngt-1-canboatjs') {
    return [
      new require('./actisense-serial')({
        device: subOptions.device,
        app: subOptions.app,
        outEvent: 'nmea2000out'
      })
    ]
  } else {
    let command
    let toChildProcess
    if (subOptions.type == 'ngt-1') {
      command = `actisense-serial ${subOptions.device}`
      toChildProcess = 'nmea2000out'
    } else if (subOptions.type == 'canbus') {
      command = `candump ${subOptions.interface} | candump2analyzer`
      toChildProcess = null
    } else {
      throw new Error(`unknown NMEA2000 type ${subOptions.type}`)
    }
    return [
      new execute({
        command: command,
        toChildProcess: toChildProcess,
        app: subOptions.app
      }),
      new Liner(subOptions)
    ]
  }
}

function nmea0183input (subOptions) {
  if (subOptions.type == 'tcp') {
    return [new Tcp(subOptions), new Liner(subOptions)]
  } else if (subOptions.type === 'udp') {
    return [new Udp(subOptions)]
  } else if (subOptions.type === 'serial') {
    const serialport = require('./serialport')
    return [new serialport(subOptions)]
  } else {
    throw new Error(`Unknown networking tyoe: ${options.networking}`)
  }
}

function executeInput (subOptions) {
  return [new execute(subOptions), new Liner(subOptions)]
}

function fileInput (subOptions) {
  const result = [new FileStream(subOptions)]
  result.push(new Liner(subOptions))
  return result
}

function signalKInput (subOptions) {
  if (subOptions.type === 'ws' || subOptions.type === 'wss') {
    const options = { app: subOptions.app }
    if (!subOptions.useDiscovery) {
      options.host = subOptions.host
      options.port = subOptions.port
    }
    options.protocol = subOptions.type
    const mdns_ws = require('./mdns-ws')
    return [new mdns_ws(options)]
  } else if (subOptions.type === 'tcp') {
    return [new Tcp(subOptions), new Liner(suboptions)]
  } else if (subOptions.type === 'udp') {
    return [new Udp(subOptions)]
  }
  throw new Error(`unknown SignalK type: ${subOptions.type}`)
}
<commit_msg>Fix error (typo) (#469)<commit_after>const Transform = require('stream').Transform
const Writable = require('stream').Writable
const _ = require('lodash')
const debug = require('debug')('signalk:simple')
const N2kAnalyzer = require('./n2kAnalyzer')
const FromJson = require('./from_json')
const MultiplexedLog = require('./multiplexedlog')
const nmea0183_signalk = require('./nmea0183-signalk')
const N2kToSignalK = require('./n2k-signalk')
const Log = require('./log')
const Liner = require('./liner')
const execute = require('./execute')
const Udp = require('./udp')
const Tcp = require('./tcp')
const FileStream = require('./filestream')
const Throttle = require('./throttle')
const TimestampThrottle = require('./timestamp-throttle')
const CanboatJs = require('./canboatjs')

function Simple (options) {
  Transform.call(this, { objectMode: true })

  const dataType = options.subOptions.dataType || options.type
  if (!dataType) {
    throw new Error(`Unknown data type for ${options.type}`)
  }

  if (!pipeStartByType[options.type]) {
    throw new Error(`Invalid input type: ${options.type}`)
  }
  if (!dataTypeMapping[dataType]) {
    throw new Error(`Unknown data type: ${dataType}`)
  }
  if (dataType !== 'Multiplexed' && !discriminatorByDataType[dataType]) {
    throw new Error(`No discriminator for: ${dataType}`)
  }

  options.subOptions.app = options.app

  const mappingType =
    options.type == 'NMEA2000' &&
    options.subOptions &&
    options.subOptions.type == 'ngt-1-canboatjs'
      ? 'NMEA2000JS'
      : dataType

  const pipeline = [].concat(
    pipeStartByType[options.type](options.subOptions),
    getLogger(options.app, options.logging, discriminatorByDataType[dataType]),
    dataTypeMapping[mappingType](options)
  )

  for (var i = pipeline.length - 2; i >= 0; i--) {
    pipeline[i].pipe(pipeline[i + 1])
  }
  pipeline[pipeline.length - 1].pipe(this)

  this.pipeline = pipeline
}

require('util').inherits(Simple, Transform)

Simple.prototype._transform = function (msg, encoding, done) {
  this.push(msg)
  done()
}

Simple.prototype.end = function () {
  this.pipeline[0].end()
}

module.exports = Simple

const getLogger = (app, logging, discriminator) =>
  logging
    ? [
      new Log({
        app: app,
        discriminator
      })
    ]
    : []

const discriminatorByDataType = {
  NMEA2000JS: 'A',
  NMEA2000: 'A',
  NMEA0183: 'N',
  SignalK: 'I'
}

const dataTypeMapping = {
  SignalK: options =>
    options.subOptions.type != 'wss' && options.subOptions.type != 'ws'
      ? [new FromJson(options.subOptions)]
      : [],
  NMEA0183: options => [
    new Throttle({
      rate: options.subOptions.throttleRate || 1000,
      app: options.app
    }),
    new nmea0183_signalk(options.subOptions)
  ],
  NMEA2000: options => {
    const result = [new N2kAnalyzer(options.subOptions)]
    if (options.type === 'FileStream') {
      result.push(new TimestampThrottle())
    }
    return result.concat([new N2kToSignalK(options.subOptions)])
  },
  NMEA2000JS: options => {
    const result = [new CanboatJs(options.subOptions)]
    if (options.type === 'FileStream') {
      result.push(new TimestampThrottle())
    }
    return result.concat([new N2kToSignalK(options.subOptions)])
  },
  Multiplexed: options => [new MultiplexedLog(options.subOptions)]
}

const pipeStartByType = {
  NMEA2000: nmea2000input,
  NMEA0183: nmea0183input,
  Execute: executeInput,
  FileStream: fileInput,
  SignalK: signalKInput
}

function nmea2000input (subOptions) {
  if (subOptions.type === 'ngt-1-canboatjs') {
    return [
      new require('./actisense-serial')({
        device: subOptions.device,
        app: subOptions.app,
        outEvent: 'nmea2000out'
      })
    ]
  } else {
    let command
    let toChildProcess
    if (subOptions.type == 'ngt-1') {
      command = `actisense-serial ${subOptions.device}`
      toChildProcess = 'nmea2000out'
    } else if (subOptions.type == 'canbus') {
      command = `candump ${subOptions.interface} | candump2analyzer`
      toChildProcess = null
    } else {
      throw new Error(`unknown NMEA2000 type ${subOptions.type}`)
    }
    return [
      new execute({
        command: command,
        toChildProcess: toChildProcess,
        app: subOptions.app
      }),
      new Liner(subOptions)
    ]
  }
}

function nmea0183input (subOptions) {
  if (subOptions.type == 'tcp') {
    return [new Tcp(subOptions), new Liner(subOptions)]
  } else if (subOptions.type === 'udp') {
    return [new Udp(subOptions)]
  } else if (subOptions.type === 'serial') {
    const serialport = require('./serialport')
    return [new serialport(subOptions)]
  } else {
    throw new Error(`Unknown networking tyoe: ${options.networking}`)
  }
}

function executeInput (subOptions) {
  return [new execute(subOptions), new Liner(subOptions)]
}

function fileInput (subOptions) {
  const result = [new FileStream(subOptions)]
  result.push(new Liner(subOptions))
  return result
}

function signalKInput (subOptions) {
  if (subOptions.type === 'ws' || subOptions.type === 'wss') {
    const options = { app: subOptions.app }
    if (!subOptions.useDiscovery) {
      options.host = subOptions.host
      options.port = subOptions.port
    }
    options.protocol = subOptions.type
    const mdns_ws = require('./mdns-ws')
    return [new mdns_ws(options)]
  } else if (subOptions.type === 'tcp') {
    return [new Tcp(subOptions), new Liner(subOptions)]
  } else if (subOptions.type === 'udp') {
    return [new Udp(subOptions)]
  }
  throw new Error(`unknown SignalK type: ${subOptions.type}`)
}
<|endoftext|>


<commit_before>module.exports = {
  htmlLang: '{{htmlLang}}',
  assetPath: '{{govukAssetPath}}',
  afterHeader: '{{$afterHeader}}{{/afterHeader}}',
  bodyClasses: '{{$bodyClasses}}{{/bodyClasses}}',
  bodyEnd: '{{$bodyEnd}}{{/bodyEnd}}',
  content: '{{$main}}{{/main}}',
  cookieMessage: '{{$cookieMessage}}{{/cookieMessage}}',
  footerSupportLinks: '{{$footerSupportLinks}}{{/footerSupportLinks}}',
  footerTop: '{{$footerTop}}{{/footerTop}}',
  head: '{{$head}}{{/head}}',
  headerClass: '{{$headerClass}}{{/headerClass}}',
  insideHeader: '{{$insideHeader}}{{/insideHeader}}',
  pageTitle: '{{$pageTitle}}{{/pageTitle}}',
  propositionHeader: '{{$propositionHeader}}{{/propositionHeader}}',
  skipLinkMessage: '{{$skipLinkMessage}}Skip to main content{{/skipLinkMessage}}',
  globalHeaderText: '{{$globalHeaderText}}GOV.UK{{/globalHeaderText}}',
  licenceMessage: '{{$licenceMessage}}
All content is available under the Open Government Licence v3.0, except where otherwise stated
{{/licenceMessage}}',
  crownCopyrightMessage: '{{$crownCopyrightMessage}}© Crown copyright{{/crownCopyrightMessage}}'
};
<commit_msg>Add default govuk link and logo title<commit_after>module.exports = {
  htmlLang: '{{htmlLang}}',
  assetPath: '{{govukAssetPath}}',
  afterHeader: '{{$afterHeader}}{{/afterHeader}}',
  bodyClasses: '{{$bodyClasses}}{{/bodyClasses}}',
  bodyEnd: '{{$bodyEnd}}{{/bodyEnd}}',
  content: '{{$main}}{{/main}}',
  cookieMessage: '{{$cookieMessage}}{{/cookieMessage}}',
  footerSupportLinks: '{{$footerSupportLinks}}{{/footerSupportLinks}}',
  footerTop: '{{$footerTop}}{{/footerTop}}',
  head: '{{$head}}{{/head}}',
  headerClass: '{{$headerClass}}{{/headerClass}}',
  homepageUrl: '{{$homepageUrl}}https://www.gov.uk{{/homepageUrl}}',
  logoLinkTitle: '{{$logoLinkTitle}}Go to the GOV.UK homepage{{/logoLinkTitle}}',
  insideHeader: '{{$insideHeader}}{{/insideHeader}}',
  pageTitle: '{{$pageTitle}}{{/pageTitle}}',
  propositionHeader: '{{$propositionHeader}}{{/propositionHeader}}',
  skipLinkMessage: '{{$skipLinkMessage}}Skip to main content{{/skipLinkMessage}}',
  globalHeaderText: '{{$globalHeaderText}}GOV.UK{{/globalHeaderText}}',
  licenceMessage: '{{$licenceMessage}}
All content is available under the Open Government Licence v3.0, except where otherwise stated
{{/licenceMessage}}',
  crownCopyrightMessage: '{{$crownCopyrightMessage}}© Crown copyright{{/crownCopyrightMessage}}'
};
<|endoftext|>