<commit_before>import { Util }                            from '../../../../../imports/api/util';
import { D3ContributorDragControlHandler } from './d3_contributor_drag_control_handler';

let d3            = require('d3'),
    controlTextX  = 0,
    controlTextY  = 0,
    controlRadius = 10,
    debug         = false;

export class D3CapacityPlanTeamHandler {
  /**
   * D3CapacityPlanSprintHandler takes care of constructing and updating the capacity plan team representations
   * @param chart
   */
  constructor (chart) {
    this.chart          = chart;
    this.controlHandler = new D3ContributorDragControlHandler(chart, this);
  }

  update () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.update');
    let self  = this,
        chart = this.chart;

    self.calculateEnvelopes();

    // Service the teams sections
    self.insertTeams();
    self.updateTeams();
    self.removeTeams();

    // Service the teams groups on the contributorLayer
    self.insertContributorTeams();
    self.updateContributorTeams();
    self.removeContributorTeams();

    // Service the contributors
    self.insertContributors();
    self.updateContributors();
    self.removeContributors();
  }

  /**
   * Update the selection of currently existing teams
   */
  updateTeamsSelection () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateTeamsSelection');
    let self  = this,
        chart = this.chart;

    self.teamSelection = chart.teamLayer.selectAll('.team-group')
        .data(chart.data.teams, (team) => {
          return team._id
        });
  }

  /**
   * Insert new teams
   */
  insertTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.insertTeams');
    let self  = this,
        chart = this.chart;

    self.updateTeamsSelection();

    // Insert the base group
    let teamEnter = self.teamSelection.enter()
        .append('g')
        .attr('class', 'team-group controls-hover')
        .attr('data-team-id', (d) => {
          return d._id
        });

    // Insert the title group
    let teamTitleEnter = teamEnter.append('g')
        .attr('class', 'team-title-group')
        .on('click', (team) => {
          chart.data.option.toggleTeamVisibility(team._id);
        })
        .on('mouseenter', (team) => {
          // Highlight all of the team contributors
          team.contributors.forEach((contributor) => {
            chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + contributor._id + '"]').classed('highlight', true)
          });
        })
        .on('mouseleave', (team) => {
          // Remove highlight from all of the team contributors
          team.contributors.forEach((contributor) => {
            chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + contributor._id + '"]').classed('highlight', false)
          });
        });

    teamTitleEnter.append('rect')
        .attr('class', 'team-title-background')
        .attr('x', 0)
        .attr('y', 0)
        .attr('height', chart.config.teams.titleHeight)
        .attr('width', chart.namesWidth + chart.config.teams.padding);

    teamTitleEnter.append('text')
        .attr('class', 'team-title')
        .text((team) => {
          return team.title
        });

    // Insert the re-order controls
    // Add controls for this effort
    let teamControlsEnter = teamEnter.append('g')
        .attr('class', 'controls-group');

    let upButtonEnter = teamControlsEnter.append('g')
        .attr('class', 'round-control effort-control-up')
        .attr('transform', 'translate(' + (1 * controlRadius + 2 * chart.config.efforts.padding) + ', 0)')
        .on('click', (team) => {
          chart.data.option.moveTeamUp(team._id)
        });

    upButtonEnter.append('circle')
        .attr('class', 'round-control-background')
        .attr('r', controlRadius);

    upButtonEnter.append('text')
        .attr('x', controlTextX)
        .attr('y', controlTextY)
        .text('\u2191');

    let downButtonEnter = teamControlsEnter.append('g')
        .attr('class', 'round-control effort-control-down')
        .attr('transform', 'translate(' + (3 * controlRadius + 3 * chart.config.efforts.padding) + ', 0)')
        .on('click', (team) => {
          chart.data.option.moveTeamDown(team._id)
        });

    downButtonEnter.append('circle')
        .attr('class', 'round-control-background')
        .attr('r', controlRadius);

    downButtonEnter.append('text')
        .attr('x', controlTextX)
        .attr('y', controlTextY)
        .text('\u2193');

  }

  /**
   * Update existing teams
   */
  updateTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateTeams');
    let self  = this,
        chart = this.chart;

    self.updateTeamsSelection();

    // Place the team envelope
    self.teamSelection
        .transition()
        .duration(500)
        .attr('transform', (team, i) => {
          return 'translate(0,' + team.envelope.y1 + ')'
        });

    // Update the team title
    self.teamSelection.select('.team-title')
        .text((team) => {
          return team.title
        })
        .attr('transform', () => {
          return 'translate(' + (chart.namesWidth - chart.config.teams.padding) + ', ' + chart.config.teams.titlePadding + ')'
        });

    // Update the team title background
    self.teamSelection.select('.team-title-background')
        .classed('collapsed', (team) => {
          return team.visible === false
        })
        .attr('width', chart.namesWidth + chart.config.teams.padding - 2);
  }

  /**
   * Remove unneeded teams
   */
  removeTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.removeTeams');
    let self  = this,
        chart = this.chart;

    self.teamSelection.exit().remove();
  }

  /**
   * Update the selection of currently existing teams groups for the contributor layer
   */
  updateContributorTeamSelection () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributorTeamSelection');
    let self  = this,
        chart = this.chart;

    self.contributorTeamSelection = chart.contributorLayer.selectAll('.contributor-team-group')
        .data(chart.data.teams, (team) => {
          return team._id
        });
  }

  /**
   * Insert new teams groups for the contributor layer
   */
  insertContributorTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.insertContributorTeams');
    let self  = this,
        chart = this.chart;

    self.updateContributorTeamSelection();

    // Insert the base group
    self.contributorTeamSelection.enter()
        .append('g')
        .attr('class', 'contributor-team-group')
        .attr('data-team-id', (d) => {
          return d._id
        });
  }

  /**
   * Update existing teams groups for the contributor layer
   */
  updateContributorTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributorTeams');
    let self  = this,
        chart = this.chart;

    self.updateContributorTeamSelection();

    // Place the team envelope
    self.contributorTeamSelection
        .transition()
        .duration(500)
        .attr('transform', (team, i) => {
          return 'translate(0,' + team.envelope.y1 + ')'
        });
  }

  /**
   * Remove unneeded teams groups on the contributor layer
   */
  removeContributorTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.removeContributorTeams');
    let self  = this,
        chart = this.chart;

    self.contributorTeamSelection.exit().remove();
  }

  /**
   * Update the selection of currently existing contributors
   */
  updateContributorSelection () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributorSelection');
    let self  = this,
        chart = this.chart;

    self.contributorSelection = chart.contributorLayer
        .selectAll('.contributor-team-group')
        .selectAll('.contributor-group')
        .data((d, i) => {
          return d.contributors
        }, (d) => {
          return d._id
        })
  }

  /**
   * Insert new contributors
   */
  insertContributors () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.insertContributors');
    let self  = this,
        chart = this.chart;

    self.updateContributorSelection();

    let contributorGroupsEnter = self.contributorSelection.enter()
        .append('g')
        .attr('class', 'contributor-group link-drag-hover')
        .attr('data-contributor-id', (d) => {
          return d._id
        })
        .on('mouseenter', (d) => {
          if (chart.drag === undefined) {
            chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + d._id + '"]').classed('highlight', true)
          }
        })
        .on('mouseleave', (d) => {
          chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + d._id + '"]').classed('highlight', false)
        });

    contributorGroupsEnter.append('rect')
        .attr('class', 'contributor-background')
        .attr('x', -chart.namesWidth + chart.config.teams.padding)
        .attr('y', 0)
        .attr('width', chart.namesWidth + chart.config.teams.padding - 2)
        .attr('height', chart.config.contributors.height);

    contributorGroupsEnter.append('path')
        .attr('class', 'contributor-background-path contributor-highlight')
        .attr('data-contributor-id', (d) => {
          return d._id
        })
        .attr('d', 'm' + (-chart.namesWidth + chart.config.teams.padding) + ' ' + (chart.config.contributors.height / 2) + ' l' + (chart.namesWidth + chart.config.teams.padding - 2) + ' 0');

    contributorGroupsEnter.append('text')
        .attr('class', 'contributor-name')
        .attr('x', 0)
        .attr('y', chart.config.contributors.height * 0.25)
        .text((d) => {
          return d.name
        });

    // Create the controls
    self.controlHandler.insert(contributorGroupsEnter);
  }

  /**
   * Update all contributors
   */
  updateContributors () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributors');
    let self  = this,
        chart = this.chart;

    self.updateContributorSelection();

    self.contributorSelection.attr('display', (contributor) => {
          return contributor.visible ? 'block' : 'none'
        })
        .attr('transform', (contributor) => {
          return 'translate(' + (chart.namesWidth - chart.config.teams.padding) + ',' + (contributor.envelope.y1) + ')'
        });

    self.contributorSelection.select('.contributor-background')
        .attr('x', -chart.namesWidth + chart.config.teams.padding)
        .attr('width', chart.namesWidth + chart.config.teams.padding - 2);

    self.contributorSelection.select('.contributor-background-path')
        .attr('d', 'm' + (-chart.namesWidth + chart.config.teams.padding) + ' ' + (chart.config.contributors.height / 2) + ' l' + (chart.namesWidth + chart.config.teams.padding - 2) + ' 0');

    self.contributorSelection.select('.link-drag-group')
        .attr('transform', 'translate(' + (chart.config.teams.padding * 2) + ',' + (chart.config.contributors.height / 2) + ')');
  }

  /**
   * Remove unneeded contributors
   */
  removeContributors () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.removeContributors');
    let self  = this,
        chart = this.chart;

    self.contributorSelection.exit().remove();
  }

  /**
   * Size all of the team blocks
   */
  calculateEnvelopes () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.calculateEnvelopes');
    let self  = this,
        chart = this.chart;

    // Set the y position for the groups
    let dy = 0,
        height,
        width;

    chart.namesWidth = 0;
    chart.data.teams.forEach((team) => {
      // Measure the width of the title
      chart.offscreenLayer.append('text')
          .attr('id', '_' + team._id)
          .attr('class', 'team-title')
          .text(team.title);

      if (team.visible) {
        height = chart.config.teams.titleHeight + team.contributors.length * chart.config.contributors.height + chart.config.teams.padding;
      } else {
        height = chart.config.teams.titleHeight + chart.config.teams.padding;
      }

      team.envelope = {
        x1    : 0,
        y1    : dy,
        x2    : chart.config.margin.left + chart.config.contributors.width,
        y2    : dy + height,
        height: height,
        width : chart.offscreenLayer.select('#_' + team._id).node().getBoundingClientRect().width
      };

      // Factor in the team title to the max width calc
      chart.namesWidth = Math.max(team.envelope.width, chart.namesWidth);

      team.contributors.forEach((contributor, i) => {
        // Measure the width of the title
        chart.offscreenLayer.append('text')
            .attr('class', 'contributor-name')
            .attr('id', '_' + contributor._id)
            .text(contributor.name);

        width = chart.offscreenLayer.select('#_' + contributor._id).node().getBoundingClientRect().width;

        contributor.envelope = {
          x1   : 0,
          y1   : i * chart.config.contributors.height + chart.config.teams.titleHeight,
          x2   : width,
          y2   : (i + 1) * chart.config.contributors.height + chart.config.teams.titleHeight,
          width: width
        };

        // Factor in the contributor name to the max width calc
        chart.namesWidth = Math.max(width, chart.namesWidth);
      });

      dy = team.envelope.y2;
    });
    chart.contributorsHeight = Math.max(dy, 20);

    // Add some padding to the names and force it to a whole number of pixels
    chart.namesWidth = Math.max(chart.namesWidth, chart.config.contributors.width);
    chart.namesWidth += chart.config.teams.padding * 2;
    chart.namesWidth = parseInt(chart.namesWidth);

    // Clean up the offscreen layer
    chart.offscreenLayer.selectAll('text').remove();
  }
}
<commit_msg>Removed animations from capacity plan chart teams placement<commit_after>import { Util }                            from '../../../../../imports/api/util';
import { D3ContributorDragControlHandler } from './d3_contributor_drag_control_handler';

