<commit_before>/*
*********************************************************************************************

  System Loader Implementation

    - Implemented to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js

    - <script type="module"> supported

*********************************************************************************************
*/

(function (global) {
  var isBrowser = typeof window != 'undefined';
  var Loader = global.Reflect && global.Reflect.Loader || require('./loader');
  var Promise = global.Promise || require('es6-promise').Promise;

  // Helpers
  // Absolute URL parsing, from https://gist.github.com/Yaffle/1088850
  function parseURI(url) {
    var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
    // authority = '//' + user + ':' + pass '@' + hostname + ':' port
    return (m ? {
      href     : m[0] || '',
      protocol : m[1] || '',
      authority: m[2] || '',
      host     : m[3] || '',
      hostname : m[4] || '',
      port     : m[5] || '',
      pathname : m[6] || '',
      search   : m[7] || '',
      hash     : m[8] || ''
    } : null);
  }
  function toAbsoluteURL(base, href) {
    function removeDotSegments(input) {
      var output = [];
      input.replace(/^(\.\.?(\/|$))+/, '')
        .replace(/\/(\.(\/|$))+/g, '/')
        .replace(/\/\.\.$/, '/../')
        .replace(/\/?[^\/]*/g, function (p) {
          if (p === '/..')
            output.pop();
          else
            output.push(p);
      });
      return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
    }

    href = parseURI(href || '');
    base = parseURI(base || '');

    return !href || !base ? null : (href.protocol || base.protocol) +
      (href.protocol || href.authority ? href.authority : base.authority) +
      removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
      (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
      href.hash;
  }

  var fetchTextFromURL;
  if (isBrowser) {
    fetchTextFromURL = function(url, fulfill, reject) {
      var xhr = new XMLHttpRequest();
      var sameDomain = true;
      if (!('withCredentials' in xhr)) {
        // check if same domain
        var domainCheck = /^(\w+:)?\/\/([^\/]+)/.exec(url);
        if (domainCheck) {
          sameDomain = domainCheck[2] === window.location.host;
          if (domainCheck[1])
            sameDomain &= domainCheck[1] === window.location.protocol;
        }
      }
      if (!sameDomain) {
        xhr = new XDomainRequest();
        xhr.onload = load;
        xhr.onerror = error;
        xhr.ontimeout = error;
      }
      function load() {
        fulfill(xhr.responseText);
      }
      function error() {
        reject(xhr.statusText + ': ' + url || 'XHR error');
      }

      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status === 200 || (xhr.status == 0 && xhr.responseText)) {
            load();
          } else {
            error();
          }
        }
      };
      xhr.open("GET", url, true);
      xhr.send(null);
    }
  }
  else {
    var fs = require('fs');
    fetchTextFromURL = function(url, fulfill, reject) {
      return fs.readFile(url, function(err, data) {
        if (err)
          return reject(err);
        else
          fulfill(data + '');
      });
    }
  }

  var System = new Loader({
    global: isBrowser ? window : global,
    strict: true,
    normalize: function(name, parentName, parentAddress) {
      if (typeof name != 'string')
        throw new TypeError('Module name must be a string');

      var segments = name.split('/');

      if (segments.length == 0)
        throw new TypeError('No module name provided');

      // current segment
      var i = 0;
      // is the module name relative
      var rel = false;
      // number of backtracking segments
      var dotdots = 0;
      if (segments[0] == '.') {
        i++;
        if (i == segments.length)
          throw new TypeError('Illegal module name "' + name + '"');
        rel = true;
      }
      else {
        while (segments[i] == '..') {
          i++;
          if (i == segments.length)
            throw new TypeError('Illegal module name "' + name + '"');
        }
        if (i)
          rel = true;
        dotdots = i;
      }

      for (var j = i; j < segments.length; j++) {
        var segment = segments[j];
        if (segment == '' || segment == '.' || segment == '..')
          throw new TypeError('Illegal module name "' + name + '"');
      }

      if (!rel)
        return name;

      // build the full module name
      var normalizedParts = [];
      var parentParts = (parentName || '').split('/');
      var normalizedLen = parentParts.length - 1 - dotdots;

      normalizedParts = normalizedParts.concat(parentParts.splice(0, parentParts.length - 1 - dotdots));
      normalizedParts = normalizedParts.concat(segments.splice(i, segments.length - i));

      return normalizedParts.join('/');
    },
    locate: function(load) {
      var name = load.name;

      // NB no specification provided for System.paths, used ideas discussed in https://github.com/jorendorff/js-loaders/issues/25

      // most specific (longest) match wins
      var pathMatch = '', wildcard;

      // check to see if we have a paths entry
      for (var p in this.paths) {
        var pathParts = p.split('*');
        if (pathParts.length > 2)
          throw new TypeError('Only one wildcard in a path is permitted');

        // exact path match
        if (pathParts.length == 1) {
          if (name == p && p.length > pathMatch.length)
            pathMatch = p;
        }

        // wildcard path match
        else {
          if (name.substr(0, pathParts[0].length) == pathParts[0] && name.substr(name.length - pathParts[1].length) == pathParts[1]) {
            pathMatch = p;
            wildcard = name.substr(pathParts[0].length, name.length - pathParts[1].length - pathParts[0].length);
          }
        }
      }

      var outPath = this.paths[pathMatch];
      if (wildcard)
        outPath = outPath.replace('*', wildcard);

      return toAbsoluteURL(this.baseURL, outPath);
    },
    fetch: function(load) {
      return new Promise(function(resolve, reject) {
        fetchTextFromURL(toAbsoluteURL(this.baseURL, load.address), function(source) {
          resolve(source);
        }, reject);
      });
    }
  });

  if (isBrowser) {
    var href = window.location.href.split('#')[0].split('?')[0];
    System.baseURL = href.substring(0, href.lastIndexOf('/') + 1);
  }
  else {
    System.baseURL = './';
  }
  System.paths = { '*': '*.js' };

  if (global.System && global.traceur)
    global.traceurSystem = global.System;

  if (isBrowser)
    global.System = System;

  // <script type="module"> support
  // allow a data-init function callback once loaded
  if (isBrowser) {
    var curScript = document.getElementsByTagName('script');
    curScript = curScript[curScript.length - 1];

    function completed() {
      document.removeEventListener( "DOMContentLoaded", completed, false );
      window.removeEventListener( "load", completed, false );
      ready();
    }

    function ready() {
      var scripts = document.getElementsByTagName('script');

      for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        if (script.type == 'module') {
          var source = script.innerHTML;
          System.module(source)['catch'](function(err) { setTimeout(function() { throw err; }); });
        }
      }
    }

    // DOM ready, taken from https://github.com/jquery/jquery/blob/master/src/core/ready.js#L63
    if (document.readyState === 'complete') {
      setTimeout(ready);
    }
    else if (document.addEventListener) {
      document.addEventListener('DOMContentLoaded', completed, false);
      window.addEventListener('load', completed, false);
    }

    // run the data-init function on the script tag
    if (curScript.getAttribute('data-init'))
      window[curScript.getAttribute('data-init')]();
  }

  if (typeof exports === 'object')
    module.exports = System;

})(typeof global !== 'undefined' ? global : this);
<commit_msg>Delay the call to require() in system.js until needed<commit_after>/*
*********************************************************************************************

  System Loader Implementation

    - Implemented to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js

    - <script type="module"> supported

*********************************************************************************************
*/

(function (global) {
  var isBrowser = typeof window != 'undefined';
  var Loader = global.Reflect && global.Reflect.Loader || require('./loader');
  var Promise = global.Promise || require('es6-promise').Promise;

  // Helpers
  // Absolute URL parsing, from https://gist.github.com/Yaffle/1088850
  function parseURI(url) {
    var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
    // authority = '//' + user + ':' + pass '@' + hostname + ':' port
    return (m ? {
      href     : m[0] || '',
      protocol : m[1] || '',
      authority: m[2] || '',
      host     : m[3] || '',
      hostname : m[4] || '',
      port     : m[5] || '',
      pathname : m[6] || '',
      search   : m[7] || '',
      hash     : m[8] || ''
    } : null);
  }
  function toAbsoluteURL(base, href) {
    function removeDotSegments(input) {
      var output = [];
      input.replace(/^(\.\.?(\/|$))+/, '')
        .replace(/\/(\.(\/|$))+/g, '/')
        .replace(/\/\.\.$/, '/../')
        .replace(/\/?[^\/]*/g, function (p) {
          if (p === '/..')
            output.pop();
          else
            output.push(p);
      });
      return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
    }

    href = parseURI(href || '');
    base = parseURI(base || '');

    return !href || !base ? null : (href.protocol || base.protocol) +
      (href.protocol || href.authority ? href.authority : base.authority) +
      removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
      (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
      href.hash;
  }

  var fetchTextFromURL;
  if (isBrowser) {
    fetchTextFromURL = function(url, fulfill, reject) {
      var xhr = new XMLHttpRequest();
      var sameDomain = true;
      if (!('withCredentials' in xhr)) {
        // check if same domain
        var domainCheck = /^(\w+:)?\/\/([^\/]+)/.exec(url);
        if (domainCheck) {
          sameDomain = domainCheck[2] === window.location.host;
          if (domainCheck[1])
            sameDomain &= domainCheck[1] === window.location.protocol;
        }
      }
      if (!sameDomain) {
        xhr = new XDomainRequest();
        xhr.onload = load;
        xhr.onerror = error;
        xhr.ontimeout = error;
      }
      function load() {
        fulfill(xhr.responseText);
      }
      function error() {
        reject(xhr.statusText + ': ' + url || 'XHR error');
      }

      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status === 200 || (xhr.status == 0 && xhr.responseText)) {
            load();
          } else {
            error();
          }
        }
      };
      xhr.open("GET", url, true);
      xhr.send(null);
    }
  }
  else {
    var fs;
    fetchTextFromURL = function(url, fulfill, reject) {
      fs = fs || require('fs');
      return fs.readFile(url, function(err, data) {
        if (err)
          return reject(err);
        else
          fulfill(data + '');
      });
    }
  }

  var System = new Loader({
    global: isBrowser ? window : global,
    strict: true,
    normalize: function(name, parentName, parentAddress) {
      if (typeof name != 'string')
        throw new TypeError('Module name must be a string');

      var segments = name.split('/');

      if (segments.length == 0)
        throw new TypeError('No module name provided');

      // current segment
      var i = 0;
      // is the module name relative
      var rel = false;
      // number of backtracking segments
      var dotdots = 0;
      if (segments[0] == '.') {
        i++;
        if (i == segments.length)
          throw new TypeError('Illegal module name "' + name + '"');
        rel = true;
      }
      else {
        while (segments[i] == '..') {
          i++;
          if (i == segments.length)
            throw new TypeError('Illegal module name "' + name + '"');
        }
        if (i)
          rel = true;
        dotdots = i;
      }

      for (var j = i; j < segments.length; j++) {
        var segment = segments[j];
        if (segment == '' || segment == '.' || segment == '..')
          throw new TypeError('Illegal module name "' + name + '"');
      }

      if (!rel)
        return name;

      // build the full module name
      var normalizedParts = [];
      var parentParts = (parentName || '').split('/');
      var normalizedLen = parentParts.length - 1 - dotdots;

      normalizedParts = normalizedParts.concat(parentParts.splice(0, parentParts.length - 1 - dotdots));
      normalizedParts = normalizedParts.concat(segments.splice(i, segments.length - i));

      return normalizedParts.join('/');
    },
    locate: function(load) {
      var name = load.name;

      // NB no specification provided for System.paths, used ideas discussed in https://github.com/jorendorff/js-loaders/issues/25

      // most specific (longest) match wins
      var pathMatch = '', wildcard;

      // check to see if we have a paths entry
      for (var p in this.paths) {
        var pathParts = p.split('*');
        if (pathParts.length > 2)
          throw new TypeError('Only one wildcard in a path is permitted');

        // exact path match
        if (pathParts.length == 1) {
          if (name == p && p.length > pathMatch.length)
            pathMatch = p;
        }

        // wildcard path match
        else {
          if (name.substr(0, pathParts[0].length) == pathParts[0] && name.substr(name.length - pathParts[1].length) == pathParts[1]) {
            pathMatch = p;
            wildcard = name.substr(pathParts[0].length, name.length - pathParts[1].length - pathParts[0].length);
          }
        }
      }

      var outPath = this.paths[pathMatch];
      if (wildcard)
        outPath = outPath.replace('*', wildcard);

      return toAbsoluteURL(this.baseURL, outPath);
    },
    fetch: function(load) {
      return new Promise(function(resolve, reject) {
        fetchTextFromURL(toAbsoluteURL(this.baseURL, load.address), function(source) {
          resolve(source);
        }, reject);
      });
    }
  });

  if (isBrowser) {
    var href = window.location.href.split('#')[0].split('?')[0];
    System.baseURL = href.substring(0, href.lastIndexOf('/') + 1);
  }
  else {
    System.baseURL = './';
  }
  System.paths = { '*': '*.js' };

  if (global.System && global.traceur)
    global.traceurSystem = global.System;

  if (isBrowser)
    global.System = System;

  // <script type="module"> support
  // allow a data-init function callback once loaded
  if (isBrowser) {
    var curScript = document.getElementsByTagName('script');
    curScript = curScript[curScript.length - 1];

    function completed() {
      document.removeEventListener( "DOMContentLoaded", completed, false );
      window.removeEventListener( "load", completed, false );
      ready();
    }

    function ready() {
      var scripts = document.getElementsByTagName('script');

      for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        if (script.type == 'module') {
          var source = script.innerHTML;
          System.module(source)['catch'](function(err) { setTimeout(function() { throw err; }); });
        }
      }
    }

    // DOM ready, taken from https://github.com/jquery/jquery/blob/master/src/core/ready.js#L63
    if (document.readyState === 'complete') {
      setTimeout(ready);
    }
    else if (document.addEventListener) {
      document.addEventListener('DOMContentLoaded', completed, false);
      window.addEventListener('load', completed, false);
    }

    // run the data-init function on the script tag
    if (curScript.getAttribute('data-init'))
      window[curScript.getAttribute('data-init')]();
  }

  if (typeof exports === 'object')
    module.exports = System;

})(typeof global !== 'undefined' ? global : this);
<|endoftext|>