let d3            = require('d3'),
    controlTextX  = 0,
    controlTextY  = 0,
    controlRadius = 10,
    debug         = false;

export class D3CapacityPlanTeamHandler {
  /**
   * D3CapacityPlanSprintHandler takes care of constructing and updating the capacity plan team representations
   * @param chart
   */
  constructor (chart) {
    this.chart          = chart;
    this.controlHandler = new D3ContributorDragControlHandler(chart, this);
  }

  update () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.update');
    let self  = this,
        chart = this.chart;

    self.calculateEnvelopes();

    // Service the teams sections
    self.insertTeams();
    self.updateTeams();
    self.removeTeams();

    // Service the teams groups on the contributorLayer
    self.insertContributorTeams();
    self.updateContributorTeams();
    self.removeContributorTeams();

    // Service the contributors
    self.insertContributors();
    self.updateContributors();
    self.removeContributors();
  }

  /**
   * Update the selection of currently existing teams
   */
  updateTeamsSelection () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateTeamsSelection');
    let self  = this,
        chart = this.chart;

    self.teamSelection = chart.teamLayer.selectAll('.team-group')
        .data(chart.data.teams, (team) => {
          return team._id
        });
  }

  /**
   * Insert new teams
   */
  insertTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.insertTeams');
    let self  = this,
        chart = this.chart;

    self.updateTeamsSelection();

    // Insert the base group
    let teamEnter = self.teamSelection.enter()
        .append('g')
        .attr('class', 'team-group controls-hover')
        .attr('data-team-id', (d) => {
          return d._id
        });

    // Insert the title group
    let teamTitleEnter = teamEnter.append('g')
        .attr('class', 'team-title-group')
        .on('click', (team) => {
          chart.data.option.toggleTeamVisibility(team._id);
        })
        .on('mouseenter', (team) => {
          // Highlight all of the team contributors
          team.contributors.forEach((contributor) => {
            chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + contributor._id + '"]').classed('highlight', true)
          });
        })
        .on('mouseleave', (team) => {
          // Remove highlight from all of the team contributors
          team.contributors.forEach((contributor) => {
            chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + contributor._id + '"]').classed('highlight', false)
          });
        });

    teamTitleEnter.append('rect')
        .attr('class', 'team-title-background')
        .attr('x', 0)
        .attr('y', 0)
        .attr('height', chart.config.teams.titleHeight)
        .attr('width', chart.namesWidth + chart.config.teams.padding);

    teamTitleEnter.append('text')
        .attr('class', 'team-title')
        .text((team) => {
          return team.title
        });

    // Insert the re-order controls
    // Add controls for this effort
    let teamControlsEnter = teamEnter.append('g')
        .attr('class', 'controls-group');

    let upButtonEnter = teamControlsEnter.append('g')
        .attr('class', 'round-control effort-control-up')
        .attr('transform', 'translate(' + (1 * controlRadius + 2 * chart.config.efforts.padding) + ', 0)')
        .on('click', (team) => {
          chart.data.option.moveTeamUp(team._id)
        });

    upButtonEnter.append('circle')
        .attr('class', 'round-control-background')
        .attr('r', controlRadius);

    upButtonEnter.append('text')
        .attr('x', controlTextX)
        .attr('y', controlTextY)
        .text('\u2191');

    let downButtonEnter = teamControlsEnter.append('g')
        .attr('class', 'round-control effort-control-down')
        .attr('transform', 'translate(' + (3 * controlRadius + 3 * chart.config.efforts.padding) + ', 0)')
        .on('click', (team) => {
          chart.data.option.moveTeamDown(team._id)
        });

    downButtonEnter.append('circle')
        .attr('class', 'round-control-background')
        .attr('r', controlRadius);

    downButtonEnter.append('text')
        .attr('x', controlTextX)
        .attr('y', controlTextY)
        .text('\u2193');

  }

  /**
   * Update existing teams
   */
  updateTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateTeams');
    let self  = this,
        chart = this.chart;

    self.updateTeamsSelection();

    // Place the team envelope
    self.teamSelection
        .attr('transform', (team, i) => {
          return 'translate(0,' + team.envelope.y1 + ')'
        });

    // Update the team title
    self.teamSelection.select('.team-title')
        .text((team) => {
          return team.title
        })
        .attr('transform', () => {
          return 'translate(' + (chart.namesWidth - chart.config.teams.padding) + ', ' + chart.config.teams.titlePadding + ')'
        });

    // Update the team title background
    self.teamSelection.select('.team-title-background')
        .classed('collapsed', (team) => {
          return team.visible === false
        })
        .attr('width', chart.namesWidth + chart.config.teams.padding - 2);
  }

  /**
   * Remove unneeded teams
   */
  removeTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.removeTeams');
    let self  = this,
        chart = this.chart;

    self.teamSelection.exit().remove();
  }

  /**
   * Update the selection of currently existing teams groups for the contributor layer
   */
  updateContributorTeamSelection () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributorTeamSelection');
    let self  = this,
        chart = this.chart;

    self.contributorTeamSelection = chart.contributorLayer.selectAll('.contributor-team-group')
        .data(chart.data.teams, (team) => {
          return team._id
        });
  }

  /**
   * Insert new teams groups for the contributor layer
   */
  insertContributorTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.insertContributorTeams');
    let self  = this,
        chart = this.chart;

    self.updateContributorTeamSelection();

    // Insert the base group
    self.contributorTeamSelection.enter()
        .append('g')
        .attr('class', 'contributor-team-group')
        .attr('data-team-id', (d) => {
          return d._id
        });
  }

  /**
   * Update existing teams groups for the contributor layer
   */
  updateContributorTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributorTeams');
    let self  = this,
        chart = this.chart;

    self.updateContributorTeamSelection();

    // Place the team envelope
    self.contributorTeamSelection
        .attr('transform', (team, i) => {
          return 'translate(0,' + team.envelope.y1 + ')'
        });
  }

  /**
   * Remove unneeded teams groups on the contributor layer
   */
  removeContributorTeams () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.removeContributorTeams');
    let self  = this,
        chart = this.chart;

    self.contributorTeamSelection.exit().remove();
  }

  /**
   * Update the selection of currently existing contributors
   */
  updateContributorSelection () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributorSelection');
    let self  = this,
        chart = this.chart;

    self.contributorSelection = chart.contributorLayer
        .selectAll('.contributor-team-group')
        .selectAll('.contributor-group')
        .data((d, i) => {
          return d.contributors
        }, (d) => {
          return d._id
        })
  }

  /**
   * Insert new contributors
   */
  insertContributors () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.insertContributors');
    let self  = this,
        chart = this.chart;

    self.updateContributorSelection();

    let contributorGroupsEnter = self.contributorSelection.enter()
        .append('g')
        .attr('class', 'contributor-group link-drag-hover')
        .attr('data-contributor-id', (d) => {
          return d._id
        })
        .on('mouseenter', (d) => {
          if (chart.drag === undefined) {
            chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + d._id + '"]').classed('highlight', true)
          }
        })
        .on('mouseleave', (d) => {
          chart.svg.selectAll('.contributor-highlight[data-contributor-id="' + d._id + '"]').classed('highlight', false)
        });

    contributorGroupsEnter.append('rect')
        .attr('class', 'contributor-background')
        .attr('x', -chart.namesWidth + chart.config.teams.padding)
        .attr('y', 0)
        .attr('width', chart.namesWidth + chart.config.teams.padding - 2)
        .attr('height', chart.config.contributors.height);

    contributorGroupsEnter.append('path')
        .attr('class', 'contributor-background-path contributor-highlight')
        .attr('data-contributor-id', (d) => {
          return d._id
        })
        .attr('d', 'm' + (-chart.namesWidth + chart.config.teams.padding) + ' ' + (chart.config.contributors.height / 2) + ' l' + (chart.namesWidth + chart.config.teams.padding - 2) + ' 0');

    contributorGroupsEnter.append('text')
        .attr('class', 'contributor-name')
        .attr('x', 0)
        .attr('y', chart.config.contributors.height * 0.25)
        .text((d) => {
          return d.name
        });

    // Create the controls
    self.controlHandler.insert(contributorGroupsEnter);
  }

  /**
   * Update all contributors
   */
  updateContributors () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.updateContributors');
    let self  = this,
        chart = this.chart;

    self.updateContributorSelection();

    self.contributorSelection.attr('display', (contributor) => {
          return contributor.visible ? 'block' : 'none'
        })
        .attr('transform', (contributor) => {
          return 'translate(' + (chart.namesWidth - chart.config.teams.padding) + ',' + (contributor.envelope.y1) + ')'
        });

    self.contributorSelection.select('.contributor-background')
        .attr('x', -chart.namesWidth + chart.config.teams.padding)
        .attr('width', chart.namesWidth + chart.config.teams.padding - 2);

    self.contributorSelection.select('.contributor-background-path')
        .attr('d', 'm' + (-chart.namesWidth + chart.config.teams.padding) + ' ' + (chart.config.contributors.height / 2) + ' l' + (chart.namesWidth + chart.config.teams.padding - 2) + ' 0');

    self.contributorSelection.select('.link-drag-group')
        .attr('transform', 'translate(' + (chart.config.teams.padding * 2) + ',' + (chart.config.contributors.height / 2) + ')');
  }

  /**
   * Remove unneeded contributors
   */
  removeContributors () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.removeContributors');
    let self  = this,
        chart = this.chart;

    self.contributorSelection.exit().remove();
  }

  /**
   * Size all of the team blocks
   */
  calculateEnvelopes () {
    debug && console.log(Util.timestamp(), 'D3CapacityPlanTeamHandler.calculateEnvelopes');
    let self  = this,
        chart = this.chart;

    // Set the y position for the groups
    let dy = 0,
        height,
        width;

    chart.namesWidth = 0;
    chart.data.teams.forEach((team) => {
      // Measure the width of the title
      chart.offscreenLayer.append('text')
          .attr('id', '_' + team._id)
          .attr('class', 'team-title')
          .text(team.title);

      if (team.visible) {
        height = chart.config.teams.titleHeight + team.contributors.length * chart.config.contributors.height + chart.config.teams.padding;
      } else {
        height = chart.config.teams.titleHeight + chart.config.teams.padding;
      }

      team.envelope = {
        x1    : 0,
        y1    : dy,
        x2    : chart.config.margin.left + chart.config.contributors.width,
        y2    : dy + height,
        height: height,
        width : chart.offscreenLayer.select('#_' + team._id).node().getBoundingClientRect().width
      };

      // Factor in the team title to the max width calc
      chart.namesWidth = Math.max(team.envelope.width, chart.namesWidth);

      team.contributors.forEach((contributor, i) => {
        // Measure the width of the title
        chart.offscreenLayer.append('text')
            .attr('class', 'contributor-name')
            .attr('id', '_' + contributor._id)
            .text(contributor.name);

        width = chart.offscreenLayer.select('#_' + contributor._id).node().getBoundingClientRect().width;

        contributor.envelope = {
          x1   : 0,
          y1   : i * chart.config.contributors.height + chart.config.teams.titleHeight,
          x2   : width,
          y2   : (i + 1) * chart.config.contributors.height + chart.config.teams.titleHeight,
          width: width
        };

        // Factor in the contributor name to the max width calc
        chart.namesWidth = Math.max(width, chart.namesWidth);
      });

      dy = team.envelope.y2;
    });
    chart.contributorsHeight = Math.max(dy, 20);

    // Add some padding to the names and force it to a whole number of pixels
    chart.namesWidth = Math.max(chart.namesWidth, chart.config.contributors.width);
    chart.namesWidth += chart.config.teams.padding * 2;
    chart.namesWidth = parseInt(chart.namesWidth);

    // Clean up the offscreen layer
    chart.offscreenLayer.selectAll('text').remove();
  }
}
<|endoftext|>