<commit_before>Package.describe({summary: "Telescope API package"});

Package.onUse(function (api) {

  api.use(['telescope-lib', 'telescope-base'], ['server']);

  api.add_files(['lib/server/api.js',
                 'lib/server/routes.js',
                 'lib/server/categories.js',
                 'lib/server/posts.js',
                 'lib/server/comments.js',
                 'lib/server/user.js',
                 'lib/server/search.js',
                 'lib/server/avatar.js',
                 'lib/server/checkUsername.js',
                 'lib/components/scoring.js',
                 'lib/components/vote.js' ,
                 'lib/server/showoff.js',
                 'lib/server/showoffComment.js'], ['server']);

  api.export(['serveAPI']);

});
<commit_msg>fix bug<commit_after>Package.describe({summary: "Telescope API package"});

Package.onUse(function (api) {

  api.use(['telescope-lib', 'telescope-base'], ['server']);

  api.add_files(['lib/server/api.js',
                 'lib/server/routes.js',
                 'lib/server/categories.js',
                 'lib/server/posts.js',
                 'lib/server/comments.js',
                 'lib/server/commentPost.js',
                 'lib/server/user.js',
                 'lib/server/search.js',
                 'lib/server/avatar.js',
                 'lib/server/checkUsername.js',
                 'lib/components/scoring.js',
                 'lib/components/vote.js' ,
                 'lib/server/showoff.js',
                 'lib/server/showoffComment.js'], ['server']);

  api.export(['serveAPI']);

});
<|endoftext|>


<commit_before>
/**
 * reddit.com.js
 *
 * README.md
 * > Automatically collapse children comments. Press '+' to expand.
 * > Automatically expand and load imgur links.
 *
 * deps:
 *  jQuery 1.9
 */

/**
 * Hide children comments.
 */

function hideChildren() {
  $('.commentarea .comment .child').hide();
}

/**
 * Add '+' icon and bind event listener.
 */

function addExpandButton() {
  $('.tagline').append('[+]');
  $('.tagline .expand-children')
    .css('cursor', 'pointer')
    .click(function() {
    toggleExpandButton(this);
    $('.commentarea .comment .child').toggle();
  });
}

/**
 * Toggle expand button from + to -.
 *
 * @param {Element} el
 */

function toggleExpandButton(el) {
  if (~$(el).text().indexOf('+'))
    $(el).text('[-]');
  else
    $(el).text('[+]');
}

/**
 * Load an image only if on comments page.
 */

function loadImage() {
  var thumbnail = $('a.thumbnail');
  if (thumbnail.length > 1) return;
  var href = thumbnail.attr('href');
  $('a.thumbnail img')
    .attr('src', href)
    .attr('width', '')
    .attr('height', '');
  thumbnail.removeClass('thumbnail');
}

/**
 * Start script.
 */

hideChildren();
addExpandButton();
loadImage();
<commit_msg>fixed children toggle<commit_after>
/**
 * reddit.com.js
 *
 * README.md
 * > Automatically collapse children comments. Press '+' to expand.
 * > Automatically expand and load imgur links.
 * > Format AMAs so that all answered questions are on top in decreasing votes.
 *
 * deps:
 *  jQuery 1.9
 */

/**
 * Hide children comments.
 */

function hideChildren() {
  $('.commentarea .comment .child').hide();
}

/**
 * Add '+' icon and bind event listener.
 */

function addExpandButton() {
  $('.tagline').append('[+]');
  $('.tagline .expand-children')
    .css('cursor', 'pointer')
    .click(function() {
    toggleExpandButton(this);
    $(this).closest('.comment').find('.child').toggle();
  });
}

/**
 * Toggle expand button from + to -.
 *
 * @param {Element} el
 */

function toggleExpandButton(el) {
  if (~$(el).text().indexOf('+'))
    $(el).text('[-]');
  else
    $(el).text('[+]');
}

/**
 * Load an image only if on comments page.
 */

function loadImage() {
  var thumbnail = $('a.thumbnail');
  if (thumbnail.length > 1) return;
  var href = thumbnail.attr('href');
  $('a.thumbnail img')
    .attr('src', href)
    .attr('width', '')
    .attr('height', '');
  thumbnail.removeClass('thumbnail');
}

/**
 * Start script.
 */

hideChildren();
addExpandButton();
loadImage();
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import DetailsViolationRow from './DetailsViolationRow';

class DetailsInspectionRow extends React.Component {
  constructor(props, context){
    super(props, context);
    this.state = {
      isOpen: false
    };
    this.clickHandler = this.clickHandler.bind(this);
  }

  clickHandler() {
    this.setState({isOpen:!this.state.isOpen});
  }

  render() {
    const {inspection, formatDate, inspectionIndex} = this.props;

    let opts = {
      dataToggle: null,
      dataTarget: null,
      showIcon: 'hidden',
      clickHandler: null
    };
    let violationRows = [];

    //TODO: create a better key and indexId
    if(inspection.violations.length > 0 && inspection.violations[0]) {
      opts.dataToggle = 'collapse';
      opts.dataTarget = '.'+inspectionIndex;
      opts.showIcon = 'show';
      //TODO: make sure the fix worked (see below)
      // Remove this to Fix bug with render, but need to figure out how to activate the plus/minus sign
      //opts.clickHandler = this.clickHandler();
      violationRows = inspection.violations.map((item) => {
        return (
          <DetailsViolationRow violation={item} index={inspectionIndex} key={item.violationRecordId} />
        );
      });
    }
    let inspectionRows = [];

    let inspectionType = null;
    switch(Number(inspection.inspectionType)) {
      case 128:
        inspectionType = "Routine Inspection"; //remove field review
        break;
      case 129:
        inspectionType = "Return Inspection"; // Keep as is
        break;
      case 136:
        inspectionType = "Consultation/Education"; //remove field
        break;
    }

    inspectionRows.push(

        {inspectionType}
        {formatDate(inspection.inspectionDate)}
        {inspection.inspectionScore}

    );
    inspectionRows.push(violationRows);

    return (

        {inspectionRows}

    );
  }
}
// const DetailsInspectionRow = ({inspection, formatDate, inspectionIndex, activeViolations, inspectionRowOnClick}) => {
//   let opts = {
//     dataToggle: null,
//     dataTarget: null,
//     showIcon: 'hidden',
//     clickHandler: null
//   };
//   let violationRows = [];

//   //TODO: create a better key and indexId
//   if(inspection.violations.length > 0) {
//     opts.dataToggle = 'collapse';
//     opts.dataTarget = '.'+inspectionIndex;
//     opts.showIcon = 'show';
//     // Remove this to Fix bug with render, but need to figure out how to activate the plus/minus sign
//     opts.clickHandler = () => inspectionRowOnClick(inspection.inspectionSerialNum);
//     violationRows = inspection.violations.map((item) => {
//       return (
//         <DetailsViolationRow violation={item} index={inspectionIndex} key={item.violationRecordId} />
//       );
//     });
//   }
//   let inspectionRows = [];

//   let inspectionType = null;
//   switch(Number(inspection.inspectionType)) {
//     case 128:
//       inspectionType = "Routine Inspection/Field Review"; //remove field review
//       break;
//     case 129:
//       inspectionType = "Return Inspection"; // Keep as is
//       break;
//     case 136:
//       inspectionType = "Consultation/Education - Field"; //remove field
//       break;
//   }

//   inspectionRows.push(
//

//       {inspectionType}
//       {formatDate(inspection.inspectionDate)}
//       {inspection.inspectionScore}<span className={(activeViolations.indexOf(inspection.inspectionSerialNum) < 0) ? 'fa fa-plus': 'fa fa-minus'}/>
//