<commit_before>import 'whatwg-fetch';
import assign from 'object-assign';
import BluebirdPromise from 'bluebird';
import LoginStore from '../stores/LoginStore';

// return Bluebird promises from native fetch calls, too
function fetch(...args) {
  return BluebirdPromise.resolve(window.fetch(...args));
}

function makeUrl(path) {
  let uri = path;
  const token = LoginStore.getToken();
  if (token) {
    // heh…
    if (uri.indexOf('?') !== -1) {
      uri += `&token=${token}`;
    } else {
      uri += `?token=${token}`;
    }
  }

  return uri;
}

function rejectNonOK(response) {
  if (response.status !== 200) {
    return response.json().then(message => {
      const error = assign(new Error(message), { response });
      return Promise.reject(error);
    });
  }
  return response;
}

export function get(uri) {
  return fetch(makeUrl(uri), {
    method: 'get',
    credentials: 'same-origin'
  }).then(rejectNonOK);
}

export function post(uri, data) {
  return fetch(makeUrl(uri), {
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin',
    body: JSON.stringify(data)
  }).then(rejectNonOK);
}

export function put(uri, data) {
  return fetch(makeUrl(uri), {
    method: 'put',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin',
    body: JSON.stringify(data)
  }).then(rejectNonOK);
}

export function del(uri, data) {
  return fetch(makeUrl(uri), {
    method: 'delete',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin',
    body: JSON.stringify(data)
  }).then(rejectNonOK);
}
<commit_msg>request: allow passing object for query parameters in GET requests<commit_after>import 'whatwg-fetch';
import assign from 'object-assign';
import BluebirdPromise from 'bluebird';
import { stringify as stringifyQS } from 'querystring';
import LoginStore from '../stores/LoginStore';

// return Bluebird promises from native fetch calls, too
function fetch(...args) {
  return BluebirdPromise.resolve(window.fetch(...args));
}

function makeUrl(path, params = {}) {
  let uri = path;
  const token = LoginStore.getToken();

  const query = token ? { token, ...params } : params;
  if (query) {
    // heh…
    uri += (uri.indexOf('?') !== -1 ? '&' : '?') + stringifyQS(query);
  }

  return uri;
}

function rejectNonOK(response) {
  if (response.status !== 200) {
    return response.json().then(message => {
      const error = assign(new Error(message), { response });
      return Promise.reject(error);
    });
  }
  return response;
}

export function get(uri, data) {
  return fetch(makeUrl(uri, data), {
    method: 'get',
    credentials: 'same-origin'
  }).then(rejectNonOK);
}

export function post(uri, data) {
  return fetch(makeUrl(uri), {
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin',
    body: JSON.stringify(data)
  }).then(rejectNonOK);
}

export function put(uri, data) {
  return fetch(makeUrl(uri), {
    method: 'put',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin',
    body: JSON.stringify(data)
  }).then(rejectNonOK);
}

export function del(uri, data) {
  return fetch(makeUrl(uri), {
    method: 'delete',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin',
    body: JSON.stringify(data)
  }).then(rejectNonOK);
}
<|endoftext|>


<commit_before>"use strict";
var exec = require('child_process').exec;
var path = require('path');
var gulp = require('gulp');
var nsp = require('gulp-nsp');

gulp.task('nsp', function (cb) {
    nsp({package: path.resolve('package.json')}, cb);
});

gulp.task('bithound', function () {
    if (!process.env.CI) {
        return;
    }

    exec('bithound check git@github.com:superleap/github-issues-label-sync.git', (error, stdout, stderr) => {
        console.log(stdout);
        console.log(stderr);
    });
});

gulp.task('prepublish', ['nsp', 'bithound']);
<commit_msg>chore(ci): node 0.12 travis fix<commit_after>"use strict";
var exec = require('child_process').exec;
var path = require('path');
var gulp = require('gulp');
var nsp = require('gulp-nsp');

gulp.task('nsp', function (cb) {
    nsp({package: path.resolve('package.json')}, cb);
});

gulp.task('bithound', function () {
    if (!process.env.CI) {
        return;
    }

    exec('bithound check git@github.com:superleap/github-issues-label-sync.git', function (error, stdout, stderr) {
        console.log(stdout);
        console.log(stderr);
    });
});

gulp.task('prepublish', ['nsp', 'bithound']);
<|endoftext|>


<commit_before>import { SpritePlayer, SpriteBullet, ShootSound } from "../SpriteConstants";
import { CharacterWitdh, CharacterHeight, DirectionBoomerang } from "../Constants";

const Damage = 10;
const Velocity = 200;
const MaxBullet = 10;
const TimeLapse = 10;
const MaxLife = 1000;

class Character extends Phaser.Sprite {

  constructor(game, x, y, key, frame) {
    super(game, x, y, SpritePlayer, frame);
    game.physics.arcade.enable(this);
    //this.body.gravity.y = 500;
    this.body.bounce.x = this.body.bounce.y = 0;
    this.body.mass = 1;
    this.direction = 1;
    this.anchor.setTo(0.5, 0.5);
    this.life = MaxLife;

    const fire = [8, 9, 10, 11];
    const walk = [0, 1, 2, 3, 4, 5, 6, 7];
    const slide = [12, 13, 14, 15, 14, 13, 12];

    this.animations.add('walk', walk, TimeLapse, false);
    this.animations.add('slide', slide, TimeLapse, false);
    this.animations.add('fire', fire, TimeLapse * 6, false);

    this.weapon = game.add.weapon(MaxBullet, SpriteBullet);
    this.weapon.bulletKillType = Phaser.Weapon.KILL_WORLD_BOUNDS;
    this.weapon.bulletAngleOffset = 90;
    //this.weapon.addBulletAnimation("fire", fire, TimeLapse, true);
    this.weapon.bulletAngleVariance = 5;
    this.weapon.bulletSpeed = 500;
    this.weapon.fireRate = 300;

    this.weapon.trackSprite(this, 30, 20, true);
    this.shootFx = game.add.audio(ShootSound);
    this.shootFx.allowMultiple = true;

     this.shootFx.addMarker('shootMarker', 0, 0.3);

    this.pad = this.game.input.gamepad.pad1;
    this.cursor = game.input.keyboard.createCursorKeys();

    this.fireButton = game.input.keyboard.addKey(Phaser.KeyCode.SPACEBAR);
    this.fireClick = game.input.activePointer.leftButton;

    this.up = game.input.keyboard.addKey(Phaser.Keyboard.Z);
    this.leftKey = game.input.keyboard.addKey(Phaser.Keyboard.Q);
    this.rightKey = game.input.keyboard.addKey(Phaser.Keyboard.D);
    this.down = game.input.keyboard.addKey(Phaser.Keyboard.S);

    this.lastDirection = null;
  }

  update() {
    if(!this.isDeath()) {
      this.body.velocity.x = 0;
      this.body.velocity.y = 0;
      this.rotation = this.game.physics.arcade.angleToPointer(this);
      let move = null;
      //console.log(this.angle)

      if (this.cursor.left.isDown || this.leftKey.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_X) < -0.1 ) {
          this.body.velocity.x = -Velocity;
          this.direction = -1;
          this.lastDirection = DirectionBoomerang.left;
          move = "left";
      }
      else if (this.cursor.right.isDown || this.rightKey.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_X) > 0.1) {
          this.body.velocity.x = Velocity;
          this.direction = 1;
          this.lastDirection = DirectionBoomerang.right;
          move = "right";
      }

      if (this.cursor.up.isDown || this.up.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_Y) < -0.1) {
        this.body.velocity.y = -Velocity;
        this.lastDirection = DirectionBoomerang.up;
        move = "up";
      } else if (this.cursor.down.isDown || this.down.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_Y) > 0.1) {
        this.body.velocity.y = Velocity;
        this.lastDirection = DirectionBoomerang.down;
        move = "down";
      }

      if(this.fireButton.isDown || this.fireClick.isDown || this.pad.justPressed(Phaser.Gamepad.XBOX360_A)) {
        this.weapon.fire();
        if(!this.shootFx.isPlaying) {
          console.log("jkjk")
          this.shootFx.play("shootMarker");
        }
        this.animations.play("fire");
      }
      this.anim(move);
    }
  }

  anim(move) {
    const angle = this.angle;
    if(move === "left") {
      if((angle < - 90 + 10 && angle > -90 - 10) || (angle > 90 - 10 && angle < 90 + 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(move === "right") {
      if((angle < - 90 + 10 && angle > - 90 - 10) || (angle > 90 - 10 && angle < 90 + 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(move === "up") {
      if((angle > - 10 && angle < 10) || (angle > - 180 + 10 && angle > 180 - 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(move === "down") {
      if((angle > - 10 && angle < 10) || (angle > - 180 + 10 && angle > 180 - 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(!move) {
      this.animations.stop("walk");
      this.animations.stop("slide");
    }
  }

  directionChoosed() {
    return this.lastDirection;
  }

  damage() {
    this.life = this.life - Damage;
  }

  cure() {
    this.life = this.life + Damage;
  }

  lifeInPercent() {
    return this.life / MaxLife;
  }

  isDeath() {
    return this.life < 0;
  }

  isOutSideTheLevel(game) {
    return this.body.position.x + CharacterWitdh < 0 ||
           this.body.position.x > game.world.bounds.width ||
           this.body.position.y < 0 ||
           this.body.position.y + CharacterHeight > game.world.bounds.height;
  }

  bullets() {
    return this.weapon.bullets;
  }

}

export default Character;<commit_msg>remove console.log<commit_after>import { SpritePlayer, SpriteBullet, ShootSound } from "../SpriteConstants";
import { CharacterWitdh, CharacterHeight, DirectionBoomerang } from "../Constants";

const Damage = 10;
const Velocity = 200;
const MaxBullet = 10;
const TimeLapse = 10;
const MaxLife = 1000;

class Character extends Phaser.Sprite {

  constructor(game, x, y, key, frame) {
    super(game, x, y, SpritePlayer, frame);
    game.physics.arcade.enable(this);
    //this.body.gravity.y = 500;
    this.body.bounce.x = this.body.bounce.y = 0;
    this.body.mass = 1;
    this.direction = 1;
    this.anchor.setTo(0.5, 0.5);
    this.life = MaxLife;

    const fire = [8, 9, 10, 11];
    const walk = [0, 1, 2, 3, 4, 5, 6, 7];
    const slide = [12, 13, 14, 15, 14, 13, 12];

    this.animations.add('walk', walk, TimeLapse, false);
    this.animations.add('slide', slide, TimeLapse, false);
    this.animations.add('fire', fire, TimeLapse * 6, false);

    this.weapon = game.add.weapon(MaxBullet, SpriteBullet);
    this.weapon.bulletKillType = Phaser.Weapon.KILL_WORLD_BOUNDS;
    this.weapon.bulletAngleOffset = 90;
    //this.weapon.addBulletAnimation("fire", fire, TimeLapse, true);
    this.weapon.bulletAngleVariance = 5;
    this.weapon.bulletSpeed = 500;
    this.weapon.fireRate = 300;

    this.weapon.trackSprite(this, 30, 20, true);
    this.shootFx = game.add.audio(ShootSound);
    this.shootFx.allowMultiple = true;

     this.shootFx.addMarker('shootMarker', 0, 0.3);

    this.pad = this.game.input.gamepad.pad1;
    this.cursor = game.input.keyboard.createCursorKeys();

    this.fireButton = game.input.keyboard.addKey(Phaser.KeyCode.SPACEBAR);
    this.fireClick = game.input.activePointer.leftButton;

    this.up = game.input.keyboard.addKey(Phaser.Keyboard.Z);
    this.leftKey = game.input.keyboard.addKey(Phaser.Keyboard.Q);
    this.rightKey = game.input.keyboard.addKey(Phaser.Keyboard.D);
    this.down = game.input.keyboard.addKey(Phaser.Keyboard.S);

    this.lastDirection = null;
  }

  update() {
    if(!this.isDeath()) {
      this.body.velocity.x = 0;
      this.body.velocity.y = 0;
      this.rotation = this.game.physics.arcade.angleToPointer(this);
      let move = null;
      //console.log(this.angle)

      if (this.cursor.left.isDown || this.leftKey.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_X) < -0.1 ) {
          this.body.velocity.x = -Velocity;
          this.direction = -1;
          this.lastDirection = DirectionBoomerang.left;
          move = "left";
      }
      else if (this.cursor.right.isDown || this.rightKey.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_X) > 0.1) {
          this.body.velocity.x = Velocity;
          this.direction = 1;
          this.lastDirection = DirectionBoomerang.right;
          move = "right";
      }

      if (this.cursor.up.isDown || this.up.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_Y) < -0.1) {
        this.body.velocity.y = -Velocity;
        this.lastDirection = DirectionBoomerang.up;
        move = "up";
      } else if (this.cursor.down.isDown || this.down.isDown || this.pad.axis(Phaser.Gamepad.XBOX360_STICK_LEFT_Y) > 0.1) {
        this.body.velocity.y = Velocity;
        this.lastDirection = DirectionBoomerang.down;
        move = "down";
      }

      if(this.fireButton.isDown || this.fireClick.isDown || this.pad.justPressed(Phaser.Gamepad.XBOX360_A)) {
        this.weapon.fire();
        if(!this.shootFx.isPlaying) {
          this.shootFx.play("shootMarker");
        }
        this.animations.play("fire");
      }
      this.anim(move);
    }
  }

  anim(move) {
    const angle = this.angle;
    if(move === "left") {
      if((angle < - 90 + 10 && angle > -90 - 10) || (angle > 90 - 10 && angle < 90 + 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(move === "right") {
      if((angle < - 90 + 10 && angle > - 90 - 10) || (angle > 90 - 10 && angle < 90 + 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(move === "up") {
      if((angle > - 10 && angle < 10) || (angle > - 180 + 10 && angle > 180 - 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(move === "down") {
      if((angle > - 10 && angle < 10) || (angle > - 180 + 10 && angle > 180 - 10)) {
        this.animations.play("slide");
      } else {
        this.animations.play("walk");
      }
    }

    if(!move) {
      this.animations.stop("walk");
      this.animations.stop("slide");
    }
  }

  directionChoosed() {
    return this.lastDirection;
  }

  damage() {
    this.life = this.life - Damage;
  }

  cure() {
    this.life = this.life + Damage;
  }

  lifeInPercent() {
    return this.life / MaxLife;
  }

  isDeath() {
    return this.life < 0;
  }

  isOutSideTheLevel(game) {
    return this.body.position.x + CharacterWitdh < 0 ||
           this.body.position.x > game.world.bounds.width ||
           this.body.position.y < 0 ||
           this.body.position.y + CharacterHeight > game.world.bounds.height;
  }

  bullets() {
    return this.weapon.bullets;
  }

}

export default Character;<|endoftext|>


<commit_before>(function (FMS, Backbone, _, $) {
    _.extend( FMS, {
        LocatorView: FMS.FMSView.extend({
            skipLocationCheck: false,

            locate: function() {
                $(document).delegate('.ui-content', 'touchmove', false);
                $('#locating').show();
                this.listenTo(FMS.locator, 'gps_located', this.gotLocation);
                this.listenTo(FMS.locator, 'gps_failed', this.failedLocation);
                this.listenTo(FMS.locator, 'gps_locating', this.locationUpdate);

                FMS.locator.geolocate(CONFIG.ACCURACY, this.skipLocationCheck);
                this.startLocateProgress();
            },

            startLocateProgress: function() {
                this.located = false;
                this.locateCount = 1;
                var that = this;
                window.setTimeout( function() {that.showLocateProgress();}, 1000);
            },

            locationUpdate: function( accuracy ) {
                if ( accuracy && accuracy < 500 ) {
                    $('#progress-bar').css( 'background-color', 'orange' );
                } else if ( accuracy && accuracy < 250 ) {
                    $('#progress-bar').css( 'background-color', 'yellow' );
                } else {
                    $('#progress-bar').css( 'background-color', 'grey' );
                }

                $('#accuracy').text(parseInt(accuracy, 10) + 'm');
            },

            showLocateProgress: function() {
                if ( !this.located && this.locateCount > 20 ) {
                    var details = { msg: FMS.strings.geolocation_failed };
                    this.failedLocation(details);
                    return;
                }
                var percent = ( ( 20 - this.locateCount ) / 20 ) * 100;
                $('#progress-bar').css( 'width', percent + '%' );
                this.locateCount++;
                var that = this;
                window.setTimeout( function() {that.showLocateProgress();}, 1000);
            },

            finishedLocating: function() {
                this.stopListening(FMS.locator, 'gps_locating');
                this.stopListening(FMS.locator, 'gps_located');
                this.stopListening(FMS.locator, 'gps_failed');
                $(document).undelegate('.ui-content', 'touchmove', false);
                $('#locating').hide();
            },

            failedLocation: function(details) {
                this.finishedLocating();
            },

            gotLocation: function(info) {
                this.finishedLocating();
            }
        })
    });
})(FMS, Backbone, _, $);
<commit_msg>Stop running locate progress indicator  when hidden<commit_after>(function (FMS, Backbone, _, $) {
    _.extend( FMS, {
        LocatorView: FMS.FMSView.extend({
            skipLocationCheck: false,

            locate: function() {
                $(document).delegate('.ui-content', 'touchmove', false);
                $('#locating').show();
                this.listenTo(FMS.locator, 'gps_located', this.gotLocation);
                this.listenTo(FMS.locator, 'gps_failed', this.failedLocation);
                this.listenTo(FMS.locator, 'gps_locating', this.locationUpdate);

                FMS.locator.geolocate(CONFIG.ACCURACY, this.skipLocationCheck);
                this.startLocateProgress();
            },

            startLocateProgress: function() {
                this.located = false;
                this.locateCount = 1;
                var that = this;
                window.setTimeout( function() {that.showLocateProgress();}, 1000);
            },

            locationUpdate: function( accuracy ) {
                if ( accuracy && accuracy < 500 ) {
                    $('#progress-bar').css( 'background-color', 'orange' );
                } else if ( accuracy && accuracy < 250 ) {
                    $('#progress-bar').css( 'background-color', 'yellow' );
                } else {
                    $('#progress-bar').css( 'background-color', 'grey' );
                }

                $('#accuracy').text(parseInt(accuracy, 10) + 'm');
            },

            showLocateProgress: function() {
                if ( $('#locating').css('display') == 'none' ) {
                    return;
                }
                if ( !this.located && this.locateCount > 20 ) {
                    var details = { msg: FMS.strings.geolocation_failed };
                    this.failedLocation(details);
                    return;
                }
                var percent = ( ( 20 - this.locateCount ) / 20 ) * 100;
                $('#progress-bar').css( 'width', percent + '%' );
                this.locateCount++;
                var that = this;
                window.setTimeout( function() {that.showLocateProgress();}, 1000);
            },

            finishedLocating: function() {
                this.stopListening(FMS.locator, 'gps_locating');
                this.stopListening(FMS.locator, 'gps_located');
                this.stopListening(FMS.locator, 'gps_failed');
                $(document).undelegate('.ui-content', 'touchmove', false);
                $('#locating').hide();
            },

            failedLocation: function(details) {
                this.finishedLocating();
            },

            gotLocation: function(info) {
                this.finishedLocating();
            }
        })
    });
})(FMS, Backbone, _, $);
<|endoftext|>


<commit_before>'use strict';

// see http://markgoodyear.com/2014/01/getting-started-with-gulp/
var gulp = require('gulp');
var gutil  = require('gulp-util');
var lazypipe = require('lazypipe');
var less = require('gulp-less');
var autoprefixer = require('gulp-autoprefixer');
var minifycss = require('gulp-minify-css');
var browserify = require('gulp-browserify');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var imagemin = require('gulp-imagemin');
var svgmin = require('gulp-svgmin');
var rimraf = require('gulp-rimraf'); // preferred over gulp-clean, see https://github.com/peter-vilja/gulp-clean/pull/3
var ngmin = require('gulp-ngmin');
var htmlmin = require('gulp-htmlmin');
var rev = require('gulp-rev');
var inject = require('gulp-inject');
var mocha = require('gulp-mocha');
var gettext = require('gulp-angular-gettext');
var karma = require('karma');
var open = require('open');
var path = require('path');
var fork = require('child_process').fork;
var path = require('path');
var _ = require('lodash');

// add Kibana's grunt tasks
// blocked on https://github.com/gratimax/gulp-grunt/issues/3
// for now you'll have to manually build Kibana
//require('gulp-grunt')(gulp, {
//  base: __dirname + '/kibana',
//  prefix: 'kibana-'
//});

// TODO use gulp-changed

// TODO get livereload working with https://github.com/mollerse/gulp-embedlr

var paths = {
  scripts: 'public/scripts/**/*.js',
  scriptMain: 'public/scripts/app.js',
  styles: 'public/styles/**/*.less',
  svgs: 'public/images/**/*.svg',
  html: 'views/**/*.html',
  partials: 'public/partials/**/*.html',
  indexHtml: 'views/index.html',
  serverTests: 'test/server/**/test-*.js',
  clientTests: 'test/client/**/test-*.js',
  imagesDest: 'dist/public/images',
  bowerComponents: 'public/bower_components',
  nodeModules: 'public/node_modules',
  copiedFonts: 'public/fonts/bootstrap'
};

var fontExtensions = ['.eot', '.svg', '.ttf', '.woff'];

// build CSS for production
gulp.task('styles', ['clean-styles'], function () {
  // need to depend on clean-styles b/c inject will add all CSS files in dist/styles (including any old ones)
  return gulp.src(paths.styles)
    .pipe(less({
      paths: [paths.bowerComponents, paths.nodeModules]
    }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(minifycss())
    .pipe(rev())
    .pipe(gulp.dest('dist/public/styles'));
});

gulp.task('clean-styles', function () {
  return gulp.src('dist/public/styles', {read: false})
    .pipe(rimraf());
});

// Copy Bootstap's fonts to public/fonts
gulp.task('bootstrap-fonts', function () {
  var fontPaths = fontExtensions.map(function (ext) {
    return 'public/bower_components/bootstrap/fonts/**/*' + ext;
  });

  return gulp.src(fontPaths)
    .pipe(gulp.dest('public/fonts/bootstrap'));
});

gulp.task('fonts', ['bootstrap-fonts'], function () {
  return gulp.src(fontExtensions.map(function (ext) {
      return 'public/fonts/**/*' + ext;
    }))
    .pipe(gulp.dest('dist/public/fonts'));
});

gulp.task('jshint', function () {
  return gulp.src(paths.scripts)
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

// If we ever need to split up our browserify bundles, here are some useful links:
// https://github.com/domenic/browserify-deoptimizer
// http://benclinkinbeard.com/posts/external-bundles-for-faster-browserify-builds/
// http://esa-matti.suuronen.org/blog/2013/04/15/asynchronous-module-loading-with-browserify/

gulp.task('scripts', ['jshint'], function () {
  return gulp.src(paths.scriptMain)
    .pipe(browserify({
      debug: false // we use browserify-middleware in development
    }))
    .pipe(ngmin())
    .pipe(uglify({
      preserveComments: 'some' // preserve license headers
    }))
    .pipe(rev())
    .pipe(gulp.dest('dist/public/scripts'));
});

gulp.task('clean-scripts', function () {
  return gulp.src('dist/public/scripts', {read: false})
    .pipe(rimraf());
});

var imageminTransform = function () {
  return imagemin({
    optimizationLevel: 3,
    progressive: true,
    interlaced: true
  });
};

// Minifying JPGs, PNGs, and GIFs requires native libs which can be annoying to install on Windows, so
// they're all optional dependencies (see https://github.com/kevva/image-min). If the package isn't installed,
// we don't do the minification.

gulp.task('jpgs', function () {
  var pipeline = gulp.src(['public/images/**/*.jpg', 'public/images/**/*.jpeg']);

  // jpegtran-bin can be annoying on Windows, so make it optional
  var canJpeg = (function () {
    try {
      require.resolve('jpegtran-bin');
      return true;
    } catch (e) {
      return false;
    }
  })();
  if (canJpeg) {
    pipeline = pipeline.pipe(imageminTransform());
  } else {
    console.warn('jpegtran-bin not installed. Not minifying jpgs');
  }

  return pipeline.pipe(gulp.dest(paths.imagesDest));
});

gulp.task('pngs', function () {
  var pipeline = gulp.src(['public/images/**/*.png']);
  var canPng = (function () {
    try {
      require.resolve('pngquant-bin');
      return true;
    } catch (e) {
      return false;
    }
  })();
  if (canPng) {
    pipeline = pipeline.pipe(imageminTransform());
  } else {
    console.warn('pngquant-bin not installed. Not minifying pngs');
  }

  return pipeline.pipe(gulp.dest(paths.imagesDest));
});

gulp.task('gifs', function () {
  var pipeline = gulp.src(['public/images/**/*.gif']);
  var canPng = (function () {
    try {
      require.resolve('gifsicle');
      return true;
    } catch (e) {
      return false;
    }
  })();
  if (canPng) {
    pipeline = pipeline.pipe(imageminTransform());
  } else {
    console.warn('gifsicle not installed. Not minifying gifs');
  }

  return pipeline.pipe(gulp.dest(paths.imagesDest));
});

gulp.task('svgs', function () {
  return gulp.src(paths.svgs)
    .pipe(svgmin())
    .pipe(gulp.dest('dist/public/images'));
});

gulp.task('images', ['jpgs', 'pngs', 'gifs', 'svgs']);

gulp.task('partials', function () {
  return gulp.src(paths.partials)
    .pipe(htmlmin({
      collapseWhitespace: true,
      collapseBooleanAttributes: true,
      removeComments: true,
      removeAttributeQuotes: false,
      removeRedundantAttributes: true,
      removeEmptyAttributes: true,
      removeOptionalTags: false // removing is probably a bad idea with partial docs
    }))
    .pipe(gulp.dest('dist/public/partials'));
});

// Although we do a lot of processing in middleware, this task is still useful to replace references to resources
// with references to revved versions.
gulp.task('inject', ['styles', 'scripts'], function () {
  // TODO clean up when https://github.com/klei/gulp-inject/issues/9 is resolved
  return gulp.src(['dist/public/scripts/**/*.js', 'dist/public/styles/**/*.css'], {read: false})
    .pipe(inject(paths.indexHtml, {
      ignorePath: '/dist'
    }))
    .pipe(gulp.dest('dist/views'));
});

gulp.task('html', ['inject'], function () {
  return gulp.src(['views/**/*.html', '!' + paths.indexHtml, 'dist/views/**/*.html'])
    .pipe(htmlmin({
      collapseWhitespace: true,
      collapseBooleanAttributes: true,
      removeComments: true,
      removeAttributeQuotes: false,
      removeRedundantAttributes: true,
      useShortDoctype: true, // enforces what we already do
      removeEmptyAttributes: true,
      removeOptionalTags: true
    }))
    .pipe(gulp.dest('dist/views'));
});

//gulp.task('watch', function () {
//  // TODO get this working when manually rebuilding gets annoying enough
//});

gulp.task('pot', function () {
  gulp.src([paths.partials, paths.scripts])
    .pipe(gettext.extract('fracas.pot', {
      postProcess: function (po) {
        po.items.forEach(function (item) {
          item.references = item.references.map(function (ref) {
            return path.relative(path.join(__dirname, 'po'), ref)
              .replace(/\\/g, '/'); // replace any Windows-style paths
          });
        });
      }
    }))
    .pipe(gulp.dest('po/'));
});

gulp.task('translations', function () {
  gulp.src('po/*.po')
    .pipe(gettext.compile({
      format: 'json'
    }))
    .pipe(gulp.dest('dist/public/translations/'));
});

gulp.task('clean', function () {
  return gulp.src(['dist', '.tmp', paths.copiedFonts], {read: false})
    .pipe(rimraf());
});

gulp.task('build', ['images', 'fonts', 'partials', 'html', 'pot', 'translations'/*, 'kibana-build'*/]);

gulp.task('server', ['build'], function (callback) {
  var env = _.clone(process.env);
  env.NODE_ENV = 'production';

  var child = fork(__dirname + '/server.js', [], {
    env: env
  });
  child.on('message', function (m) {
    if (m.started) {
      gutil.log('Opening ' + m.url);
      open(m.url);
    }
  });
  child.on('error', callback);
  child.on('exit', function () {
    callback(null);
  });
});

var mochaTransform = lazypipe()
  .pipe(function () {
    return mocha({
      ui: 'bdd',
      reporter: 'nyan'
    });
  });

// if we want integration tests, we can split this into server-unit-tests and server-integration tests
gulp.task('server-tests', function () {
  return gulp.src(paths.serverTests, {read: false})
    .pipe(mochaTransform());
});

// these are unit tests, integration/e2e tests would use Protractor as the test runner
gulp.task('client-tests', function (cb) {
  // FIXME this doesn't work
  karma.server.start({configFile: __dirname + '/test/client/karma.conf.js'}, function () {
    cb(null);
  });
});

gulp.task('tests', ['server-tests']);

gulp.task('default', ['build']);
<commit_msg>Fix path reference for gulp build.<commit_after>'use strict';

// see http://markgoodyear.com/2014/01/getting-started-with-gulp/
var gulp = require('gulp');
var gutil  = require('gulp-util');
var lazypipe = require('lazypipe');
var less = require('gulp-less');
var autoprefixer = require('gulp-autoprefixer');
var minifycss = require('gulp-minify-css');
var browserify = require('gulp-browserify');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var imagemin = require('gulp-imagemin');
var svgmin = require('gulp-svgmin');
var rimraf = require('gulp-rimraf'); // preferred over gulp-clean, see https://github.com/peter-vilja/gulp-clean/pull/3
var ngmin = require('gulp-ngmin');
var htmlmin = require('gulp-htmlmin');
var rev = require('gulp-rev');
var inject = require('gulp-inject');
var mocha = require('gulp-mocha');
var gettext = require('gulp-angular-gettext');
var karma = require('karma');
var open = require('open');
var path = require('path');
var fork = require('child_process').fork;
var path = require('path');
var _ = require('lodash');

// add Kibana's grunt tasks
// blocked on https://github.com/gratimax/gulp-grunt/issues/3
// for now you'll have to manually build Kibana
//require('gulp-grunt')(gulp, {
//  base: __dirname + '/kibana',
//  prefix: 'kibana-'
//});

// TODO use gulp-changed

// TODO get livereload working with https://github.com/mollerse/gulp-embedlr

var paths = {
  scripts: 'public/scripts/**/*.js',
  scriptMain: 'public/scripts/app.js',
  styles: 'public/styles/**/*.less',
  svgs: 'public/images/**/*.svg',
  html: 'views/**/*.html',
  partials: 'public/partials/**/*.html',
  indexHtml: 'views/index.html',
  serverTests: 'test/server/**/test-*.js',
  clientTests: 'test/client/**/test-*.js',
  imagesDest: 'dist/public/images',
  bowerComponents: 'public/bower_components',
  nodeModules: 'node_modules',
  copiedFonts: 'public/fonts/bootstrap'
};

var fontExtensions = ['.eot', '.svg', '.ttf', '.woff'];

// build CSS for production
gulp.task('styles', ['clean-styles'], function () {
  // need to depend on clean-styles b/c inject will add all CSS files in dist/styles (including any old ones)
  return gulp.src(paths.styles)
    .pipe(less({
      paths: [paths.bowerComponents, paths.nodeModules]
    }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(minifycss())
    .pipe(rev())
    .pipe(gulp.dest('dist/public/styles'));
});

gulp.task('clean-styles', function () {
  return gulp.src('dist/public/styles', {read: false})
    .pipe(rimraf());
});

// Copy Bootstap's fonts to public/fonts
gulp.task('bootstrap-fonts', function () {
  var fontPaths = fontExtensions.map(function (ext) {
    return 'public/bower_components/bootstrap/fonts/**/*' + ext;
  });

  return gulp.src(fontPaths)
    .pipe(gulp.dest('public/fonts/bootstrap'));
});

gulp.task('fonts', ['bootstrap-fonts'], function () {
  return gulp.src(fontExtensions.map(function (ext) {
      return 'public/fonts/**/*' + ext;
    }))
    .pipe(gulp.dest('dist/public/fonts'));
});

gulp.task('jshint', function () {
  return gulp.src(paths.scripts)
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

// If we ever need to split up our browserify bundles, here are some useful links:
// https://github.com/domenic/browserify-deoptimizer
// http://benclinkinbeard.com/posts/external-bundles-for-faster-browserify-builds/
// http://esa-matti.suuronen.org/blog/2013/04/15/asynchronous-module-loading-with-browserify/

gulp.task('scripts', ['jshint'], function () {
  return gulp.src(paths.scriptMain)
    .pipe(browserify({
      debug: false // we use browserify-middleware in development
    }))
    .pipe(ngmin())
    .pipe(uglify({
      preserveComments: 'some' // preserve license headers
    }))
    .pipe(rev())
    .pipe(gulp.dest('dist/public/scripts'));
});

gulp.task('clean-scripts', function () {
  return gulp.src('dist/public/scripts', {read: false})
    .pipe(rimraf());
});

var imageminTransform = function () {
  return imagemin({
    optimizationLevel: 3,
    progressive: true,
    interlaced: true
  });
};

// Minifying JPGs, PNGs, and GIFs requires native libs which can be annoying to install on Windows, so
// they're all optional dependencies (see https://github.com/kevva/image-min). If the package isn't installed,
// we don't do the minification.

gulp.task('jpgs', function () {
  var pipeline = gulp.src(['public/images/**/*.jpg', 'public/images/**/*.jpeg']);

  // jpegtran-bin can be annoying on Windows, so make it optional
  var canJpeg = (function () {
    try {
      require.resolve('jpegtran-bin');
      return true;
    } catch (e) {
      return false;
    }
  })();
  if (canJpeg) {
    pipeline = pipeline.pipe(imageminTransform());
  } else {
    console.warn('jpegtran-bin not installed. Not minifying jpgs');
  }

  return pipeline.pipe(gulp.dest(paths.imagesDest));
});

gulp.task('pngs', function () {
  var pipeline = gulp.src(['public/images/**/*.png']);
  var canPng = (function () {
    try {
      require.resolve('pngquant-bin');
      return true;
    } catch (e) {
      return false;
    }
  })();
  if (canPng) {
    pipeline = pipeline.pipe(imageminTransform());
  } else {
    console.warn('pngquant-bin not installed. Not minifying pngs');
  }

  return pipeline.pipe(gulp.dest(paths.imagesDest));
});

gulp.task('gifs', function () {
  var pipeline = gulp.src(['public/images/**/*.gif']);
  var canPng = (function () {
    try {
      require.resolve('gifsicle');
      return true;
    } catch (e) {
      return false;
    }
  })();
  if (canPng) {
    pipeline = pipeline.pipe(imageminTransform());
  } else {
    console.warn('gifsicle not installed. Not minifying gifs');
  }

  return pipeline.pipe(gulp.dest(paths.imagesDest));
});

gulp.task('svgs', function () {
  return gulp.src(paths.svgs)
    .pipe(svgmin())
    .pipe(gulp.dest('dist/public/images'));
});

gulp.task('images', ['jpgs', 'pngs', 'gifs', 'svgs']);

gulp.task('partials', function () {
  return gulp.src(paths.partials)
    .pipe(htmlmin({
      collapseWhitespace: true,
      collapseBooleanAttributes: true,
      removeComments: true,
      removeAttributeQuotes: false,
      removeRedundantAttributes: true,
      removeEmptyAttributes: true,
      removeOptionalTags: false // removing is probably a bad idea with partial docs
    }))
    .pipe(gulp.dest('dist/public/partials'));
});

// Although we do a lot of processing in middleware, this task is still useful to replace references to resources
// with references to revved versions.
gulp.task('inject', ['styles', 'scripts'], function () {
  // TODO clean up when https://github.com/klei/gulp-inject/issues/9 is resolved
  return gulp.src(['dist/public/scripts/**/*.js', 'dist/public/styles/**/*.css'], {read: false})
    .pipe(inject(paths.indexHtml, {
      ignorePath: '/dist'
    }))
    .pipe(gulp.dest('dist/views'));
});

gulp.task('html', ['inject'], function () {
  return gulp.src(['views/**/*.html', '!' + paths.indexHtml, 'dist/views/**/*.html'])
    .pipe(htmlmin({
      collapseWhitespace: true,
      collapseBooleanAttributes: true,
      removeComments: true,
      removeAttributeQuotes: false,
      removeRedundantAttributes: true,
      useShortDoctype: true, // enforces what we already do
      removeEmptyAttributes: true,
      removeOptionalTags: true
    }))
    .pipe(gulp.dest('dist/views'));
});

//gulp.task('watch', function () {
//  // TODO get this working when manually rebuilding gets annoying enough
//});

gulp.task('pot', function () {
  gulp.src([paths.partials, paths.scripts])
    .pipe(gettext.extract('fracas.pot', {
      postProcess: function (po) {
        po.items.forEach(function (item) {
          item.references = item.references.map(function (ref) {
            return path.relative(path.join(__dirname, 'po'), ref)
              .replace(/\\/g, '/'); // replace any Windows-style paths
          });
        });
      }
    }))
    .pipe(gulp.dest('po/'));
});

gulp.task('translations', function () {
  gulp.src('po/*.po')
    .pipe(gettext.compile({
      format: 'json'
    }))
    .pipe(gulp.dest('dist/public/translations/'));
});

gulp.task('clean', function () {
  return gulp.src(['dist', '.tmp', paths.copiedFonts], {read: false})
    .pipe(rimraf());
});

gulp.task('build', ['images', 'fonts', 'partials', 'html', 'pot', 'translations'/*, 'kibana-build'*/]);

gulp.task('server', ['build'], function (callback) {
  var env = _.clone(process.env);
  env.NODE_ENV = 'production';

  var child = fork(__dirname + '/server.js', [], {
    env: env
  });
  child.on('message', function (m) {
    if (m.started) {
      gutil.log('Opening ' + m.url);
      open(m.url);
    }
  });
  child.on('error', callback);
  child.on('exit', function () {
    callback(null);
  });
});

var mochaTransform = lazypipe()
  .pipe(function () {
    return mocha({
      ui: 'bdd',
      reporter: 'nyan'
    });
  });

// if we want integration tests, we can split this into server-unit-tests and server-integration tests
gulp.task('server-tests', function () {
  return gulp.src(paths.serverTests, {read: false})
    .pipe(mochaTransform());
});

// these are unit tests, integration/e2e tests would use Protractor as the test runner
gulp.task('client-tests', function (cb) {
  // FIXME this doesn't work
  karma.server.start({configFile: __dirname + '/test/client/karma.conf.js'}, function () {
    cb(null);
  });
});

gulp.task('tests', ['server-tests']);

gulp.task('default', ['build']);
<|endoftext|>


<commit_before>import React from 'react';
import updateQuestion from '../../../../actions/questions/updateQuestion';

class ContentQuestionEdit extends React.Component {

    constructor(props) {
        super(props);
        const numAnswers = this.props.question.answers.length;
        this.state = {
            qid: this.props.question.id,
            title: this.props.question.title,
            difficulty: this.props.question.difficulty,
            answer1: this.props.question.answers[0].answer,
            answer2: numAnswers > 1 ? this.props.question.answers[1].answer: '',
            answer3: numAnswers > 2 ? this.props.question.answers[2].answer: '',
            answer4: numAnswers > 3 ? this.props.question.answers[3].answer: '',
            correct1: this.props.question.answers[0].correct,
            correct2: numAnswers > 2 ? this.props.question.answers[1].correct: '',
            correct3: numAnswers > 3 ? this.props.question.answers[2].correct: '',
            correct4: numAnswers > 3 ? this.props.question.answers[3].correct: '',
            explanation: 'Test explanation', //this.props.question.explanation,
            userId: this.props.userId,
            relatedObjectId: this.props.selector.sid,
            relatedObject: this.props.selector.stype,
        };
        this.updateQuestionTitle = this.updateQuestionTitle.bind(this);
        this.updateQuestionDifficulty = this.updateQuestionDifficulty.bind(this);
        /* update answers */
        this.updateAnswer1 = this.updateAnswer1.bind(this);
        this.updateAnswer2 = this.updateAnswer2.bind(this);
        this.updateAnswer3 = this.updateAnswer3.bind(this);
        this.updateAnswer4 = this.updateAnswer4.bind(this);
        /* update correct answer choice */
        this.updateCorrect1 = this.updateCorrect1.bind(this);
        this.updateCorrect2 = this.updateCorrect2.bind(this);
        this.updateCorrect3 = this.updateCorrect3.bind(this);
        this.updateCorrect4 = this.updateCorrect4.bind(this);

        this.updateExplanation = this.updateExplanation.bind(this);
        this.saveButtonClick = this.saveButtonClick.bind(this);
    };

    saveButtonClick(e) {
        e.preventDefault();
        this.context.executeAction(updateQuestion, {question: this.state});
    }

    /* Update answer choice text */
    updateAnswer1(e) {
        this.setState({answer1: e.target.value});
    }

    updateAnswer2(e) {
        this.setState({answer2: e.target.value});
    }

    updateAnswer3(e) {
        this.setState({answer3: e.target.value});
    }

    updateAnswer4(e) {
        //console.log(e.target.value);
        this.setState({answer4: e.target.value});
    }

    /* Update correct choice among available answer choices */
    updateCorrect1(e) {
        this.setState({correct1: e.target.checked});
    }

    updateCorrect2(e) {
        this.setState({correct2: e.target.checked});
    }

    updateCorrect3(e) {
        this.setState({correct3: e.target.checked});
    }

    updateCorrect4(e) {
        console.log(e.target.value);
        this.setState({correct4: e.target.checked});
    }

    updateExplanation(e) {
        this.setState({explanation: e.target.value});
    }

    updateQuestionTitle(e) {
        this.setState({title: e.target.value});
    };

    updateQuestionDifficulty(e) {
        this.setState({difficulty: e.target.value});
    }

    render() {
        const numAnswers = this.props.question.answers.length;
        const answerChoiceWidth = {
            width: '680px',
        };
        return (

                    <form className="ui form">

Question
                                <textarea rows="3"  name="question" id="question" aria-required="true" defaultValue={this.state.title} onChange={this.updateQuestionTitle} />

                                <fieldset>
                                    <legend>Difficulty</legend>

                                                {this.state.difficulty === 1 ?
                                                    <input type="radio" id="easy" name="difficulty" checked="checked" defaultValue={1} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                    : <input type="radio" id="easy" name="difficulty" defaultValue={1} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                }
                                                Easy

                                                {this.state.difficulty === 2 ?
                                                    <input type="radio" id="moderate" name="difficulty" checked="checked" defaultValue={2} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                    : <input type="radio" id="moderate" name="difficulty" defaultValue={2} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                }
                                                Moderate

                                            {this.state.difficulty === 3 ?
                                                <input type="radio" id="hard" name="difficulty" checked="checked" defaultValue={3} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                : <input type="radio" id="hard" name="difficulty" defaultValue={3} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                            }
                                                Hard

                                </fieldset>

                            <fieldset>
                                <legend>Answer Choices</legend>

                                        <input type="checkbox" name="example1" id="answer1" tabIndex="0" className="hidden" defaultChecked={this.state.correct1} onChange={this.updateCorrect1}/>

                                    <input style={answerChoiceWidth} type="text" name="response1" id="response1" defaultValue={this.state.answer1}  onChange={this.updateAnswer1}/>

                                        <input  type="checkbox" name="example2" id="answer2" tabIndex="0" className="hidden" defaultChecked={numAnswers > 2 && this.state.correct2} onChange={this.updateCorrect2}/>

                                    <input style={answerChoiceWidth} type="text" name="response2" id="response2" defaultValue={numAnswers > 1 ? this.state.answer2: ''}  onChange={this.updateAnswer2}/>

                                        <input type="checkbox" name="example3" id="answer3" tabIndex="0" className="hidden" defaultChecked={numAnswers > 2 && this.state.correct3} onChange={this.updateCorrect3}/>

                                    <input type="text" style={answerChoiceWidth} name="response4" id="response4" defaultValue={numAnswers > 2 ? this.state.answer3: ''} onChange={this.updateAnswer3}/>

                                        <input type="checkbox" name="example4" id="answer4" tabIndex="0" className="hidden" defaultChecked={(numAnswers > 3 && this.state.correct4)} onChange={this.updateCorrect4}/>

                                    <input type="text" style={answerChoiceWidth} name="response4" id="response4" defaultValue={numAnswers > 3 ? this.state.answer4: ''} onChange={this.updateAnswer4}/>

                            </fieldset>

                            Explanation (optional)
                            <textarea rows="2" id="explanation" defaultValue={this.state.explanation} onChange={this.updateExplanation}></textarea>

                                    <button className="ui primary button" onClick={this.saveButtonClick}>Save</button>
                                    <button className="ui secondary button">Cancel</button>

                    </form>

        );
    }
}

ContentQuestionEdit.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};

export default ContentQuestionEdit;
<commit_msg>Remove debug code<commit_after>import React from 'react';
import updateQuestion from '../../../../actions/questions/updateQuestion';

class ContentQuestionEdit extends React.Component {

    constructor(props) {
        super(props);
        const numAnswers = this.props.question.answers.length;
        this.state = {
            qid: this.props.question.id,
            title: this.props.question.title,
            difficulty: this.props.question.difficulty,
            answer1: this.props.question.answers[0].answer,
            answer2: numAnswers > 1 ? this.props.question.answers[1].answer: '',
            answer3: numAnswers > 2 ? this.props.question.answers[2].answer: '',
            answer4: numAnswers > 3 ? this.props.question.answers[3].answer: '',
            correct1: this.props.question.answers[0].correct,
            correct2: numAnswers > 2 ? this.props.question.answers[1].correct: '',
            correct3: numAnswers > 3 ? this.props.question.answers[2].correct: '',
            correct4: numAnswers > 3 ? this.props.question.answers[3].correct: '',
            explanation: 'Test explanation', //this.props.question.explanation,
            userId: this.props.userId,
            relatedObjectId: this.props.selector.sid,
            relatedObject: this.props.selector.stype,
        };
        this.updateQuestionTitle = this.updateQuestionTitle.bind(this);
        this.updateQuestionDifficulty = this.updateQuestionDifficulty.bind(this);
        /* update answers */
        this.updateAnswer1 = this.updateAnswer1.bind(this);
        this.updateAnswer2 = this.updateAnswer2.bind(this);
        this.updateAnswer3 = this.updateAnswer3.bind(this);
        this.updateAnswer4 = this.updateAnswer4.bind(this);
        /* update correct answer choice */
        this.updateCorrect1 = this.updateCorrect1.bind(this);
        this.updateCorrect2 = this.updateCorrect2.bind(this);
        this.updateCorrect3 = this.updateCorrect3.bind(this);
        this.updateCorrect4 = this.updateCorrect4.bind(this);

        this.updateExplanation = this.updateExplanation.bind(this);
        this.saveButtonClick = this.saveButtonClick.bind(this);
    };

    saveButtonClick(e) {
        e.preventDefault();
        this.context.executeAction(updateQuestion, {question: this.state});
    }

    /* Update answer choice text */
    updateAnswer1(e) {
        this.setState({answer1: e.target.value});
    }

    updateAnswer2(e) {
        this.setState({answer2: e.target.value});
    }

    updateAnswer3(e) {
        this.setState({answer3: e.target.value});
    }

    updateAnswer4(e) {
        //console.log(e.target.value);
        this.setState({answer4: e.target.value});
    }

    /* Update correct choice among available answer choices */
    updateCorrect1(e) {
        this.setState({correct1: e.target.checked});
    }

    updateCorrect2(e) {
        this.setState({correct2: e.target.checked});
    }

    updateCorrect3(e) {
        this.setState({correct3: e.target.checked});
    }

    updateCorrect4(e) {
        this.setState({correct4: e.target.checked});
    }

    updateExplanation(e) {
        this.setState({explanation: e.target.value});
    }

    updateQuestionTitle(e) {
        this.setState({title: e.target.value});
    };

    updateQuestionDifficulty(e) {
        this.setState({difficulty: e.target.value});
    }

    render() {
        const numAnswers = this.props.question.answers.length;
        const answerChoiceWidth = {
            width: '680px',
        };
        return (

                    <form className="ui form">

Question
                                <textarea rows="3"  name="question" id="question" aria-required="true" defaultValue={this.state.title} onChange={this.updateQuestionTitle} />

                                <fieldset>
                                    <legend>Difficulty</legend>

                                                {this.state.difficulty === 1 ?
                                                    <input type="radio" id="easy" name="difficulty" checked="checked" defaultValue={1} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                    : <input type="radio" id="easy" name="difficulty" defaultValue={1} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                }
                                                Easy

                                                {this.state.difficulty === 2 ?
                                                    <input type="radio" id="moderate" name="difficulty" checked="checked" defaultValue={2} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                    : <input type="radio" id="moderate" name="difficulty" defaultValue={2} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                }
                                                Moderate

                                            {this.state.difficulty === 3 ?
                                                <input type="radio" id="hard" name="difficulty" checked="checked" defaultValue={3} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                                : <input type="radio" id="hard" name="difficulty" defaultValue={3} tabIndex="0" onChange={this.updateQuestionDifficulty} />
                                            }
                                                Hard

                                </fieldset>

                            <fieldset>
                                <legend>Answer Choices</legend>

                                        <input type="checkbox" name="example1" id="answer1" tabIndex="0" className="hidden" defaultChecked={this.state.correct1} onChange={this.updateCorrect1}/>

                                    <input style={answerChoiceWidth} type="text" name="response1" id="response1" defaultValue={this.state.answer1}  onChange={this.updateAnswer1}/>

                                        <input  type="checkbox" name="example2" id="answer2" tabIndex="0" className="hidden" defaultChecked={numAnswers > 2 && this.state.correct2} onChange={this.updateCorrect2}/>

                                    <input style={answerChoiceWidth} type="text" name="response2" id="response2" defaultValue={numAnswers > 1 ? this.state.answer2: ''}  onChange={this.updateAnswer2}/>

                                        <input type="checkbox" name="example3" id="answer3" tabIndex="0" className="hidden" defaultChecked={numAnswers > 2 && this.state.correct3} onChange={this.updateCorrect3}/>

                                    <input type="text" style={answerChoiceWidth} name="response4" id="response4" defaultValue={numAnswers > 2 ? this.state.answer3: ''} onChange={this.updateAnswer3}/>

                                        <input type="checkbox" name="example4" id="answer4" tabIndex="0" className="hidden" defaultChecked={(numAnswers > 3 && this.state.correct4)} onChange={this.updateCorrect4}/>

                                    <input type="text" style={answerChoiceWidth} name="response4" id="response4" defaultValue={numAnswers > 3 ? this.state.answer4: ''} onChange={this.updateAnswer4}/>

                            </fieldset>

                            Explanation (optional)
                            <textarea rows="2" id="explanation" defaultValue={this.state.explanation} onChange={this.updateExplanation}></textarea>

                                    <button className="ui primary button" onClick={this.saveButtonClick}>Save</button>
                                    <button className="ui secondary button">Cancel</button>

                    </form>

        );
    }
}

ContentQuestionEdit.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};

export default ContentQuestionEdit;
<|endoftext|>


<commit_before>'use strict';

import JSONFormatter from 'json-formatter-js';
import {
  convertXOf,
  _if
} from './helpers.js';

/**
 * @class JSONSchemaView
 *
 * A pure JavaScript component for rendering JSON Schema in HTML.
*/
export default class JSONSchemaView {

  /**
   * @param {object} schema The JSON Schema object
   *
   * @param {number} [open=1] his number indicates up to how many levels the
   * rendered tree should expand. Set it to `0` to make the whole tree collapsed
   * or set it to `Infinity` to expand the tree deeply
   * @param {object} options.
   *  theme {string}: one of the following options: ['dark']
  */
  constructor(schema, open, options = {theme: null}) {
    this.schema = schema;
    this.open = open;
    this.options = options;
    this.isCollapsed = open <= 0;

    // if schema is an empty object which means any JOSN
    this.isAny = typeof schema === 'object' &&
      !Array.isArray(schema) &&
      !Object.keys(schema)
      .filter(k=> ['title', 'description'].indexOf(k) === -1).length;

    // Determine if a schema is an array
    this.isArray = !this.isAny && this.schema && this.schema.type === 'array';

    this.isObject = this.schema &&
      (this.schema.type === 'object' ||
       this.schema.properties ||
       this.schema.anyOf ||
       this.schema.oneof ||
       this.schema.allOf);

    // Determine if a schema is a primitive
    this.isPrimitive = !this.isAny && !this.isArray && !this.isObject;

    //
    this.showToggle = this.schema.description ||
      this.schema.title ||
      (this.isPrimitive && (
        this.schema.minimum ||
        this.schema.maximum ||
        this.schema.exclusiveMinimum ||
        this.schema.exclusiveMaximum ||
        this.schema.format ||
        this.schema.default ||
        this.schema.minLength ||
        this.schema.maxLength ||
        this.schema.enum)
      );

    // populate isRequired property down to properties
    if (this.schema && Array.isArray(this.schema.required)) {
      this.schema.required.forEach(requiredProperty => {
        if (typeof this.schema.properties[requiredProperty] === 'object') {
          this.schema.properties[requiredProperty].isRequired = true;
        }
      });
    }
  }

  /*
   * Returns the template with populated properties.
   * This template does not have the children
  */
  template() {
    if (!this.schema) {
      return '';
    }

    return `

      ${_if(this.isAny)`

          ${_if(this.showToggle)`
            ${this.schema.title || ''}
          `}

          <any>

          ${_if(this.schema.description && !this.isCollapsed)`

${this.schema.description}

          `}

      `}

      ${_if(this.isPrimitive)`

          ${_if(this.showToggle)`
            ${this.schema.title || ''}
          `}

            ${this.schema.type}

          ${_if(this.schema.isRequired)`
            *
          `}

          ${_if(!this.isCollapsed && this.schema.format)`
            (${this.schema.format})
          `}

          ${_if(!this.isCollapsed && this.schema.default)`
            default: ${this.schema.default}
          `}

          ${_if(!this.isCollapsed && this.schema.minimum)`
            minimum:${this.schema.minimum}
          `}

          ${_if(!this.isCollapsed && this.schema.exclusiveMinimum)`
            (ex)minimum:${this.schema.exclusiveMinimum}
          `}

          ${_if(!this.isCollapsed && this.schema.maximum)`
            maximum:${this.schema.maximum}
          `}

          ${_if(!this.isCollapsed && this.schema.exclusiveMaximum)`
            (ex)maximum:${this.schema.exclusiveMaximum}
          `}

          ${_if(!this.isCollapsed && this.schema.minLength)`
            minLength:${this.schema.minLength}
          `}

          ${_if(!this.isCollapsed && this.schema.maxLength)`
            maxLength:${this.schema.maxLength}
          `}

          ${_if(this.schema.description && !this.isCollapsed)`

${this.schema.description}

          `}

          ${_if(!this.isCollapsed && this.schema.enum)`
            ${this.enum(this.schema, this.isCollapsed, this.open)}
          `}

          ${_if(this.schema.allOf && !this.isCollapsed)`${this.xOf(this.schema, 'allOf')}`}
          ${_if(this.schema.oneOf && !this.isCollapsed)`${this.xOf(this.schema, 'oneOf')}`}
          ${_if(this.schema.anyOf && !this.isCollapsed)`${this.xOf(this.schema, 'anyOf')}`}

      `}

      ${_if(this.isArray)`

          ${this.schema.title || ''}[${_if(this.isCollapsed)`<span class="closing bracket">]</span>`}
          ${_if(!this.isCollapsed && (this.schema.uniqueItems || this.schema.minItems || this.schema.maxItems))`

            (${this.schema.minItems || 0}..${this.schema.maxItems || '∞'})
            ${_if(!this.isCollapsed && this.schema.uniqueItems)`<span title="unique" class="uniqueItems">♦</span>`}

          `}

            ${_if(!this.isCollapsed && this.schema.description)`

${this.schema.description}

            `}

          ${_if(!this.isCollapsed && this.schema.enum)`
            ${this.enum(this.schema, this.isCollapsed, this.open)}
          `}

          ${_if(this.schema.allOf && !this.isCollapsed)`${this.xOf(this.schema, 'allOf')}`}
          ${_if(this.schema.oneOf && !this.isCollapsed)`${this.xOf(this.schema, 'oneOf')}`}
          ${_if(this.schema.anyOf && !this.isCollapsed)`${this.xOf(this.schema, 'anyOf')}`}

          ${_if(!this.isCollapsed)`
          ]
          `}

      `}

      ${_if(!this.isPrimitive && !this.isArray && !this.isAny)`

          ${this.schema.title || ''} {${_if(this.isCollapsed)`
              }
          `}

            ${_if(!this.isCollapsed && this.schema.description)`

${this.schema.description}

            `}

          ${_if(!this.isCollapsed && this.schema.enum)`
            ${this.enum(this.schema, this.isCollapsed, this.open)}
          `}

          ${_if(this.schema.allOf && !this.isCollapsed)`${this.xOf(this.schema, 'allOf')}`}
          ${_if(this.schema.oneOf && !this.isCollapsed)`${this.xOf(this.schema, 'oneOf')}`}
          ${_if(this.schema.anyOf && !this.isCollapsed)`${this.xOf(this.schema, 'anyOf')}`}

          ${_if(!this.isCollapsed)`
          }
          `}

      `}
`.replace(/\s*\n/g, '\n').replace(/(
      ${_if(this.isAny)`

          ${_if(this.showToggle)`
            ${this.schema.title || ''}
          `}

          <any>

          ${_if(this.schema.description && !this.isCollapsed)`

${this.schema.description}

          `}

      `}

      ${_if(this.isPrimitive)`

          ${_if(this.showToggle)`
            ${this.schema.title || ''}
          `}

            ${this.schema.type}

          ${_if(this.schema.isRequired)`
            *
          `}

          ${_if(!this.isCollapsed && this.schema.format)`
            (${this.schema.format})
          `}

          ${_if(!this.isCollapsed && this.schema.default)`
            default: ${this.schema.default}
          `}

          ${_if(!this.isCollapsed && this.schema.minimum)`
            minimum:${this.schema.minimum}
          `}

          ${_if(!this.isCollapsed && this.schema.exclusiveMinimum)`
            (ex)minimum:${this.schema.exclusiveMinimum}
          `}

          ${_if(!this.isCollapsed && this.schema.maximum)`
            maximum:${this.schema.maximum}
          `}

          ${_if(!this.isCollapsed && this.schema.exclusiveMaximum)`
            (ex)maximum:${this.schema.exclusiveMaximum}
          `}

          ${_if(!this.isCollapsed && this.schema.minLength)`
            minLength:${this.schema.minLength}
          `}

          ${_if(!this.isCollapsed && this.schema.maxLength)`
            maxLength:${this.schema.maxLength}
          `}

          ${_if(this.schema.description && !this.isCollapsed)`

${this.schema.description}

          `}

          ${_if(!this.isCollapsed && this.schema.enum)`
            ${this.enum(this.schema, this.isCollapsed, this.open)}
          `}

          ${_if(this.schema.allOf && !this.isCollapsed)`${this.xOf(this.schema, 'allOf')}`}
          ${_if(this.schema.oneOf && !this.isCollapsed)`${this.xOf(this.schema, 'oneOf')}`}
          ${_if(this.schema.anyOf && !this.isCollapsed)`${this.xOf(this.schema, 'anyOf')}`}

      `}

      ${_if(this.isArray)`

          ${this.schema.title || ''}[${_if(this.isCollapsed)`<span class="closing bracket">]</span>`}
          ${_if(!this.isCollapsed && (this.schema.uniqueItems || this.schema.minItems || this.schema.maxItems))`

            (${this.schema.minItems || 0}..${this.schema.maxItems || '∞'})
            ${_if(!this.isCollapsed && this.schema.uniqueItems)`<span title="unique" class="uniqueItems">♦</span>`}

          `}

            ${_if(!this.isCollapsed && this.schema.description)`

${this.schema.description}

            `}

          ${_if(!this.isCollapsed && this.schema.enum)`
            ${this.enum(this.schema, this.isCollapsed, this.open)}
          `}

          ${_if(this.schema.allOf && !this.isCollapsed)`${this.xOf(this.schema, 'allOf')}`}
          ${_if(this.schema.oneOf && !this.isCollapsed)`${this.xOf(this.schema, 'oneOf')}`}
          ${_if(this.schema.anyOf && !this.isCollapsed)`${this.xOf(this.schema, 'anyOf')}`}

          ${_if(!this.isCollapsed)`
          ]
          `}

      `}

      ${_if(!this.isPrimitive && !this.isArray && !this.isAny)`

          ${this.schema.title || ''} {${_if(this.isCollapsed)`
              }
          `}

            ${_if(!this.isCollapsed && this.schema.description)`

${this.schema.description}

            `}

          ${_if(!this.isCollapsed && this.schema.enum)`
            ${this.enum(this.schema, this.isCollapsed, this.open)}
          `}

          ${_if(this.schema.allOf && !this.isCollapsed)`${this.xOf(this.schema, 'allOf')}`}
          ${_if(this.schema.oneOf && !this.isCollapsed)`${this.xOf(this.schema, 'oneOf')}`}
          ${_if(this.schema.anyOf && !this.isCollapsed)`${this.xOf(this.schema, 'anyOf')}`}

          ${_if(!this.isCollapsed)`
          }
          `}

      `}
`.replace(/\s*\n/g, '\n').replace(/(