//   );
//   inspectionRows.push(violationRows);

//   return (
//

//       {inspectionRows}
//

//   );
// };

DetailsInspectionRow.propTypes = {
  inspection: PropTypes.object.isRequired,
  formatDate: PropTypes.func.isRequired,
  inspectionIndex: PropTypes.number.isRequired
 // activeViolations: PropTypes.array
 // inspectionRowOnClick: PropTypes.func.isRequired
};

export default DetailsInspectionRow;
<commit_msg>Violation rows will filter by red and blue violations<commit_after>import React, {PropTypes} from 'react';
import DetailsViolationRow from './DetailsViolationRow';

class DetailsInspectionRow extends React.Component {
  constructor(props, context){
    super(props, context);
    this.state = {
      isOpen: false
    };
    this.clickHandler = this.clickHandler.bind(this);
  }

  clickHandler() {
    this.setState({isOpen:!this.state.isOpen});
  }

  render() {
    const {inspection, formatDate, inspectionIndex} = this.props;

    let opts = {
      dataToggle: null,
      dataTarget: null,
      showIcon: 'hidden',
      clickHandler: null
    };
    let violationRows = [];

    //TODO: create a better key and indexId
    if(inspection.violations.length > 0 && inspection.violations[0]) {
      opts.dataToggle = 'collapse';
      opts.dataTarget = `.${inspectionIndex}`;
      opts.showIcon = 'show';
      //TODO: make sure the fix worked (see below)
      // Remove this to Fix bug with render, but need to figure out how to activate the plus/minus sign
      //opts.clickHandler = this.clickHandler();
      violationRows = inspection.violations.map((item) => {
        return (
          <DetailsViolationRow violation={item} index={inspectionIndex} key={item.violationRecordId} />
        );
      });
      violationRows.sort((a,b)=>{
        if(a.props.violation.violationType.toLowerCase() === 'red' ) {
          return -1
        }
        else {
          return 1;
        }
      })
    }
    let inspectionRows = [];

    let inspectionType = null;
    switch(Number(inspection.inspectionType)) {
      case 128:
        inspectionType = "Routine Inspection"; //remove field review
        break;
      case 129:
        inspectionType = "Return Inspection"; // Keep as is
        break;
      case 136:
        inspectionType = "Consultation/Education"; //remove field
        break;
    }

    inspectionRows.push(

        {inspectionType}
        {formatDate(inspection.inspectionDate)}
        {inspection.inspectionScore}

    );
    inspectionRows.push(violationRows);

    return (

        {inspectionRows}

    );
  }
}
// const DetailsInspectionRow = ({inspection, formatDate, inspectionIndex, activeViolations, inspectionRowOnClick}) => {
//   let opts = {
//     dataToggle: null,
//     dataTarget: null,
//     showIcon: 'hidden',
//     clickHandler: null
//   };
//   let violationRows = [];

//   //TODO: create a better key and indexId
//   if(inspection.violations.length > 0) {
//     opts.dataToggle = 'collapse';
//     opts.dataTarget = '.'+inspectionIndex;
//     opts.showIcon = 'show';
//     // Remove this to Fix bug with render, but need to figure out how to activate the plus/minus sign
//     opts.clickHandler = () => inspectionRowOnClick(inspection.inspectionSerialNum);
//     violationRows = inspection.violations.map((item) => {
//       return (
//         <DetailsViolationRow violation={item} index={inspectionIndex} key={item.violationRecordId} />
//       );
//     });
//   }
//   let inspectionRows = [];

//   let inspectionType = null;
//   switch(Number(inspection.inspectionType)) {
//     case 128:
//       inspectionType = "Routine Inspection/Field Review"; //remove field review
//       break;
//     case 129:
//       inspectionType = "Return Inspection"; // Keep as is
//       break;
//     case 136:
//       inspectionType = "Consultation/Education - Field"; //remove field
//       break;
//   }

//   inspectionRows.push(
//

//       {inspectionType}
//       {formatDate(inspection.inspectionDate)}
//       {inspection.inspectionScore}<span className={(activeViolations.indexOf(inspection.inspectionSerialNum) < 0) ? 'fa fa-plus': 'fa fa-minus'}/>
//

//   );
//   inspectionRows.push(violationRows);

//   return (
//

//       {inspectionRows}
//

//   );
// };

DetailsInspectionRow.propTypes = {
  inspection: PropTypes.object.isRequired,
  formatDate: PropTypes.func.isRequired,
  inspectionIndex: PropTypes.number.isRequired
 // activeViolations: PropTypes.array
 // inspectionRowOnClick: PropTypes.func.isRequired
};

export default DetailsInspectionRow;
<|endoftext|>


<commit_before>/*

Modified react-frame-component@0.4.0 which supports an onRender callback (e.g. to measure contents);
Original https://github.com/ryanseddon/react-frame-component/

*/

import React, {Component} from 'react';
import {unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer, unmountComponentAtNode} from 'react-dom'; // eslint-disable-line camelcase
import PropTypes from 'prop-types';
import raf from 'raf';

const hasConsole = typeof window !== 'undefined' && window.console;
const noop = () => {};
let swallowInvalidHeadWarning = noop;
let resetWarnings = noop;

if (hasConsole) {
  const originalError = console.error; // eslint-disable-line no-console
  // Rendering a  into a body is technically invalid although it
  // works. We swallow React's validateDOMNesting warning if that is the
  // message to avoid confusion
  swallowInvalidHeadWarning = () => {
    console.error = (msg) => { // eslint-disable-line no-console
      if (//.test(msg)) return;
      originalError.call(console, msg);
    };
  };
  resetWarnings = () => {
    console.error = originalError; // eslint-disable-line no-console
  };
}

class FrameComponent extends Component {
  constructor() {
    super();
    this.renderFrameContents = this.renderFrameContents.bind(this);
  }

  componentDidMount() {
    this.renderFrameContents();
  }

  componentDidUpdate() {
    this.renderFrameContents();
  }

  componentWillUnmount() {
    const doc = this.iframe.contentDocument;
    if (doc) {
      unmountComponentAtNode(doc.body);
    }
  }

  renderFrameContents() {
    const doc = this.iframe.contentDocument;

    if (doc && doc.readyState === 'complete') {
      const contents = (

          {this.props.head}
          {this.props.children}

      );

      // React warns when you render directly into the body since browser
      // extensions also inject into the body and can mess up React.
      doc.body.innerHTML = '

';
      doc.head.innerHTML = '';

      const base = doc.createElement('base');
      base.setAttribute('href', window.location.href);
      doc.head.appendChild(base);

      // Clone styles from parent document head into the iframe, so components which use webpack's style-loader get rendered correctly.
      // This doesn't clone any Catalog styles because they are either inline styles or part of the body.
      const pageStyles = Array.from(document.querySelectorAll('head > style'));
      pageStyles.forEach((s) => {
        doc.head.appendChild(s.cloneNode(true));
      });

      swallowInvalidHeadWarning();
      renderSubtreeIntoContainer(this, contents, doc.body.firstChild, () => {
        if (this.props.onRender) {
          raf(() => {
            this.props.onRender(doc.body.firstChild);
          });
        }
      });
      resetWarnings();
    } else {
      setTimeout(this.renderFrameContents, 0);
    }
  }

  render() {
    const {style, frameBorder, allowTransparency, scrolling} = this.props;
    return (
       { this.iframe = el; }}
        style={style}
        frameBorder={frameBorder}
        allowTransparency={allowTransparency}
        scrolling={scrolling} />
    );
  }
}

FrameComponent.propTypes = {
  style: PropTypes.object,
  head: PropTypes.node,
  onRender: PropTypes.func,
  children: PropTypes.node,

  // These props are passed through to the underlying
  frameBorder: PropTypes.string,
  allowTransparency: PropTypes.string,
  scrolling: PropTypes.string
};

export default FrameComponent;
<commit_msg>Clone stylesheet references from parent page into iframes (#319)<commit_after>/*

Modified react-frame-component@0.4.0 which supports an onRender callback (e.g. to measure contents);
Original https://github.com/ryanseddon/react-frame-component/

*/

import React, {Component} from 'react';
import {unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer, unmountComponentAtNode} from 'react-dom'; // eslint-disable-line camelcase
import PropTypes from 'prop-types';
import raf from 'raf';

const hasConsole = typeof window !== 'undefined' && window.console;
const noop = () => {};
let swallowInvalidHeadWarning = noop;
let resetWarnings = noop;

if (hasConsole) {
  const originalError = console.error; // eslint-disable-line no-console
  // Rendering a  into a body is technically invalid although it
  // works. We swallow React's validateDOMNesting warning if that is the
  // message to avoid confusion
  swallowInvalidHeadWarning = () => {
    console.error = (msg) => { // eslint-disable-line no-console
      if (//.test(msg)) return;
      originalError.call(console, msg);
    };
  };
  resetWarnings = () => {
    console.error = originalError; // eslint-disable-line no-console
  };
}

class FrameComponent extends Component {
  constructor() {
    super();
    this.renderFrameContents = this.renderFrameContents.bind(this);
  }

  componentDidMount() {
    this.renderFrameContents();
  }

  componentDidUpdate() {
    this.renderFrameContents();
  }

  componentWillUnmount() {
    const doc = this.iframe.contentDocument;
    if (doc) {
      unmountComponentAtNode(doc.body);
    }
  }

  renderFrameContents() {
    const doc = this.iframe.contentDocument;

    if (doc && doc.readyState === 'complete') {
      const contents = (

          {this.props.head}
          {this.props.children}

      );

      // React warns when you render directly into the body since browser
      // extensions also inject into the body and can mess up React.
      doc.body.innerHTML = '

';
      doc.head.innerHTML = '';

      const base = doc.createElement('base');
      base.setAttribute('href', window.location.href);
      doc.head.appendChild(base);

      // Clone styles from parent document head into the iframe, so components which use webpack's style-loader get rendered correctly.
      // This doesn't clone any Catalog styles because they are either inline styles or part of the body.
      const pageStyles = Array.from(document.querySelectorAll('head > style, head > link[rel="stylesheet"]'));
      pageStyles.forEach((s) => {
        doc.head.appendChild(s.cloneNode(true));
      });

      swallowInvalidHeadWarning();
      renderSubtreeIntoContainer(this, contents, doc.body.firstChild, () => {
        if (this.props.onRender) {
          raf(() => {
            this.props.onRender(doc.body.firstChild);
          });
        }
      });
      resetWarnings();
    } else {
      setTimeout(this.renderFrameContents, 0);
    }
  }

  render() {
    const {style, frameBorder, allowTransparency, scrolling} = this.props;
    return (
       { this.iframe = el; }}
        style={style}
        frameBorder={frameBorder}
        allowTransparency={allowTransparency}
        scrolling={scrolling} />
    );
  }
}

FrameComponent.propTypes = {
  style: PropTypes.object,
  head: PropTypes.node,
  onRender: PropTypes.func,
  children: PropTypes.node,

  // These props are passed through to the underlying
  frameBorder: PropTypes.string,
  allowTransparency: PropTypes.string,
  scrolling: PropTypes.string
};

export default FrameComponent;
<|endoftext|>


<commit_before>import React from "react";

const GameStats = (props) => {

  let gameStatsStyle = {
        fontSize: "1.5rem",
        margin: "1rem"
      },
      paragraphStyle = {
        display: "flex",
        justifyContent: "space-between",
        margin: "0.4rem 0rem"
      };

  return (

        Wins

        X

          {props.victoryStatistics.x}

        O

          {props.victoryStatistics.o}

  );
};

GameStats.propTypes = {
  victoryStatistics: React.PropTypes.object
};

export default GameStats;
<commit_msg>Refactor: Add props destructuring<commit_after>import React from "react";

const GameStats = ({victoryStatistics:{x,o}}) => {

  let gameStatsStyle = {
        fontSize: "1.5rem",
        margin: "1rem"
      },
      paragraphStyle = {
        display: "flex",
        justifyContent: "space-between",
        margin: "0.4rem 0rem"
      };

  return (

        Wins

        X

          {x}

        O

          {o}

  );
};

GameStats.propTypes = {
  victoryStatistics: React.PropTypes.object
};

export default GameStats;
<|endoftext|>


<commit_before>import React from 'react';
import settings from 'settings';
import Heading2 from 'components/Heading2';
import TextCenter from 'components/TextCenter';
import Grid from 'components/Grid';
import GridItem from 'components/GridItem';
import ExternalTeaser from 'components/ExternalTeaser';
import styles from './petition-footer.scss';

const PetitionFooter = ({ links }) => (

      <TextCenter>
        <Heading2 text={settings.petitionPage.attachedLinks} />
      </TextCenter>

    <Grid modifier='centered'>
      {links.map((link, index) => (
        <GridItem key={link.title}>
          <ExternalTeaser {...link} />
        </GridItem>
      ))}
    </Grid>

);

export default PetitionFooter;
<commit_msg>Fixed React similar key warning<commit_after>import React from 'react';
import settings from 'settings';
import Heading2 from 'components/Heading2';
import TextCenter from 'components/TextCenter';
import Grid from 'components/Grid';
import GridItem from 'components/GridItem';
import ExternalTeaser from 'components/ExternalTeaser';
import styles from './petition-footer.scss';

const PetitionFooter = ({ links }) => (

      <TextCenter>
        <Heading2 text={settings.petitionPage.attachedLinks} />
      </TextCenter>

    <Grid modifier='centered'>
      {links.map((link, index) => (
        <GridItem key={index}>
          <ExternalTeaser {...link} />
        </GridItem>
      ))}
    </Grid>

);

export default PetitionFooter;
<|endoftext|>


<commit_before>import VueMaterialRipple from '../../../util/ripple'

export default {
  data () {
    return {
      classes: {}
    }
  },
  props: {
    tag: {
      type: String,
      required: false,
      default: 'button'
    },
    dense: {
      type: Boolean,
      required: false,
      default: false
    },
    raised: {
      type: Boolean,
      required: false,
      default: false
    },
    compact: {
      type: Boolean,
      required: false,
      default: false
    },
    primary: {
      type: Boolean,
      required: false,
      default: false
    },
    accent: {
      type: Boolean,
      required: false,
      default: false
    },
    disabled: {
      type: Boolean,
      required: false,
      default: false
    },
    dark: {
      type: Boolean,
      required: false,
      default: false
    },
    to: {
      type: [String, Object], // "to" is not part of MDC spec, used for router-link type func
      required: false,
      default: ''
    },
    replace: {
      type: Boolean, // "replace" is not part of MDC spec, used for router-link type func
      required: false,
      default: false
    },
    append: {
      type: Boolean, // "append" is not part of MDC spec, used for router-link type func
      required: false,
      default: false
    }
  },
  mounted () {
    let vm = this

    vm.ripple = new VueMaterialRipple(vm, {
      addClass (className) {
        vm.$set(vm.classes, className, true)
      },
      removeClass (className) {
        vm.$delete(vm.classes, className)
      }
    })

    vm.ripple.init()
  },
  methods: {
    getAdditionalClasses () {
      // Useful for extending the list e.g., drawer nav
      return {}
    }
  },
  render (createElement) {
    let vm = this

    if (vm.$router) {
      // Handle router links
      var router = vm.$router
      var current = vm.$route
      var { location, href } = router.resolve(vm.to, current, vm.append)
    }

    let data = {
      class: {
        'mdc-button': true,
        'mdc-button--dense': vm.dense,
        'mdc-button--raised': vm.raised,
        'mdc-button--compact': vm.compact,
        'mdc-button--primary': vm.primary,
        'mdc-button--accent': vm.accent,
        'mdc-button--theme-dark': vm.dark,
        ...vm.classes,
        ...vm.getAdditionalClasses()
      },
      props: {
        disabled: vm.disabled
      },
      on: {
        ...vm.$listeners,
        click (e) {
          if (router && guardRouteClickEvent(e)) {
            if (vm.replace) {
              router.replace(location)
            } else {
              router.push(location)
            }
          }

          vm.$emit('click')
        }
      }
    }

    if (vm.tag === 'a' && href && href.length > 0) {
      // Set the href with the to route value
      data.attrs = {
        href
      }
    }

    return createElement(vm.tag, data, vm.$slots.default)
  }
}

// Lifted directly from https://github.com/vuejs/vue-router/blob/dev/src/components/link.js
function guardRouteClickEvent (e) {
  // don't redirect with control keys
  if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
  // don't redirect when preventDefault called
  if (e.defaultPrevented) return
  // don't redirect on right click
  if (e.button !== undefined && e.button !== 0) return
  // don't redirect if `target="_blank"`
  if (e.currentTarget && e.currentTarget.getAttribute) {
    const target = e.currentTarget.getAttribute('target')
    if (/\b_blank\b/i.test(target)) return
  }
  // this may be a Weex event which doesn't have this method
  if (e.preventDefault) {
    e.preventDefault()
  }
  return true
}
<commit_msg>Minor change to vue-router type handling on mdc-button<commit_after>import VueMaterialRipple from '../../../util/ripple'

export default {
  data () {
    return {
      classes: {}
    }
  },
  props: {
    tag: {
      type: String,
      required: false,
      default: 'button'
    },
    dense: {
      type: Boolean,
      required: false,
      default: false
    },
    raised: {
      type: Boolean,
      required: false,
      default: false
    },
    compact: {
      type: Boolean,
      required: false,
      default: false
    },
    primary: {
      type: Boolean,
      required: false,
      default: false
    },
    accent: {
      type: Boolean,
      required: false,
      default: false
    },
    disabled: {
      type: Boolean,
      required: false,
      default: false
    },
    dark: {
      type: Boolean,
      required: false,
      default: false
    },
    to: {
      type: [String, Object], // "to" is not part of MDC spec, used for router-link type func
      required: false,
      default: ''
    },
    replace: {
      type: Boolean, // "replace" is not part of MDC spec, used for router-link type func
      required: false,
      default: false
    },
    append: {
      type: Boolean, // "append" is not part of MDC spec, used for router-link type func
      required: false,
      default: false
    }
  },
  mounted () {
    let vm = this

    vm.ripple = new VueMaterialRipple(vm, {
      addClass (className) {
        vm.$set(vm.classes, className, true)
      },
      removeClass (className) {
        vm.$delete(vm.classes, className)
      }
    })

    vm.ripple.init()
  },
  methods: {
    getAdditionalClasses () {
      // Useful for extending the list e.g., drawer nav
      return {}
    }
  },
  render (createElement) {
    let vm = this

    if (vm.to !== '' && vm.$router) {
      // Handle router links
      var router = vm.$router
      var current = vm.$route
      var { location, href } = router.resolve(vm.to, current, vm.append)
    }

    let data = {
      class: {
        'mdc-button': true,
        'mdc-button--dense': vm.dense,
        'mdc-button--raised': vm.raised,
        'mdc-button--compact': vm.compact,
        'mdc-button--primary': vm.primary,
        'mdc-button--accent': vm.accent,
        'mdc-button--theme-dark': vm.dark,
        ...vm.classes,
        ...vm.getAdditionalClasses()
      },
      props: {
        disabled: vm.disabled
      },
      on: {
        ...vm.$listeners,
        click (e) {
          if (router && guardRouteClickEvent(e)) {
            if (vm.replace) {
              router.replace(location)
            } else {
              router.push(location)
            }
          }

          vm.$emit('click')
        }
      }
    }

    if (vm.tag === 'a' && href && href.length > 0) {
      // Set the href with the to route value
      data.attrs = {
        href
      }
    }

    return createElement(vm.tag, data, vm.$slots.default)
  }
}

// Lifted directly from https://github.com/vuejs/vue-router/blob/dev/src/components/link.js
function guardRouteClickEvent (e) {
  // don't redirect with control keys
  if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
  // don't redirect when preventDefault called
  if (e.defaultPrevented) return
  // don't redirect on right click
  if (e.button !== undefined && e.button !== 0) return
  // don't redirect if `target="_blank"`
  if (e.currentTarget && e.currentTarget.getAttribute) {
    const target = e.currentTarget.getAttribute('target')
    if (/\b_blank\b/i.test(target)) return
  }
  // this may be a Weex event which doesn't have this method
  if (e.preventDefault) {
    e.preventDefault()
  }
  return true
}
<|endoftext|>


<commit_before>import { ScreenComponent } from './../../lib/ScreenComponent';

class CreditsScreen extends ScreenComponent {

	static init () {
		if (Object.keys (this.engine.configuration ('credits')).length > 0) {
			this.engine.component ('main-menu').addButton ({
				string: 'Credits',
				data: {
					action: 'open-screen',
					open: 'credits'
				}
			});
		}
		return Promise.resolve ();
	}

	constructor () {
		super ();

		this.props = {
			credits: {}
		};
	}

	willMount () {
		super.willMount ();
		this.setProps ({
			credits: this.engine.configuration ('credits')
		});
		return Promise.resolve ();
	}

	render () {
		const items = Object.keys (this.props.credits).map ((section) => {
			let html = `<h3>${this.engine.replaceVariables (section)}</h3><div>`;
			const content = this.props.credits[section];

			if (typeof content === 'string') {
				return `

						${content}

`;
			}

			for (const key of Object.keys (content)) {
				const title = this.engine.replaceVariables (key);
				let value = content[key];

				if (value instanceof Array) {
					value = value.join (', ');
				}

				if (typeof value === 'string') {
					value = this.engine.replaceVariables (value);
				}

				if (title.indexOf ('_') === 0) {
					html += `

								${value}

`;
				} else {
					html += `

								${title}
								${value}

`;
				}

			}
			html += '
';

			return html;
		});

		return `
			<button class="fas fa-arrow-left top left" data-action="back"></button>

Credits

				${items}

		`;
	}
}

CreditsScreen.tag = 'credits-screen';

export default CreditsScreen;<commit_msg>Remove extra comma from credit screen section joins<commit_after>import { ScreenComponent } from './../../lib/ScreenComponent';

class CreditsScreen extends ScreenComponent {

	static init () {
		if (Object.keys (this.engine.configuration ('credits')).length > 0) {
			this.engine.component ('main-menu').addButton ({
				string: 'Credits',
				data: {
					action: 'open-screen',
					open: 'credits'
				}
			});
		}
		return Promise.resolve ();
	}

	constructor () {
		super ();

		this.props = {
			credits: {}
		};
	}

	willMount () {
		super.willMount ();
		this.setProps ({
			credits: this.engine.configuration ('credits')
		});
		return Promise.resolve ();
	}

	render () {
		const items = Object.keys (this.props.credits).map ((section) => {
			let html = `<h3>${this.engine.replaceVariables (section)}</h3><div>`;
			const content = this.props.credits[section];

			if (typeof content === 'string') {
				return `

						${content}

`;
			}

			for (const key of Object.keys (content)) {
				const title = this.engine.replaceVariables (key);
				let value = content[key];

				if (value instanceof Array) {
					value = value.join (', ');
				}

				if (typeof value === 'string') {
					value = this.engine.replaceVariables (value);
				}

				if (title.indexOf ('_') === 0) {
					html += `

								${value}

`;
				} else {
					html += `

								${title}
								${value}

`;
				}

			}
			html += '
';

			return html;
		}).join ('');

		return `
			<button class="fas fa-arrow-left top left" data-action="back"></button>

Credits

				${items}

		`;
	}
}

CreditsScreen.tag = 'credits-screen';

export default CreditsScreen;<|endoftext|>


<commit_before>'use strict'
import { Link } from 'react-router'
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import cx from 'classnames'
import leftNav from '../../../static/asset/disabled-left-arrow.svg'
import rightNav from '../../../static/asset/disabled-right-arrow.svg'
import styles from './Bookmarks.scss'

// lodash
import get from 'lodash/get'
import map from 'lodash/map'

const RIGHT = 'right'
const LEFT = 'left'
const DISTANCE_PER_SCROLL = 50

class Bookmark extends Component {
  constructor(props) {
    super(props)
  }

  componentDidMount() {
    const { changeSelectedBookmarkOffsetLeft, isSelected } = this.props
    let bookmarkNode = ReactDOM.findDOMNode(this.refs.bookmarkRef)
    if (isSelected) {
      changeSelectedBookmarkOffsetLeft(bookmarkNode.offsetLeft)
    }
  }

  render() {
    const { bookmark, isSelected, slug } = this.props
    let classNames = {
      [styles.selected]: isSelected
    }

    return (

        <Link to={`/a/${slug}`}>
          { bookmark }
        </Link>

    )
  }
}

export default class Bookmarks extends Component {
  constructor(props) {
    super(props)
    this.state = {
      bookmarksWidth : 0,
      containerWidth: 0,
      selectedBookmarkOffsetLeft: 0
    }
    this.changeSelectedBookmarkOffsetLeft = this._changeSelectedBookmarkOffsetLeft.bind(this)
  }

  componentDidMount() {
    let bookmarksNode = ReactDOM.findDOMNode(this.refs.bookmarksRef)
    this.setState({
      containerWidth: bookmarksNode.offsetWidth,
      bookmarksWidth: this._calculateTotalWidth()
    })
  }

  componentDidUpdate() {
    let node = document.getElementById('bookmarksForLongformArtilce')
    node.scrollLeft = this._calculateScrollLeft()
  }

  _changeSelectedBookmarkOffsetLeft(offsetLeft) {
    this.setState({
      selectedBookmarkOffsetLeft: offsetLeft
    })
  }

  _calculateScrollLeft() {
    const { containerWidth, selectedBookmarkOffsetLeft } = this.state
    if (selectedBookmarkOffsetLeft > (containerWidth / 2)) {
      return  selectedBookmarkOffsetLeft - (containerWidth / 2)
    }
    return 0
  }

  _calculateTotalWidth() {
    let totalWidth = 0
    let bookmarksLength = get(this.props, [ 'bookmarks', 'length' ], 0)
    for (let i = 0; i < bookmarksLength; i++) {
      let bookmarkNode = ReactDOM.findDOMNode(this.refs[`bookmarkRef-${i}`])
      let width = get(bookmarkNode, 'offsetWidth', 0)
      totalWidth += width
    }
    return totalWidth
  }

  _handleNavClick(direction, event) { //eslint-disable-line
    let node = document.getElementById('bookmarksForLongformArtilce')
    let scrollLeft = node.scrollLeft
    if (direction === LEFT) {
      if (scrollLeft !== 0) {
        let scrollDelta = scrollLeft - DISTANCE_PER_SCROLL
        node.scrollLeft = scrollDelta < 0 ? 0 : scrollDelta
      }
    } else if (direction === RIGHT) {
      node.scrollLeft = scrollLeft + DISTANCE_PER_SCROLL
    }
  }

  render() {
    const { bookmarks } = this.props
    let _Bookmarks = map(bookmarks, (ele, index) => {
      return (
        <Bookmark
          ref={`bookmarkRef-${index}`}
          key={index}
          changeSelectedBookmarkOffsetLeft={this.changeSelectedBookmarkOffsetLeft}
          {...ele}
        />
      )
    })

    // this is used for caculating the offsetLeft of the current bookmark
    let style = {
      position: 'relative'
    }

    return (

          {_Bookmarks}

    )
  }
}
<commit_msg>[Update] Update bookmark. set target="_blank" when style of article is interactive<commit_after>'use strict'
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import cx from 'classnames'
import leftNav from '../../../static/asset/disabled-left-arrow.svg'
import rightNav from '../../../static/asset/disabled-right-arrow.svg'
import styles from './Bookmarks.scss'

// lodash
import get from 'lodash/get'
import map from 'lodash/map'

import Link from '../Link'
import { INTERACTIVE_ARTICLE_STYLE } from '../../constants/index'

const RIGHT = 'right'
const LEFT = 'left'
const DISTANCE_PER_SCROLL = 50

class Bookmark extends Component {
  constructor(props) {
    super(props)
  }

  componentDidMount() {
    const { changeSelectedBookmarkOffsetLeft, isSelected } = this.props
    let bookmarkNode = ReactDOM.findDOMNode(this.refs.bookmarkRef)
    if (isSelected) {
      changeSelectedBookmarkOffsetLeft(bookmarkNode.offsetLeft)
    }
  }

  render() {
    const { bookmark, isSelected, slug, style } = this.props
    let classNames = {
      [styles.selected]: isSelected
    }

    return (

        <Link to={`/a/${slug}`} disableReactRouter={style === INTERACTIVE_ARTICLE_STYLE} target="_blank">
          {bookmark}
        </Link>

    )
  }
}

Bookmark.propTypes = {
  bookmark: React.PropTypes.string.isRequired,
  isSelected: React.PropTypes.bool,
  slug: React.PropTypes.string.isRequired
}

Bookmark.defaultProps = {
  isSelected: false
}

export default class Bookmarks extends Component {
  constructor(props) {
    super(props)
    this.state = {
      bookmarksWidth : 0,
      containerWidth: 0,
      selectedBookmarkOffsetLeft: 0
    }
    this.changeSelectedBookmarkOffsetLeft = this._changeSelectedBookmarkOffsetLeft.bind(this)
  }

  componentDidMount() {
    let bookmarksNode = ReactDOM.findDOMNode(this.refs.bookmarksRef)
    this.setState({
      containerWidth: bookmarksNode.offsetWidth,
      bookmarksWidth: this._calculateTotalWidth()
    })
  }

  componentDidUpdate() {
    let node = document.getElementById('bookmarksForLongformArtilce')
    node.scrollLeft = this._calculateScrollLeft()
  }

  _changeSelectedBookmarkOffsetLeft(offsetLeft) {
    this.setState({
      selectedBookmarkOffsetLeft: offsetLeft
    })
  }

  _calculateScrollLeft() {
    const { containerWidth, selectedBookmarkOffsetLeft } = this.state
    if (selectedBookmarkOffsetLeft > (containerWidth / 2)) {
      return  selectedBookmarkOffsetLeft - (containerWidth / 2)
    }
    return 0
  }

  _calculateTotalWidth() {
    let totalWidth = 0
    let bookmarksLength = get(this.props, [ 'bookmarks', 'length' ], 0)
    for (let i = 0; i < bookmarksLength; i++) {
      let bookmarkNode = ReactDOM.findDOMNode(this.refs[`bookmarkRef-${i}`])
      let width = get(bookmarkNode, 'offsetWidth', 0)
      totalWidth += width
    }
    return totalWidth
  }

  _handleNavClick(direction, event) { //eslint-disable-line
    let node = document.getElementById('bookmarksForLongformArtilce')
    let scrollLeft = node.scrollLeft
    if (direction === LEFT) {
      if (scrollLeft !== 0) {
        let scrollDelta = scrollLeft - DISTANCE_PER_SCROLL
        node.scrollLeft = scrollDelta < 0 ? 0 : scrollDelta
      }
    } else if (direction === RIGHT) {
      node.scrollLeft = scrollLeft + DISTANCE_PER_SCROLL
    }
  }

  render() {
    const { bookmarks } = this.props
    let _Bookmarks = map(bookmarks, (ele, index) => {
      const { bookmarkOrder, publishedDate, ...rest } = ele
      return (
        <Bookmark
          ref={`bookmarkRef-${index}`}
          key={index}
          changeSelectedBookmarkOffsetLeft={this.changeSelectedBookmarkOffsetLeft}
          {...rest}
        />
      )
    })

    // this is used for caculating the offsetLeft of the current bookmark
    let style = {
      position: 'relative'
    }

    return (

          {_Bookmarks}

    )
  }
}

Bookmarks.propTypes = {
  bookmarks: React.PropTypes.arrayOf(
    React.PropTypes.shape({
      bookmark: React.PropTypes.string.isRequired,
      bookmarkOrder: React.PropTypes.number,
      publishedDate: React.PropTypes.string,
      isSelected: React.PropTypes.bool,
      slug: React.PropTypes.string.isRequired,
      style: React.PropTypes.string.isRequired
    })
  ).isRequired
}
<|endoftext|>


<commit_before>import {compose, identity, isNil, map, reject, tap} from 'ramda'
import {Observable} from 'rx'
import React, {PropTypes} from 'react'
import THREE from 'three'
import {randomMesh} from '../../utils/webGLHelpers'
import {clamp} from '../../utils/helpers'

const {fromEvent, merge} = Observable

const cameraZ = 16
const minZ = -128
const sideLength = 1
const maxDepth = 3 * sideLength

let currentXYRatios = null
let controlPadElement = null
let renderLoopActive = null
let token = null
let renderer = null
let camera = null
let scene = null
let mouseInputEnabled = false
let controlPadActive = false

const setRendererSize = _ => {
  const rendererSize = window.innerWidth < window.innerHeight
    ? window.innerWidth
    : window.innerHeight * 0.8
  renderer.setSize(rendererSize, rendererSize)
}

const renderLoop = _ => {
  if (!renderLoopActive) return
  window.requestAnimationFrame(renderLoop)
  const controlPadHasNotBeenUsed = isNil(currentXYRatios)
  const {z} = token.position
  if (controlPadHasNotBeenUsed) return
  if (!controlPadActive) {
    if (z > minZ - maxDepth) token.position.z -= 1
    renderer.render(scene, camera)
    return
  }
  const {xRatio, yRatio} = currentXYRatios
  const xMod = xRatio < 0.5
    ? -(xRatio - 0.5) ** 2
    : (xRatio - 0.5) ** 2
  const yMod = yRatio < 0.5
    ? -(yRatio - 0.5) ** 2
    : (yRatio - 0.5) ** 2
  const rotationBaseAmount = 0.01
  const rotationVelocityComponent = 0.8
  token.rotation.x += rotationBaseAmount + rotationVelocityComponent * xMod
  token.rotation.y += rotationBaseAmount + rotationVelocityComponent * yMod
  token.rotation.z += rotationBaseAmount + rotationVelocityComponent * xMod * yMod
  token.position.x = (xRatio - 0.5) * cameraZ
  token.position.y = (0.5 - yRatio) * cameraZ
  const returnVelocity = 8
  if (z < 0) token.position.z += z > -returnVelocity ? -z : returnVelocity
  renderer.render(scene, camera)
}

const validRatio = clamp(0, 1 - Number.EPSILON)

const calculateXAndYRatio = e => {
  const {top, right, bottom, left} = e.target.getBoundingClientRect()
  const [width, height] = [right - left, bottom - top]
  const {clientX, clientY} = e.changedTouches && e.changedTouches[0] || e
  const [x, y] = [clientX - left, clientY - top]
  return {xRatio: validRatio(x / width), yRatio: validRatio(y / height)}
}

export default class extends React.Component {
  static propTypes = {
    inputEndTransducer: PropTypes.func,
    inputTransducer: PropTypes.func
  }
  componentDidMount () {
    const {inputEndTransducer, inputTransducer} = this.props
    controlPadElement = document.querySelector('.control-pad')
    const input$ = merge(
      fromEvent(controlPadElement, 'touchstart'),
      fromEvent(controlPadElement, 'touchmove'),
      fromEvent(controlPadElement, 'mousedown'),
      fromEvent(controlPadElement, 'mousemove')
    )
    const endInput$ = merge(
      fromEvent(controlPadElement, 'touchend'),
      fromEvent(controlPadElement, 'mouseup')
    )

    input$
      .transduce(compose(
        map(tap(e => mouseInputEnabled = e.type === 'mousedown'
          ? true
          : mouseInputEnabled)),
        reject(e => e instanceof window.MouseEvent && !mouseInputEnabled),
        map(tap(_ => controlPadActive = true)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputTransducer)
      .subscribe(identity, ::console.error)

    endInput$
      .transduce(compose(
        map(tap(_ => controlPadActive = false)),
        map(tap(_ => mouseInputEnabled = false)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputEndTransducer)
      .subscribe(identity, ::console.error)

    renderLoopActive = true
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(
      54,
      1,
      cameraZ - maxDepth,
      cameraZ - minZ
    )
    renderer = new THREE.WebGLRenderer({canvas: controlPadElement})
    const directionalLight = new THREE.DirectionalLight(0xffffff)
    token = randomMesh()
    token.position.z = minZ - maxDepth

    directionalLight.position.set(16, 16, 24).normalize()
    scene.add(new THREE.AmbientLight(0x333333))
      .add(directionalLight)
      .add(token)
    camera.position.z = cameraZ

    setRendererSize()
    window.onresize = setRendererSize

    controlPadElement.oncontextmenu = e => e.preventDefault()

    renderLoop()
  }

  componentWillUnmount () {
    renderLoopActive = false
    window.onresize = null
  }

  render () {
    return <canvas width='768' height='768' className='control-pad'></canvas>
  }
}
<commit_msg>style(ControlPad) meet linting rules<commit_after>import {compose, identity, isNil, map, reject, tap} from 'ramda'
import {Observable} from 'rx'
import React, {PropTypes} from 'react'
import THREE from 'three'
import {randomMesh} from '../../utils/webGLHelpers'
import {clamp} from '../../utils/helpers'

const {fromEvent, merge} = Observable

const cameraZ = 16
const minZ = -128
const sideLength = 1
const maxDepth = 3 * sideLength

let currentXYRatios = null
let controlPadElement = null
let renderLoopActive = null
let token = null
let renderer = null
let camera = null
let scene = null
let mouseInputEnabled = false
let controlPadActive = false

const setRendererSize = _ => {
  const rendererSize = window.innerWidth < window.innerHeight
    ? window.innerWidth
    : window.innerHeight * 0.8
  renderer.setSize(rendererSize, rendererSize)
}

const renderLoop = _ => {
  if (!renderLoopActive) return
  window.requestAnimationFrame(renderLoop)
  const controlPadHasNotBeenUsed = isNil(currentXYRatios)
  const {z} = token.position
  if (controlPadHasNotBeenUsed) return
  if (!controlPadActive) {
    if (z > minZ - maxDepth) token.position.z -= 1
    renderer.render(scene, camera)
    return
  }
  const {xRatio, yRatio} = currentXYRatios
  const xMod = xRatio < 0.5
    ? -(xRatio - 0.5) ** 2
    : (xRatio - 0.5) ** 2
  const yMod = yRatio < 0.5
    ? -(yRatio - 0.5) ** 2
    : (yRatio - 0.5) ** 2
  const rotationBaseAmount = 0.01
  const rotationVelocityComponent = 0.8
  token.rotation.x += rotationBaseAmount + rotationVelocityComponent * xMod
  token.rotation.y += rotationBaseAmount + rotationVelocityComponent * yMod
  token.rotation.z += rotationBaseAmount + rotationVelocityComponent * xMod * yMod
  token.position.x = (xRatio - 0.5) * cameraZ
  token.position.y = (0.5 - yRatio) * cameraZ
  const returnVelocity = 8
  if (z < 0) token.position.z += z > -returnVelocity ? -z : returnVelocity
  renderer.render(scene, camera)
}

const validRatio = clamp(0, 1 - Number.EPSILON)

const calculateXAndYRatio = e => {
  const {top, right, bottom, left} = e.target.getBoundingClientRect()
  const [width, height] = [right - left, bottom - top]
  const {clientX, clientY} = e.changedTouches && e.changedTouches[0] || e
  const [x, y] = [clientX - left, clientY - top]
  return {xRatio: validRatio(x / width), yRatio: validRatio(y / height)}
}

export default class extends React.Component {
  static propTypes = {
    inputEndTransducer: PropTypes.func,
    inputTransducer: PropTypes.func
  };
  componentDidMount () {
    const {inputEndTransducer, inputTransducer} = this.props
    controlPadElement = document.querySelector('.control-pad')
    const input$ = merge(
      fromEvent(controlPadElement, 'touchstart'),
      fromEvent(controlPadElement, 'touchmove'),
      fromEvent(controlPadElement, 'mousedown'),
      fromEvent(controlPadElement, 'mousemove')
    )
    const endInput$ = merge(
      fromEvent(controlPadElement, 'touchend'),
      fromEvent(controlPadElement, 'mouseup')
    )

    input$
      .transduce(compose(
        map(tap(e => mouseInputEnabled = e.type === 'mousedown'
          ? true
          : mouseInputEnabled)),
        reject(e => e instanceof window.MouseEvent && !mouseInputEnabled),
        map(tap(_ => controlPadActive = true)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputTransducer)
      .subscribe(identity, ::console.error)

    endInput$
      .transduce(compose(
        map(tap(_ => controlPadActive = false)),
        map(tap(_ => mouseInputEnabled = false)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputEndTransducer)
      .subscribe(identity, ::console.error)

    renderLoopActive = true
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(
      54,
      1,
      cameraZ - maxDepth,
      cameraZ - minZ
    )
    renderer = new THREE.WebGLRenderer({canvas: controlPadElement})
    const directionalLight = new THREE.DirectionalLight(0xffffff)
    token = randomMesh()
    token.position.z = minZ - maxDepth

    directionalLight.position.set(16, 16, 24).normalize()
    scene.add(new THREE.AmbientLight(0x333333))
      .add(directionalLight)
      .add(token)
    camera.position.z = cameraZ

    setRendererSize()
    window.onresize = setRendererSize

    controlPadElement.oncontextmenu = e => e.preventDefault()

    renderLoop()
  }

  componentWillUnmount () {
    renderLoopActive = false
    window.onresize = null
  }

  render () {
    return <canvas width='768' height='768' className='control-pad'></canvas>
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import {
  Form,
  Radio,
  Modal,
  Button,
  Message,
} from 'semantic-ui-react';

// CUSTOM
import { renderIfTrue } from '../renderIfTrue';

// URL to direct requests to, from the Google Apps Script
const postUrl = 'https://script.google.com/macros/s/AKfycbyXYbemTPcqsdbmXITnjaNi-CkN85g5kKPrgzt4AS8ykT2jH6Zn/exec';

/**
 * Modal that asks for permission to store user data for debugging purposes
 */
class AskPermission extends React.Component {
  state = {
    submitType: null,
    submitting: false,
  };

  close = () => {
    this.setState({ submitFunc: null });
    this.props.closeAskPermission();
  };

  submit = (evnt) => {
    this.props.submit(evnt, this.state.submitType);
  };

  setSubmitType = (evnt, inputProps) => {
    this.setState({ submitType: inputProps.value });
  };

  // Return an array to take advantage of `Modal` styling
  render () {return ([
    <Modal.Content key = { `ask-permission-content` }>

{ this.props.question }

      <Form.Field>
        <Radio
          name     = { `data-ask` }
          value    = { `withData` }
          label    = { `Yes` }
          checked  = { this.state.submitType === `withData` }
          onChange = { this.setSubmitType } />
      </Form.Field>
      <Form.Field>
        <Radio
          name     = { `data-ask` }
          value    = { `withoutData` }
          label    = { `No` }
          checked  = { this.state.submitType === `withoutData` }
          onChange = { this.setSubmitType } />
      </Form.Field>
    </Modal.Content>,
    <Modal.Actions key = { `ask-permission-actions` }>
      <Button
        onClick  ={ this.close }
        disabled ={ this.props.submitting }>Cancel
      </Button>
      <Button
        onClick  = { this.submit }
        loading  = { this.props.submitting }
        disabled = { this.state.submitType === null }
        color    ='teal'>
        {renderIfTrue(this.state.submitType === `withData`,
          `Send it with my information`
        )}
        {renderIfTrue(this.state.submitType === `withoutData`,
          `Send it without my information`
        )}
        {renderIfTrue(this.state.submitType === null,
          `Send it`
        )}
      </Button>
    </Modal.Actions>,
  ]);}
}

/**
 * Modal that shows the feedback form.
 */
class FeedbackPrompt extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      formData:         {},
      ready:            false,
      submitting:       false,
      submissionFailed: false,
    };
  }

  handleInputChange = (event) => {
    // Source: https://reactjs.org/docs/forms.html#controlled-components
    const target = event.target;
    const name = target.name;
    let value;
    if (target.type === 'checkbox') {
      value = target.checked;
    } else {
      value = target.value;
    }

    this.setState({ formData: Object.assign({}, this.state.formData, { [ name ]: value }) });
  };

  // returns promise that succeeds if submission is successful, else rejects.
  sendDataToSpreadsheet(data) {
    const fetchOptions = {
      method: 'POST',
      body:   JSON.stringify(data),
    };
    return fetch(postUrl, fetchOptions)
      .then((response) => {
        if (response.ok) {
          return response;
        } else {
          var error = new Error(response.statusText);
          error.response = response;
          throw error;
        }
      });
  }

  close = (event) => {
    // Reset state for next time it's opened
    this.setState({
      formData:         {},
      ready:            false,
      submitting:       false,
      submissionFailed: false,
    });
    this.props.close();
  };

  onReady = () => {
    this.setState({ ready: true });
  };

  closeAskPermission = () => {
    this.setState({ ready: false });
  };

  submit = (evnt, type) => {
    this.setState({ submitting: true });

    let data = this.state.formData;
    if (type === `withData`) {
      data = Object.assign({ clientData: this.props.data }, this.state.formData);
    }

    this.sendDataToSpreadsheet(data)
      .then((response) => {
        this.close();
      })
      .catch((error) => {
        this.setState({ submissionFailed: true, submitting: false });
        console.error(error.message);
      });
  };

  render () {
    const inputProps = (name) => {
      return {
        name,
        value:    this.state.formData[ name ] || '',
        onChange: this.handleInputChange,
      };
    };

    return (
      <Modal
        size='large'
        open={ this.props.isOpen }
        onClose={ this.close }
        closeOnDimmerClick={ false }
        closeOnEscape={ false }
        closeIcon>
        <Modal.Header>Tell Us More</Modal.Header>
        <Modal.Content scrolling>
          <Form>
            <Form.Input
              { ...inputProps('currentSnap') }
              label={ 'If amount for the CURRENT SNAP amount was wrong, what\'s the right amount?' } />
            <Form.Input
              { ...inputProps('futureSnap') }
              label={ 'If amount for the FUTURE SNAP amount was wrong, what\'s the right amount?' } />
            <Form.Input
              { ...inputProps('futureS8') }
              label={ 'If amount for the FUTURE Section 8 amount was wrong, what\'s the right amount?' } />
            <Form.TextArea
              { ...inputProps('otherCircumstances') }
              label={ 'What else could be going on that could affect your benefit amount? ' +
              'For example, are you a veteran? Are you a full-time student?' } />
            <Form.TextArea
              { ...inputProps('bugReport') }
              label={ 'If there was a bug or error, describe the bug and what you were trying to do when the bug happened.' } />
            <Form.TextArea
              { ...inputProps('comments') }
              label={ 'Do you have any other comments?' } />
          </Form>
          <Message
            hidden={ !this.state.submissionFailed }
            error>
            Error submitting data, please try again or email us.
          </Message>
        </Modal.Content>
        <Modal.Actions>
          <Button
            onClick={ this.close }
            disabled={ this.state.submitting }>Cancel
          </Button>
          <Button
            onClick={ this.onReady }
            color='teal'>Finished
          </Button>
        </Modal.Actions>

        <Modal
          size               = { `large` }
          open               = { this.state.ready }
          onClose            = { this.closeAskPermission }
          closeOnDimmerClick = { false }
          closeOnEscape      = { false }
          closeIcon>
          <AskPermission
            submitting         = { this.state.submitting }
            closeAskPermission = { this.closeAskPermission }
            submit             = { this.submit }
            question           = { `Is it ok if we save the information you've filled in? We won't save anythign else about you. If you had problems, it'll help us solve them.` } />
        </Modal>
      </Modal>
    );
  }
};

export default FeedbackPrompt;
export { AskPermission };
<commit_msg>Tweaks<commit_after>import React from 'react';
import {
  Form,
  Radio,
  Modal,
  Button,
  Message,
} from 'semantic-ui-react';

// CUSTOM
import { renderIfTrue } from '../renderIfTrue';

// URL to direct requests to, from the Google Apps Script
const postUrl = 'https://script.google.com/macros/s/AKfycbyXYbemTPcqsdbmXITnjaNi-CkN85g5kKPrgzt4AS8ykT2jH6Zn/exec';

/**
 * Modal that asks for permission to store user data for debugging purposes
 */
class AskPermission extends React.Component {
  state = {
    submitType: null,
    submitting: false,
  };

  close = () => {
    this.setState({ submitFunc: null });
    this.props.closeAskPermission();
  };

  submit = (evnt) => {
    this.props.submit(evnt, this.state.submitType);
  };

  setSubmitType = (evnt, inputProps) => {
    this.setState({ submitType: inputProps.value });
  };

  // Return an array to take advantage of `Modal` styling
  render () {return ([
    <Modal.Content key = { `ask-permission-content` }>

{ this.props.question }

      <Form.Field>
        <Radio
          name     = { `data-ask` }
          value    = { `withData` }
          label    = { `Yes` }
          checked  = { this.state.submitType === `withData` }
          onChange = { this.setSubmitType } />
      </Form.Field>
      <Form.Field>
        <Radio
          name     = { `data-ask` }
          value    = { `withoutData` }
          label    = { `No` }
          checked  = { this.state.submitType === `withoutData` }
          onChange = { this.setSubmitType } />
      </Form.Field>
    </Modal.Content>,
    <Modal.Actions key = { `ask-permission-actions` }>
      <Button
        onClick  ={ this.close }
        disabled ={ this.props.submitting }>Cancel
      </Button>
      <Button
        onClick  = { this.submit }
        loading  = { this.props.submitting }
        disabled = { this.state.submitType === null }
        color    ='teal'>
        {renderIfTrue(this.state.submitType === `withData`,
          `Send it with my information`
        )}
        {renderIfTrue(this.state.submitType === `withoutData`,
          `Send it without my information`
        )}
        {renderIfTrue(this.state.submitType === null,
          `Send`
        )}
      </Button>
    </Modal.Actions>,
  ]);}
}

/**
 * Modal that shows the feedback form.
 */
class FeedbackPrompt extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      formData:         {},
      ready:            false,
      submitting:       false,
      submissionFailed: false,
    };
  }

  handleInputChange = (event) => {
    // Source: https://reactjs.org/docs/forms.html#controlled-components
    const target = event.target;
    const name = target.name;
    let value;
    if (target.type === 'checkbox') {
      value = target.checked;
    } else {
      value = target.value;
    }

    this.setState({ formData: Object.assign({}, this.state.formData, { [ name ]: value }) });
  };

  // returns promise that succeeds if submission is successful, else rejects.
  sendDataToSpreadsheet(data) {
    const fetchOptions = {
      method: 'POST',
      body:   JSON.stringify(data),
    };
    return fetch(postUrl, fetchOptions)
      .then((response) => {
        if (response.ok) {
          return response;
        } else {
          var error = new Error(response.statusText);
          error.response = response;
          throw error;
        }
      });
  }

  close = (event) => {
    // Reset state for next time it's opened
    this.setState({
      formData:         {},
      ready:            false,
      submitting:       false,
      submissionFailed: false,
    });
    this.props.close();
  };

  onReady = () => {
    this.setState({ ready: true });
  };

  closeAskPermission = () => {
    this.setState({ ready: false });
  };

  submit = (evnt, type) => {
    this.setState({ submitting: true });

    let data = this.state.formData;
    if (type === `withData`) {
      data = Object.assign({ clientData: this.props.data }, this.state.formData);
    }

    this.sendDataToSpreadsheet(data)
      .then((response) => {
        this.close();
      })
      .catch((error) => {
        this.setState({ submissionFailed: true, submitting: false });
        console.error(error.message);
      });
  };

  render () {
    const inputProps = (name) => {
      return {
        name,
        value:    this.state.formData[ name ] || '',
        onChange: this.handleInputChange,
      };
    };

    return (
      <Modal
        size='large'
        open={ this.props.isOpen }
        onClose={ this.close }
        closeOnDimmerClick={ false }
        closeOnEscape={ false }
        closeIcon>
        <Modal.Header>Tell Us More</Modal.Header>
        <Modal.Content scrolling>
          <Form>
            <Form.Input
              { ...inputProps('currentSnap') }
              label={ If the current SNAP amount was wrong, what's the right amount? } />
            <Form.Input
              { ...inputProps('futureSnap') }
              label={ If the future SNAP amount was wrong, what's the right amount? } />
            <Form.Input
              { ...inputProps('futureS8') }
              label={ If the future Section 8 amount was wrong, what's the right amount? } />
            <Form.TextArea
              { ...inputProps('otherCircumstances') }
              label={ 'What else could be going on that could affect your benefit amount? ' +
              'For example, are you a veteran? Are you a full-time student?' } />
            <Form.TextArea
              { ...inputProps('bugReport') }
              label={ 'If there was a bug or error, describe the bug and what you were trying to do when the bug happened.' } />
            <Form.TextArea
              { ...inputProps('comments') }
              label={ 'Do you have any other comments?' } />
          </Form>
          <Message
            hidden={ !this.state.submissionFailed }
            error>
            Error submitting data, please try again or email us.
          </Message>
        </Modal.Content>
        <Modal.Actions>
          <Button
            onClick={ this.close }
            disabled={ this.state.submitting }>Cancel
          </Button>
          <Button
            onClick={ this.onReady }
            color='teal'>Ready
          </Button>
        </Modal.Actions>

        <Modal
          size               = { `large` }
          open               = { this.state.ready }
          onClose            = { this.closeAskPermission }
          closeOnDimmerClick = { false }
          closeOnEscape      = { false }
          closeIcon>
          <AskPermission
            submitting         = { this.state.submitting }
            closeAskPermission = { this.closeAskPermission }
            submit             = { this.submit }
            question           = { `Is it ok if we save the information you've put in here? We won't save anything else about you. If you had problems with the app, it'll help us work on them.` } />
        </Modal>
      </Modal>
    );
  }
};

export default FeedbackPrompt;
export { AskPermission };
<|endoftext|>


<commit_before>import React from 'react';
import Title from 'react-title-component';
import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import composeAsyncContainer from '../common/AsyncContainer';
import { selectTopic, filterBySnapshot, filterByTimespan, filterByFocus, fetchTopicSummary, filterByQuery } from '../../actions/topicActions';
import { addNotice, setSubHeaderVisible } from '../../actions/appActions';
import { LEVEL_WARNING, LEVEL_ERROR, ErrorNotice, WarningNotice } from '../common/Notice';
import TopicUnderConstruction from './TopicUnderConstruction';

const localMessages = {
  needsSnapshotWarning: { id: 'needSnapshot.warning', defaultMessage: 'You\'ve made changes to your Topic that require a new snapshot to be generated!' },
  snapshotBuilderLink: { id: 'needSnapshot.snapshotBuilderLink', defaultMessage: 'Visit the Snapshot Builder for details.' },
  hasAnError: { id: 'topic.hasError', defaultMessage: 'Sorry, this topic has an error!  See the notes at the top if this page for more technical details.' },
  topicRunning: { id: 'topic.topicRunning', defaultMessage: 'We are still scraping the web for all the stories in include!' },
};

class TopicContainer extends React.Component {
  componentWillMount() {
    const { needsNewSnapshot, addAppNotice, topicInfo } = this.props;
    const { formatMessage } = this.props.intl;
    // warn user if they made changes that require a new snapshot
    if (needsNewSnapshot) {
      addAppNotice({ level: LEVEL_WARNING, message: formatMessage(localMessages.needsSnapshotWarning) });
    }
    if (topicInfo.state === 'error') {
      addAppNotice({ level: LEVEL_ERROR, message: topicInfo.message });
    }
  }
  componentWillReceiveProps(nextProps) {
    const { topicId, topicInfo, selectNewTopic, needsNewSnapshot, addAppNotice } = this.props;
    const { formatMessage } = this.props.intl;
    if ((nextProps.topicId !== topicId)) {
      selectNewTopic(topicId);
      // warn user if they made changes that require a new snapshot
      if (needsNewSnapshot) {
        addAppNotice({ level: LEVEL_WARNING, message: formatMessage(localMessages.needsSnapshotWarning) });
      }
      if (topicInfo.state === 'error') {
        addAppNotice({ level: LEVEL_ERROR, message: topicInfo.message });
      }
    }
  }
  componentWillUnmount() {
    const { selectNewTopic } = this.props;
    selectNewTopic(null);
  }
  filtersAreSet() {
    const { filters, topicId } = this.props;
    return ((topicId !== null) && (filters.snapshotId !== null) && (filters.timespanId !== null));
  }
  render() {
    const { children, topicInfo } = this.props;
    const titleHandler = parentTitle => `${topicInfo.name} | ${parentTitle}`;
    // show a big error if there is one to show
    let notice = null;
    let contentToShow = children;
    switch (topicInfo.state) {
      case 'running':
        notice = (
          <WarningNotice details={topicInfo.message}>
            <FormattedMessage {...localMessages.topicRunning} />
          </WarningNotice>
        );
        contentToShow = (<TopicUnderConstruction />);
        break;
      case 'error':
        notice = (
          <ErrorNotice>
            <FormattedMessage {...localMessages.hasAnError} />
          </ErrorNotice>
        );
        break;
      default:
        notice = null;
        break;
    }
    return (

          <Title render={titleHandler} />
          {notice}
          {contentToShow}

    );
  }
}

TopicContainer.propTypes = {
  // from context
  intl: React.PropTypes.object.isRequired,
  children: React.PropTypes.node,
  location: React.PropTypes.object.isRequired,
  topicId: React.PropTypes.number.isRequired,
  // from dispatch
  asyncFetch: React.PropTypes.func.isRequired,
  selectNewTopic: React.PropTypes.func.isRequired,
  addAppNotice: React.PropTypes.func.isRequired,
  // from state
  filters: React.PropTypes.object.isRequired,
  fetchStatus: React.PropTypes.string.isRequired,
  topicInfo: React.PropTypes.object,
  needsNewSnapshot: React.PropTypes.bool.isRequired,
};

const mapStateToProps = (state, ownProps) => ({
  filters: state.topics.selected.filters,
  fetchStatus: state.topics.selected.info.fetchStatus,
  topicInfo: state.topics.selected.info,
  topicId: parseInt(ownProps.params.topicId, 10),
  needsNewSnapshot: state.topics.selected.needsNewSnapshot,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  addAppNotice: (info) => {
    dispatch(addNotice(info));
  },
  selectNewTopic: (topicId) => {
    dispatch(selectTopic(topicId));
    if (topicId === null) {
      dispatch(setSubHeaderVisible(false));
    }
  },
  asyncFetch: () => {
    dispatch(selectTopic(ownProps.params.topicId));
    // select any filters that are serialized on the url
    const query = ownProps.location.query;
    if (ownProps.location.query.snapshotId) {
      dispatch(filterBySnapshot(query.snapshotId));
    }
    if (ownProps.location.query.focusId) {
      dispatch(filterByFocus(query.focusId));
    }
    if (ownProps.location.query.timespanId) {
      dispatch(filterByTimespan(query.timespanId));
    }
    if (ownProps.location.query.q) {
      dispatch(filterByQuery(query.q));
    }
    dispatch(fetchTopicSummary(ownProps.params.topicId))
      .then(() => dispatch(setSubHeaderVisible(true)));
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
        composeAsyncContainer(
          TopicContainer
        )
      )
  );
<commit_msg>don't unset topic on unmount, cause it causes (#365)<commit_after>import React from 'react';
import Title from 'react-title-component';
import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import composeAsyncContainer from '../common/AsyncContainer';
import { selectTopic, filterBySnapshot, filterByTimespan, filterByFocus, fetchTopicSummary, filterByQuery } from '../../actions/topicActions';
import { addNotice, setSubHeaderVisible } from '../../actions/appActions';
import { LEVEL_WARNING, LEVEL_ERROR, ErrorNotice, WarningNotice } from '../common/Notice';
import TopicUnderConstruction from './TopicUnderConstruction';

const localMessages = {
  needsSnapshotWarning: { id: 'needSnapshot.warning', defaultMessage: 'You\'ve made changes to your Topic that require a new snapshot to be generated!' },
  snapshotBuilderLink: { id: 'needSnapshot.snapshotBuilderLink', defaultMessage: 'Visit the Snapshot Builder for details.' },
  hasAnError: { id: 'topic.hasError', defaultMessage: 'Sorry, this topic has an error!  See the notes at the top if this page for more technical details.' },
  topicRunning: { id: 'topic.topicRunning', defaultMessage: 'We are still scraping the web for all the stories in include!' },
};

class TopicContainer extends React.Component {
  componentWillMount() {
    const { needsNewSnapshot, addAppNotice, topicInfo } = this.props;
    const { formatMessage } = this.props.intl;
    // warn user if they made changes that require a new snapshot
    if (needsNewSnapshot) {
      addAppNotice({ level: LEVEL_WARNING, message: formatMessage(localMessages.needsSnapshotWarning) });
    }
    if (topicInfo.state === 'error') {
      addAppNotice({ level: LEVEL_ERROR, message: topicInfo.message });
    }
  }
  componentWillReceiveProps(nextProps) {
    const { topicId, topicInfo, selectNewTopic, needsNewSnapshot, addAppNotice } = this.props;
    const { formatMessage } = this.props.intl;
    // if they edited the topic, or the topic changed then reload
    if ((nextProps.topicInfo !== topicInfo) || (nextProps.topicId !== topicId)) {
      selectNewTopic(topicId);
      // warn user if they made changes that require a new snapshot
      if (needsNewSnapshot) {
        addAppNotice({ level: LEVEL_WARNING, message: formatMessage(localMessages.needsSnapshotWarning) });
      }
      if (topicInfo.state === 'error') {
        addAppNotice({ level: LEVEL_ERROR, message: topicInfo.message });
      }
    }
  }
  filtersAreSet() {
    const { filters, topicId } = this.props;
    return ((topicId !== null) && (filters.snapshotId !== null) && (filters.timespanId !== null));
  }
  render() {
    const { children, topicInfo } = this.props;
    const titleHandler = parentTitle => `${topicInfo.name} | ${parentTitle}`;
    // show a big error if there is one to show
    let notice = null;
    let contentToShow = children;
    switch (topicInfo.state) {
      case 'running':
        notice = (
          <WarningNotice details={topicInfo.message}>
            <FormattedMessage {...localMessages.topicRunning} />
          </WarningNotice>
        );
        contentToShow = (<TopicUnderConstruction />);
        break;
      case 'error':
        notice = (
          <ErrorNotice>
            <FormattedMessage {...localMessages.hasAnError} />
          </ErrorNotice>
        );
        break;
      default:
        notice = null;
        break;
    }
    return (

          <Title render={titleHandler} />
          {notice}
          {contentToShow}

    );
  }
}

TopicContainer.propTypes = {
  // from context
  intl: React.PropTypes.object.isRequired,
  children: React.PropTypes.node,
  location: React.PropTypes.object.isRequired,
  topicId: React.PropTypes.number.isRequired,
  // from dispatch
  asyncFetch: React.PropTypes.func.isRequired,
  selectNewTopic: React.PropTypes.func.isRequired,
  addAppNotice: React.PropTypes.func.isRequired,
  // from state
  filters: React.PropTypes.object.isRequired,
  fetchStatus: React.PropTypes.string.isRequired,
  topicInfo: React.PropTypes.object,
  needsNewSnapshot: React.PropTypes.bool.isRequired,
};

const mapStateToProps = (state, ownProps) => ({
  filters: state.topics.selected.filters,
  fetchStatus: state.topics.selected.info.fetchStatus,
  topicInfo: state.topics.selected.info,
  topicId: parseInt(ownProps.params.topicId, 10),
  needsNewSnapshot: state.topics.selected.needsNewSnapshot,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  addAppNotice: (info) => {
    dispatch(addNotice(info));
  },
  selectNewTopic: (topicId) => {
    dispatch(selectTopic(topicId));
    if (topicId === null) {
      dispatch(setSubHeaderVisible(false));
    }
  },
  asyncFetch: () => {
    dispatch(selectTopic(ownProps.params.topicId));
    // select any filters that are serialized on the url
    const query = ownProps.location.query;
    if (ownProps.location.query.snapshotId) {
      dispatch(filterBySnapshot(query.snapshotId));
    }
    if (ownProps.location.query.focusId) {
      dispatch(filterByFocus(query.focusId));
    }
    if (ownProps.location.query.timespanId) {
      dispatch(filterByTimespan(query.timespanId));
    }
    if (ownProps.location.query.q) {
      dispatch(filterByQuery(query.q));
    }
    dispatch(fetchTopicSummary(ownProps.params.topicId))
      .then(() => dispatch(setSubHeaderVisible(true)));
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
        composeAsyncContainer(
          TopicContainer
        )
      )
  );
<|endoftext|>