<commit_before>"use strict";
let express = require("express");
let router = express.Router(); // eslint-disable-line new-cap
let {
  db,
  as,
  TITLES_FOR_THINGS,
  SEARCH,
  FEATURED_MAP,
  FEATURED,
  SEARCH_MAP,
  LIST_MAP_CASES,
  LIST_MAP_ORGANIZATIONS
} = require("../helpers/db");
let { preparse_query } = require("../helpers/search");
const { supportedTypes, parseGetParams } = require("../helpers/things");
const createCSVDataDump = require("../helpers/create-csv-data-dump.js");
const logError = require("../helpers/log-error.js");

const RESPONSE_LIMIT = 20;

function randomTexture() {
  let index = Math.floor(Math.random() * 6) + 1;
  return `/images/texture_${index}.svg`;
}

function getLanguage(req) {
  // once we have translations for user generated content in all supported languages,
  // we can use the locale cookie to query by language.
  // currently if the locale is set to something other than "en", no results are returned,
  // so hardcoding "en" here
  // return req.cookies.locale || "en";
  return "en";
}

/**
 *  Deprecated, use /list/* methods instead
 *
 */
router.get("/getAllForType", async function getAllForType(req, res) {
  try {
    let objType = req.query.objType.toLowerCase();
    let page = Math.max(parseInt(req.query.page || 1), 1);
    if (Number.isNaN(page)) {
      page = 1;
    }
    let offset = 0;
    let response_limit = Number.MAX_SAFE_INTEGER;
    if (!req.query.response_limit) {
      // do nothing, return everything
    } else if (
      req.query.response_limit &&
      req.query.response_limit.toLowerCase() === "none"
    ) {
      response_limit = Number.MAX_SAFE_INTEGER;
    } else {
      response_limit = parseInt(req.query.response_limit || RESPONSE_LIMIT);
      offset = Math.max(page - 1, 0) * response_limit;
    }
    if (!supportedTypes.includes(objType)) {
      res.status(401).json({
        message: "Unsupported objType for getAllForType: " + objType
      });
    }
    const titlelist = await db.any(TITLES_FOR_THINGS, {
      language: as.value(getLanguage(req)),
      limit: response_limit,
      offset: offset,
      type: objType
    });
    let jtitlelist = {};
    // FIXME: this is a dumb format but it is what front-end expects.
    // Switch both (and tests) to use array of {title: , id: } pairs.
    // Also, if we're going to use {OK: true, data: []} everywhere else
    // we should use it here too.
    titlelist.forEach(function(row) {
      jtitlelist[row.title] = Number(row.thingid);
    });
    res.status(200).json(jtitlelist);
  } catch (error) {
    logError(error);
    res.status(500).json({ error: error });
  }
});

// strip off final character (assumed to be "s")
const singularLowerCase = name =>
  (name.slice(-1) === "s" ? name.slice(0, -1) : name).toLowerCase();

// just get the type, if specified
const typeFromReq = req => {
  let cat = singularLowerCase(req.query.selectedCategory || "Alls");
  return cat === "all" ? "thing" : cat;
};

const queryFileFromReq = req => {
  const featuredOnly =
    !req.query.query || (req.query.query || "").toLowerCase() === "featured";
  const resultType = (req.query.resultType || "").toLowerCase();
  let queryfile = SEARCH;
  if (featuredOnly && resultType === "map") {
    queryfile = FEATURED_MAP;
  } else if (featuredOnly) {
    queryfile = FEATURED;
  } else if (resultType == "map") {
    queryfile = SEARCH_MAP;
  }
  return queryfile;
};

const offsetFromReq = req => {
  const page = Math.max(as.number(req.query.page || 1), 1);
  return (page - 1) * limitFromReq(req);
};

const limitFromReq = req => {
  let limit = parseInt(req.query.limit || RESPONSE_LIMIT);
  const resultType = (req.query.resultType || "").toLowerCase();
  if (resultType === "map") {
    limit = 0; // return all
  }
  return limit;
};

const sortbyFromReq = req => {
  if (req.query.sortby === "post_date") {
    return "post_date";
  }
  return "updated_date";
};

const keyFacetFromReq = (req, name) => {
  let value = req.query[name];
  return value ? ` AND ${name} ='${value}' ` : "";
};

const keyListFacetFromReq = (req, name) => {
  let value = req.query[name];
  if (!value) {
    return "";
  }
  value = as.array(value.split(","));
  return ` AND ${name} && ${value} `;
};

const facetsFromReq = req => {
  if (typeFromReq(req) !== "case") {
    return "";
  }
  const keys = [
    "country",
    "scope_of_influence",
    "public_spectrum",
    "open_limited",
    "recruitment_method"
  ];
  const keyLists = [
    "general_issues",
    "purposes",
    "approaches",
    "method_types",
    "tools_techniques_types",
    "organizer_types",
    "funder_types",
    "change_types"
  ];
  let keyFacets = keys.map(key => keyFacetFromReq(req, key));
  let keyListFacets = keyLists.map(key => keyListFacetFromReq(req, key));
  return keyFacets.join("") + keyListFacets.join("");
};

/**
 * @api {get} /search Search through the cases
 * @apiGroup Search
 * @apiVersion 0.1.0
 * @apiName search
 *
 * @apiParam  {String} query query term
 * @apiParam  {String} sortingMethod ('chronological' or 'alphabetical' or 'featured')
 * @apiParam  {String} selectedCategory ('All' or 'Case' or 'Method' or 'Organization' or 'News')
 *
 * @apiSuccess {Boolean} OK true if call was successful
 * @apiSuccess {String[]} errors List of error strings (when `OK` is false)
 * @apiSuccess {Object} data Mapping of country names to counts (when `OK` is true)
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "OK": true,
 *       "data": {
 *          ... (records) ...
 *       }
 *     }
 *
 */

// Should not return things that aren't displayable as SearchHits (i.e. Users...)

// two factors for search: if there is a selectedCategory then filter by it, always
// if there is no query OR the query is "featured" then return all featured items
// One further item: need an alternative search which returns only map-level items and has no pagination

router.get("/", async function(req, res) {
  const user_query = req.query.query || "";
  const parsed_query = preparse_query(user_query);
  const limit = limitFromReq(req);
  const lang = as.value(getLanguage(req));
  const type = typeFromReq(req);
  const params = parseGetParams(req, type);
  try {
    const results = await db.any(queryFileFromReq(req), {
      query: parsed_query,
      limit: limit ? limit : null, // null is no limit in SQL
      offset: offsetFromReq(req),
      language: lang,
      userId: req.user ? req.user.id : null,
      sortby: sortbyFromReq(req),
      type: type + "s",
      facets: facetsFromReq(req)
    });
    const total = Number(
      results.length ? results[0].total || results.length : 0
    );
    const searchhits = results.filter(result => result.searchmatched).length;
    const pages = Math.max(limit ? Math.ceil(total / limit) : 1, 1); // Don't divide by zero limit, don't return page 1 of 1
    results.forEach(obj => {
      // massage results for display
      if (obj.photos.length) {
        obj.photos.forEach(img => {
          if (!img.url.startsWith("http")) {
            img.url = process.env.AWS_UPLOADS_URL + encodeURIComponent(img.url);
          }
        });
      } else {
        obj.photos = [{ url: randomTexture() }];
      }
      delete obj.total;
    });
    let OK = true;
    let returnType = req.query.returns;
    switch (returnType) {
      case "json":
        return res.status(200).json({
          total,
          pages,
          searchhits,
          results,
          user_query,
          parsed_query,
          params,
          user: req.user || null
        });
      case "htmlfrag":
        return res.status(200).render("home-search", {
          total,
          pages,
          searchhits,
          results,
          params,
          user: req.user || null
        });
      case "csv":
        if (type === "thing") {
          return res.status(200).json({
            msg:
              "You can only get a csv file from cases, methods or organizations tabs."
          });
        } else {
          const file = await createCSVDataDump(type);
          return res.download(file);
        }
      case "xml":
        return res.status(500, "XML not implemented yet").render();
      case "html": // fall through
      default:
        return res.status(200).render("home-search", {
          OK,
          total,
          pages,
          searchhits,
          results,
          params,
          user: req.user || null
        });
    }
  } catch (error) {
    console.error("Error in /search: %s", error.message);
    logError(error);
    let OK = false;
    res.status(500).json({ OK, error });
  }
});

/*
 * Deprecated, use /search/?resultType=map
 *
 */
router.get("/map", async function(req, res) {
  try {
    const RESPONSE_LIMIT = 1000;
    const offset = 0;
    const cases = await db.any(LIST_MAP_CASES, {
      language: as.value(getLanguage(req)),
      limit: RESPONSE_LIMIT,
      offset: offset
    });
    const orgs = await db.any(LIST_MAP_ORGANIZATIONS, {
      language: as.value(getLanguage(req)),
      limit: RESPONSE_LIMIT,
      offset: offset
    });

    res.status(200).json({ data: { cases, orgs } });
  } catch (error) {
    logError(error);
    res.status(500).json({ error: error });
  }
});

module.exports = router;
<commit_msg>[765] 1. allow filtering on the completeness field. 2. Add completeness on the keylist[]<commit_after>"use strict";
let express = require("express");
let router = express.Router(); // eslint-disable-line new-cap
let {
  db,
  as,
  TITLES_FOR_THINGS,
  SEARCH,
  FEATURED_MAP,
  FEATURED,
  SEARCH_MAP,
  LIST_MAP_CASES,
  LIST_MAP_ORGANIZATIONS
} = require("../helpers/db");
let { preparse_query } = require("../helpers/search");
const { supportedTypes, parseGetParams } = require("../helpers/things");
const createCSVDataDump = require("../helpers/create-csv-data-dump.js");
const logError = require("../helpers/log-error.js");

const RESPONSE_LIMIT = 20;

function randomTexture() {
  let index = Math.floor(Math.random() * 6) + 1;
  return `/images/texture_${index}.svg`;
}

function getLanguage(req) {
  // once we have translations for user generated content in all supported languages,
  // we can use the locale cookie to query by language.
  // currently if the locale is set to something other than "en", no results are returned,
  // so hardcoding "en" here
  // return req.cookies.locale || "en";
  return "en";
}

/**
 *  Deprecated, use /list/* methods instead
 *
 */
router.get("/getAllForType", async function getAllForType(req, res) {
  try {
    let objType = req.query.objType.toLowerCase();
    let page = Math.max(parseInt(req.query.page || 1), 1);
    if (Number.isNaN(page)) {
      page = 1;
    }
    let offset = 0;
    let response_limit = Number.MAX_SAFE_INTEGER;
    if (!req.query.response_limit) {
      // do nothing, return everything
    } else if (
      req.query.response_limit &&
      req.query.response_limit.toLowerCase() === "none"
    ) {
      response_limit = Number.MAX_SAFE_INTEGER;
    } else {
      response_limit = parseInt(req.query.response_limit || RESPONSE_LIMIT);
      offset = Math.max(page - 1, 0) * response_limit;
    }
    if (!supportedTypes.includes(objType)) {
      res.status(401).json({
        message: "Unsupported objType for getAllForType: " + objType
      });
    }
    const titlelist = await db.any(TITLES_FOR_THINGS, {
      language: as.value(getLanguage(req)),
      limit: response_limit,
      offset: offset,
      type: objType
    });
    let jtitlelist = {};
    // FIXME: this is a dumb format but it is what front-end expects.
    // Switch both (and tests) to use array of {title: , id: } pairs.
    // Also, if we're going to use {OK: true, data: []} everywhere else
    // we should use it here too.
    titlelist.forEach(function(row) {
      jtitlelist[row.title] = Number(row.thingid);
    });
    res.status(200).json(jtitlelist);
  } catch (error) {
    logError(error);
    res.status(500).json({ error: error });
  }
});

// strip off final character (assumed to be "s")
const singularLowerCase = name =>
  (name.slice(-1) === "s" ? name.slice(0, -1) : name).toLowerCase();

// just get the type, if specified
const typeFromReq = req => {
  let cat = singularLowerCase(req.query.selectedCategory || "Alls");
  return cat === "all" ? "thing" : cat;
};

const queryFileFromReq = req => {
  const featuredOnly =
    !req.query.query || (req.query.query || "").toLowerCase() === "featured";
  const resultType = (req.query.resultType || "").toLowerCase();
  let queryfile = SEARCH;
  if (featuredOnly && resultType === "map") {
    queryfile = FEATURED_MAP;
  } else if (featuredOnly) {
    queryfile = FEATURED;
  } else if (resultType == "map") {
    queryfile = SEARCH_MAP;
  }
  return queryfile;
};

const offsetFromReq = req => {
  const page = Math.max(as.number(req.query.page || 1), 1);
  return (page - 1) * limitFromReq(req);
};

const limitFromReq = req => {
  let limit = parseInt(req.query.limit || RESPONSE_LIMIT);
  const resultType = (req.query.resultType || "").toLowerCase();
  if (resultType === "map") {
    limit = 0; // return all
  }
  return limit;
};

const sortbyFromReq = req => {
  if (req.query.sortby === "post_date") {
    return "post_date";
  }
  return "updated_date";
};

const keyFacetFromReq = (req, name) => {
  let value = req.query[name];
  return value ? ` AND ${name} ='${value}' ` : "";
};

const keyListFacetFromReq = (req, name) => {
  let value = req.query[name];
  if (!value) {
    return "";
  }
  value = as.array(value.split(","));
  return ` AND ${name} && ${value} `;
};

const facetsFromReq = req => {
  if (typeFromReq(req) !== "case") {
    return "";
  }
  const keys = [
    "country",
    "scope_of_influence",
    "public_spectrum",
    "open_limited",
    "recruitment_method",
    "completeness"
  ];
  const keyLists = [
    "general_issues",
    "purposes",
    "approaches",
    "method_types",
    "tools_techniques_types",
    "organizer_types",
    "funder_types",
    "change_types"
  ];
  let keyFacets = keys.map(key => keyFacetFromReq(req, key));
  let keyListFacets = keyLists.map(key => keyListFacetFromReq(req, key));
  return keyFacets.join("") + keyListFacets.join("");
};

/**
 * @api {get} /search Search through the cases
 * @apiGroup Search
 * @apiVersion 0.1.0
 * @apiName search
 *
 * @apiParam  {String} query query term
 * @apiParam  {String} sortingMethod ('chronological' or 'alphabetical' or 'featured')
 * @apiParam  {String} selectedCategory ('All' or 'Case' or 'Method' or 'Organization' or 'News')
 *
 * @apiSuccess {Boolean} OK true if call was successful
 * @apiSuccess {String[]} errors List of error strings (when `OK` is false)
 * @apiSuccess {Object} data Mapping of country names to counts (when `OK` is true)
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "OK": true,
 *       "data": {
 *          ... (records) ...
 *       }
 *     }
 *
 */

// Should not return things that aren't displayable as SearchHits (i.e. Users...)

// two factors for search: if there is a selectedCategory then filter by it, always
// if there is no query OR the query is "featured" then return all featured items
// One further item: need an alternative search which returns only map-level items and has no pagination

router.get("/", async function(req, res) {
  const user_query = req.query.query || "";
  const parsed_query = preparse_query(user_query);
  const limit = limitFromReq(req);
  const lang = as.value(getLanguage(req));
  const type = typeFromReq(req);
  const params = parseGetParams(req, type);
  try {
    const results = await db.any(queryFileFromReq(req), {
      query: parsed_query,
      limit: limit ? limit : null, // null is no limit in SQL
      offset: offsetFromReq(req),
      language: lang,
      userId: req.user ? req.user.id : null,
      sortby: sortbyFromReq(req),
      type: type + "s",
      facets: facetsFromReq(req)
    });
    const total = Number(
      results.length ? results[0].total || results.length : 0
    );
    const searchhits = results.filter(result => result.searchmatched).length;
    const pages = Math.max(limit ? Math.ceil(total / limit) : 1, 1); // Don't divide by zero limit, don't return page 1 of 1
    results.forEach(obj => {
      // massage results for display
      if (obj.photos.length) {
        obj.photos.forEach(img => {
          if (!img.url.startsWith("http")) {
            img.url = process.env.AWS_UPLOADS_URL + encodeURIComponent(img.url);
          }
        });
      } else {
        obj.photos = [{ url: randomTexture() }];
      }
      delete obj.total;
    });
    let OK = true;
    let returnType = req.query.returns;
    switch (returnType) {
      case "json":
        return res.status(200).json({
          total,
          pages,
          searchhits,
          results,
          user_query,
          parsed_query,
          params,
          user: req.user || null
        });
      case "htmlfrag":
        return res.status(200).render("home-search", {
          total,
          pages,
          searchhits,
          results,
          params,
          user: req.user || null
        });
      case "csv":
        if (type === "thing") {
          return res.status(200).json({
            msg:
              "You can only get a csv file from cases, methods or organizations tabs."
          });
        } else {
          const file = await createCSVDataDump(type);
          return res.download(file);
        }
      case "xml":
        return res.status(500, "XML not implemented yet").render();
      case "html": // fall through
      default:
        return res.status(200).render("home-search", {
          OK,
          total,
          pages,
          searchhits,
          results,
          params,
          user: req.user || null
        });
    }
  } catch (error) {
    console.error("Error in /search: %s", error.message);
    logError(error);
    let OK = false;
    res.status(500).json({ OK, error });
  }
});

/*
 * Deprecated, use /search/?resultType=map
 *
 */
router.get("/map", async function(req, res) {
  try {
    const RESPONSE_LIMIT = 1000;
    const offset = 0;
    const cases = await db.any(LIST_MAP_CASES, {
      language: as.value(getLanguage(req)),
      limit: RESPONSE_LIMIT,
      offset: offset
    });
    const orgs = await db.any(LIST_MAP_ORGANIZATIONS, {
      language: as.value(getLanguage(req)),
      limit: RESPONSE_LIMIT,
      offset: offset
    });

    res.status(200).json({ data: { cases, orgs } });
  } catch (error) {
    logError(error);
    res.status(500).json({ error: error });
  }
});

module.exports = router;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';

import ChevronLeft from 'material-ui/svg-icons/navigation/chevron-left';
import ChevronRight from 'material-ui/svg-icons/navigation/chevron-right';
import ContentAddBox from 'material-ui/svg-icons/content/add-box';

import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import { List, ListItem } from 'material-ui/List';
import { lightGreen500 } from 'material-ui/styles/colors';

import ModuleListDialog from '../ModuleListDialog';

import s from './ModuleList.css';

class ModuleList extends Component {
  state = {
    isDialogOpen: false,
    selectedModule: {},
    startIndex: 0,
  }

  handleOpen = (module) => {
    this.setState({ isDialogOpen: true, selectedModule: module });
  }

  handleClose = () => {
    this.setState({
      isDialogOpen: false,
      selectedModule: {},
    });
  }

  handleListButtonTouch = (module) => {
    this.props.addModule(module);
  };

  handleAddToTimetable = (module) => {
    this.props.addModule(module);
    this.handleClose();
  }

  next = () => {
    const { startIndex } = this.state;
    if (startIndex + 10 > this.props.modules.length) return;
    this.setState({ startIndex: startIndex + 10 });
  }

  prev = () => {
    const { startIndex } = this.state;
    if (startIndex - 10 < 0) return;
    this.setState({ startIndex: startIndex - 10 });
  }

  isModuleInTimetable = (module) => {
    return module.code && this.props.moduleCodesInTimetable.includes(module.code);
  }

  renderListItem = (module) => {
    const inTimetable = this.isModuleInTimetable(module);

    const icon = (
      <IconButton
        disabled={inTimetable}
        onTouchTap={(e) => this.handleListButtonTouch(module, e)}
      >
        <ContentAddBox color={lightGreen500} />
      </IconButton>
    );

    return (
      <ListItem
        key={module.id}
        primaryText={module.code}
        secondaryText={module.name}
        rightIconButton={icon}
        onTouchTap={() => this.handleOpen(module)}
      />
    );
  }

  componentWillReceiveProps = (nextProps) => {
    // when we receive new modules, reset the start index to 0 to show page 1
    this.setState({ startIndex: 0 });
  }

  render() {
    const { isDialogOpen, selectedModule, startIndex } = this.state;
    const { modules, moduleCodesInTimetable } = this.props;

    // Create the list of module cells
    const listItems = modules.slice(startIndex, startIndex + 10).map(this.renderListItem);
    const inTimetable = selectedModule && this.isModuleInTimetable(selectedModule);

    return (

        <List>
          {listItems}
        </List>

          <FlatButton
            icon={<ChevronLeft />}
            onTouchTap={this.prev}
            disabled={startIndex < 10}
          />
          <FlatButton
            icon={<ChevronRight />}
            onTouchTap={this.next}
            disabled={startIndex + 10 >= this.props.modules.length}
          />

        <ModuleListDialog
          module={selectedModule}
          open={isDialogOpen}
          handleAddToTimetable={inTimetable ? null : this.handleAddToTimetable}
          handleClose={this.handleClose}
          moduleCodesInTimetable={moduleCodesInTimetable}
        />

    );
  }
}

ModuleList.propTypes = {
  modules: PropTypes.array.isRequired,
  addModule: PropTypes.func.isRequired,
  moduleCodesInTimetable: PropTypes.array.isRequired,
};

export default withStyles(s)(ModuleList);
<commit_msg>Re-add module title to module list<commit_after>import React, { Component, PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';

import ChevronLeft from 'material-ui/svg-icons/navigation/chevron-left';
import ChevronRight from 'material-ui/svg-icons/navigation/chevron-right';
import ContentAddBox from 'material-ui/svg-icons/content/add-box';

import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import { List, ListItem } from 'material-ui/List';
import { lightGreen500 } from 'material-ui/styles/colors';

import ModuleListDialog from '../ModuleListDialog';

import s from './ModuleList.css';

class ModuleList extends Component {
  state = {
    isDialogOpen: false,
    selectedModule: {},
    startIndex: 0,
  }

  handleOpen = (module) => {
    this.setState({ isDialogOpen: true, selectedModule: module });
  }

  handleClose = () => {
    this.setState({
      isDialogOpen: false,
      selectedModule: {},
    });
  }

  handleListButtonTouch = (module) => {
    this.props.addModule(module);
  };

  handleAddToTimetable = (module) => {
    this.props.addModule(module);
    this.handleClose();
  }

  next = () => {
    const { startIndex } = this.state;
    if (startIndex + 10 > this.props.modules.length) return;
    this.setState({ startIndex: startIndex + 10 });
  }

  prev = () => {
    const { startIndex } = this.state;
    if (startIndex - 10 < 0) return;
    this.setState({ startIndex: startIndex - 10 });
  }

  isModuleInTimetable = (module) => {
    return module.code && this.props.moduleCodesInTimetable.includes(module.code);
  }

  renderListItem = (module) => {
    const inTimetable = this.isModuleInTimetable(module);

    const icon = (
      <IconButton
        disabled={inTimetable}
        onTouchTap={(e) => this.handleListButtonTouch(module, e)}
      >
        <ContentAddBox color={lightGreen500} />
      </IconButton>
    );

    return (
      <ListItem
        key={module.id}
        primaryText={module.code}
        secondaryText={module.title}
        rightIconButton={icon}
        onTouchTap={() => this.handleOpen(module)}
      />
    );
  }

  componentWillReceiveProps = (nextProps) => {
    // when we receive new modules, reset the start index to 0 to show page 1
    this.setState({ startIndex: 0 });
  }

  render() {
    const { isDialogOpen, selectedModule, startIndex } = this.state;
    const { modules, moduleCodesInTimetable } = this.props;

    // Create the list of module cells
    const listItems = modules.slice(startIndex, startIndex + 10).map(this.renderListItem);
    const inTimetable = selectedModule && this.isModuleInTimetable(selectedModule);

    return (

        <List>
          {listItems}
        </List>

          <FlatButton
            icon={<ChevronLeft />}
            onTouchTap={this.prev}
            disabled={startIndex < 10}
          />
          <FlatButton
            icon={<ChevronRight />}
            onTouchTap={this.next}
            disabled={startIndex + 10 >= this.props.modules.length}
          />

        <ModuleListDialog
          module={selectedModule}
          open={isDialogOpen}
          handleAddToTimetable={inTimetable ? null : this.handleAddToTimetable}
          handleClose={this.handleClose}
          moduleCodesInTimetable={moduleCodesInTimetable}
        />

    );
  }
}

ModuleList.propTypes = {
  modules: PropTypes.array.isRequired,
  addModule: PropTypes.func.isRequired,
  moduleCodesInTimetable: PropTypes.array.isRequired,
};

export default withStyles(s)(ModuleList);
<|endoftext|>


<commit_before><commit_msg>fixes bug with previous switchovers<commit_after><|endoftext|>


<commit_before>/**
 * Created by PolarisChen on 16/9/11.
 */

import React, { Component, PropTypes } from 'react';
import cx from 'classnames';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Navigation.scss';
import Link from '../Link';

import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import Search from 'material-ui/svg-icons/action/search';
import Account from 'material-ui/svg-icons/action/account-circle';
import Star from 'material-ui/svg-icons/toggle/star';
import Person from 'material-ui/svg-icons/social/person';
import Exit from 'material-ui/svg-icons/action/exit-to-app';

const styles = {
  button: {
    color: '#FFF'
  }
}

class Navigation extends Component {

  static propTypes = {
    className: PropTypes.string,
  };

  constructor(props) {
    super(props);
  }

  handleLogout() {
    this.props.handleLogout();
  }

  render() {
    let isLogin = this.props.login;
    if (isLogin) {
      return (

          <IconButton iconStyle={styles.button} href="/search"><Search /></IconButton>
          <IconMenu
            iconButtonElement={<IconButton iconStyle={styles.button}><Account /></IconButton>}
            anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
            targetOrigin={{horizontal: 'right', vertical: 'top'}}
          >
            <MenuItem primaryText="Starred" leftIcon={<Star />} href="/starred" />
            <MenuItem primaryText="Following" leftIcon={<Person />} href="/following" />
            <Divider />
            <MenuItem primaryText="Sign out" leftIcon={<Exit />} onTouchTap={this.handleLogout.bind(this)} />
          </IconMenu>

      );
    } else {
      return (

          <Link className={s.link} to="/login">LOG IN</Link>
          <Link className={s.link} to="/register">SIGN UP WITH GITHUB</Link>

      );
    };
  }

}

export default withStyles(Navigation, s);
<commit_msg>Add feedback button<commit_after>/**
 * Created by PolarisChen on 16/9/11.
 */

import React, { Component, PropTypes } from 'react';
import cx from 'classnames';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Navigation.scss';
import Link from '../Link';

import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import Search from 'material-ui/svg-icons/action/search';
import Account from 'material-ui/svg-icons/action/account-circle';
import Star from 'material-ui/svg-icons/toggle/star';
import Person from 'material-ui/svg-icons/social/person';
import Exit from 'material-ui/svg-icons/action/exit-to-app';
import Feedback from 'material-ui/svg-icons/action/feedback';

const styles = {
  button: {
    color: '#FFF'
  }
}

class Navigation extends Component {

  static propTypes = {
    className: PropTypes.string,
  };

  constructor(props) {
    super(props);
  }

  handleLogout() {
    this.props.handleLogout();
  }

  render() {
    let isLogin = this.props.login;
    if (isLogin) {
      return (

          <IconButton iconStyle={styles.button} href="/search"><Search /></IconButton>
          <IconButton iconStyle={styles.button}><Feedback /></IconButton>
          <IconMenu
            iconButtonElement={<IconButton iconStyle={styles.button}><Account /></IconButton>}
            anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
            targetOrigin={{horizontal: 'right', vertical: 'top'}}
          >
            <MenuItem primaryText="Starred" leftIcon={<Star />} href="/starred" />
            <MenuItem primaryText="Following" leftIcon={<Person />} href="/following" />
            <Divider />
            <MenuItem primaryText="Sign out" leftIcon={<Exit />} onTouchTap={this.handleLogout.bind(this)} />
          </IconMenu>

      );
    } else {
      return (

          <Link className={s.link} to="/login">LOG IN</Link>
          <Link className={s.link} to="/register">SIGN UP WITH GITHUB</Link>

      );
    };
  }

}

export default withStyles(Navigation, s);
<|endoftext|>


<commit_before>import React from 'react';
import path from 'path';
import wcjsRenderer from './utils/wcjs-renderer';

try {
    var wcjs = require(path.join(process.cwd(), 'resources/bin/', 'WebChimera.js.node'));
} catch (e) {
    console.error('WCJS Load Error:', e);
}

const wcjsVlcArgs = [
    "--no-media-library",
    "--no-sub-autodetect-file",
    "--no-spu",
    "--no-stats",
    "--no-osd",
    "--network-caching", "3500",
    "--file-caching", "3000",
    "--no-skip-frames",
    "--no-video-title-show",
    "--disable-screensaver",
    "--no-autoscale",
    "--ipv4-timeout=86400000"
];

export
default React.createClass({
    getInitialState() {
        return {
            uri: (this.props.uri) ? this.props.uri : ''
        }
    },
    componentDidMount() {
        this.player = wcjsRenderer.init(wcjs, this.refs['wcjs-render'], wcjsVlcArgs);
        this.player.play("http://archive.org/download/CartoonClassics/Krazy_Kat_-_Keeping_Up_With_Krazy.mp4");
        window.addEventListener('resize', this.handleResize);
        this.handleResize();
    },
    componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
    },
    update() {

    },
    handleResize() {

        var canvas = this.refs['wcjs-render'];
        var container = document.body;
        var sourceAspect = canvas.width / canvas.height;
        var destAspect = container.clientWidth / container.clientHeight;
        var cond = destAspect > sourceAspect;

        if (cond) {
            canvas.style.height = "100%";
            canvas.style.width = ((container.clientHeight * sourceAspect) / container.clientWidth) * 100 + "%";
        } else {
            canvas.style.height = ((container.clientWidth / sourceAspect) / container.clientHeight) * 100 + "%";
            canvas.style.width = "100%";
        };

    },
    handleTogglePlay(toggle) {
        if (!this.player && (typeof(state) !== undefined && ((this.player.state === 4) == state))) return;
        this.player.togglePause(toggle ? true : false);
    },
    render() {
        return (

               <canvas ref="wcjs-render"/>

        );
    }
});<commit_msg>set some state params<commit_after>import React from 'react';
import path from 'path';
import wcjsRenderer from './utils/wcjs-renderer';

try {
    var wcjs = require(path.join(process.cwd(), 'resources/bin/', 'WebChimera.js.node'));
} catch (e) {
    console.error('WCJS Load Error:', e);
}

const wcjsVlcArgs = [
    "--no-media-library",
    "--no-sub-autodetect-file",
    "--no-spu",
    "--no-stats",
    "--no-osd",
    "--network-caching", "3500",
    "--file-caching", "3000",
    "--no-skip-frames",
    "--no-video-title-show",
    "--disable-screensaver",
    "--no-autoscale",
    "--ipv4-timeout=86400000"
];

export
default React.createClass({
    getInitialState() {
        return {
            mounted: false,
            paused: false,
            playing false,
            buffering: true,
            scrobbling: false
        }
    },
    componentDidMount() {
        this.player = wcjsRenderer.init(wcjs, this.refs['wcjs-render'], wcjsVlcArgs);
        this.player.play((this.props.uri) ? this.props.uri : 'http://archive.org/download/CartoonClassics/Krazy_Kat_-_Keeping_Up_With_Krazy.mp4');
        window.addEventListener('resize', this.handleResize);
        this.handleResize();
    },
    componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
    },
    update() {

    },
    handleResize() {

        var canvas = this.refs['wcjs-render'];
        var container = document.body;
        var sourceAspect = canvas.width / canvas.height;
        var destAspect = container.clientWidth / container.clientHeight;
        var cond = destAspect > sourceAspect;

        if (cond) {
            canvas.style.height = "100%";
            canvas.style.width = ((container.clientHeight * sourceAspect) / container.clientWidth) * 100 + "%";
        } else {
            canvas.style.height = ((container.clientWidth / sourceAspect) / container.clientHeight) * 100 + "%";
            canvas.style.width = "100%";
        };

    },
    handleTogglePlay(toggle) {
        if (!this.player && (typeof(state) !== undefined && ((this.player.state === 4) == state))) return;
        this.player.togglePause(toggle ? true : false);
    },
    render() {
        return (

               <canvas ref="wcjs-render"/>

        );
    }
});<|endoftext|>


<commit_before>import './SectionTable.less';
import React from 'react';
import PropTypes from 'prop-types';
import { TABLE_CELL_TYPE, DEFAULT_MAX_LENGTH } from '../../constants/Constants';
import isArray from 'lodash/isArray';
import map from 'lodash/map';
import isString from 'lodash/isString';
import truncate from 'lodash/truncate';
import isObjectLike from 'lodash/isObjectLike';

const SectionTable = ({ columns, readableHeaders, data, classes, style, title, titleStyle }) => {
  let tableData = data || [];

  if (isString(data)) {
    try {
      tableData = JSON.parse(data);
    } catch (ignored) {
      return
Error parsing table
;
    }
  }

  if (!isArray(tableData) && isObjectLike(tableData)) {
    tableData = tableData.data || tableData.iocs || tableData.messages;
  }

  let readyColumns = columns;
  if (!isArray(columns) && isArray(tableData)) {
    const headerKeys = {};
    tableData.forEach((val, i) => {
      for (const key in tableData[i]) { // eslint-disable-line no-restricted-syntax
        if (headerKeys[key] !== key) {
          headerKeys[key] = key;
        }
      }
    });

    readyColumns = Object.keys(headerKeys);
  }

  let tableBody;
  if (isArray(readyColumns)) {
    tableBody = (

            {readyColumns.map((col) => {
              return {!col.hidden && ((readableHeaders && readableHeaders[col]) || col)}	;
            })}

        {tableData.map((row, i) =>

            {readyColumns.map((col, j) =>
              (() => {
                const key = col.key || col;
                const cell = row[key] || readableHeaders && row[readableHeaders[key]];

                let cellToRender = '';
                if (cell) {
                  switch (cell.type) {
                    case TABLE_CELL_TYPE.image:
                      cellToRender = (

                      );
                      break;
                    default:
                      cellToRender = truncate(cell, { length: DEFAULT_MAX_LENGTH });
                  }
                }
                return {cellToRender}	;
              })()
            )}

        )}

    );
  } else {
    tableBody = (

          {map(tableData, (val, key) => (

                {key}

              {truncate(val, { length: DEFAULT_MAX_LENGTH })}

          ))}

    );
  }

  return (

      {title &&
{title}
}
      {tableBody}

  );
};
SectionTable.propTypes = {
  columns: PropTypes.array,
  readableHeaders: PropTypes.object,
  data: PropTypes.oneOfType([
    PropTypes.array,
    PropTypes.object,
    PropTypes.string
  ]),
  classes: PropTypes.string,
  style: PropTypes.object,
  title: PropTypes.string,
  titleStyle: PropTypes.object
};

export default SectionTable;
<commit_msg>No results found (#68)<commit_after>import './SectionTable.less';
import React from 'react';
import PropTypes from 'prop-types';
import { TABLE_CELL_TYPE, DEFAULT_MAX_LENGTH } from '../../constants/Constants';
import isArray from 'lodash/isArray';
import map from 'lodash/map';
import isString from 'lodash/isString';
import truncate from 'lodash/truncate';
import isObjectLike from 'lodash/isObjectLike';

const SectionTable = ({ columns, readableHeaders, data, classes, style, title, titleStyle }) => {
  let tableData = data || [];

  if (isString(data)) {
    try {
      tableData = JSON.parse(data);
    } catch (ignored) {
      return
Error parsing table
;
    }
  }

  if (!isArray(tableData) && isObjectLike(tableData)) {
    tableData = tableData.data || tableData.iocs || tableData.messages;
  }

  let readyColumns = columns;
  if (!isArray(columns) && isArray(tableData)) {
    const headerKeys = {};
    tableData.forEach((val, i) => {
      for (const key in tableData[i]) { // eslint-disable-line no-restricted-syntax
        if (headerKeys[key] !== key) {
          headerKeys[key] = key;
        }
      }
    });

    readyColumns = Object.keys(headerKeys);
  }

  let tableBody;
  if (isArray(readyColumns)) {
    tableBody = (

            {readyColumns.map((col) => {
              return {!col.hidden && ((readableHeaders && readableHeaders[col]) || col)}	;
            })}

        {tableData.length ? tableData.map((row, i) =>

            {readyColumns.map((col, j) =>
              (() => {
                const key = col.key || col;
                const cell = row[key] || readableHeaders && row[readableHeaders[key]];

                let cellToRender = '';
                if (cell) {
                  switch (cell.type) {
                    case TABLE_CELL_TYPE.image:
                      cellToRender = (

                      );
                      break;
                    default:
                      cellToRender = truncate(cell, { length: DEFAULT_MAX_LENGTH });
                  }
                }
                return {cellToRender}	;
              })()
            )}

        )
          :
          No results found
        }

    );
  } else {
    tableBody = (

          {map(tableData, (val, key) => (

                {key}

              {truncate(val, { length: DEFAULT_MAX_LENGTH })}

          ))}

    );
  }

  return (

      {title &&
{title}
}
      {tableBody}

  );
};
SectionTable.propTypes = {
  columns: PropTypes.array,
  readableHeaders: PropTypes.object,
  data: PropTypes.oneOfType([
    PropTypes.array,
    PropTypes.object,
    PropTypes.string
  ]),
  classes: PropTypes.string,
  style: PropTypes.object,
  title: PropTypes.string,
  titleStyle: PropTypes.object
};

export default SectionTable;
<|endoftext|>


<commit_before>import React, {Component, Children} from 'react';
import getCaretCoords from 'textarea-caret';
import MentionMenu from "./MentionMenu";

const getMenuProps = (keystrokeTriggered, children) => {
  const child = Array.isArray(children) ? children[keystrokeTriggered] : children;
  return child ? child.props : {};
};

class MentionWrapper extends Component {
  constructor(props) {
    super(props);
    this.state = {
      child: {},
      options: []
    };
    const {children} = props;
    this.triggers = Children.map(children, (child) => child.props.trigger);
  }

  makeOptions = async(query, resolve) => {
    const options = await resolve(query);
    this.setState({
      options
    })
  };

  maybeMention() {
    const textBeforeCaret = this.ref.value.slice(0, this.ref.selectionStart);
    const triggerIdx = textBeforeCaret.lastIndexOf(' ') + 1;
    const maybeTrigger = textBeforeCaret[triggerIdx];
    const keystrokeTriggered = this.triggers.indexOf(maybeTrigger);
    if (keystrokeTriggered !== -1) {
      const query = textBeforeCaret.slice(triggerIdx + 1);
      const coords = getCaretCoords(this.ref, this.ref.selectionStart);
      const {top, left} = this.ref.getBoundingClientRect();
      const child = getMenuProps(keystrokeTriggered, this.props.children);
      const {resolve} = child;
      this.makeOptions(query, resolve);
      // that stupid bug where the caret moves to the end happens unless we do it next tick
      setTimeout(() => {
        this.setState({
          active: 0,
          child,
          left: coords.left + left + this.ref.scrollLeft,
          triggerIdx,
          top: coords.top + top + coords.height - this.ref.scrollTop
        })
      }, 0)
    } else {
      this.closeMenu();
    }
  }

  closeMenu() {
    setTimeout(() => {
      this.setState({
        child: {},
        options: [],
        left: undefined,
        top: undefined,
        triggerIdx: undefined
      })
    }, 0)
  }

  handleInput = (e) => {
    const {onInput} = this.props;
    if (onInput) {
      onInput(e);
    }
    this.maybeMention();
  };

  inputRef = (c) => {
    this.ref = c;
    const {getRef} = this.props;
    if (getRef) {
      getRef(c);
    }
  };

  handleKeyDown = (e) => {
    const {options, active, triggerIdx} = this.state;
    if (triggerIdx !== undefined) {
      if (e.key === 'ArrowDown') {
        this.setState({
          active: Math.min(active + 1, options.length - 1)
        })
        e.preventDefault();
      } else if (e.key === 'ArrowUp') {
        this.setState({
          active: Math.max(active - 1, 0)
        })
        e.preventDefault();
      } else if (e.key === 'Tab' || e.key === 'Enter') {
        const preMention = this.ref.value.substr(0, triggerIdx + 1);
        const mention = options[active].value + ' ';
        const postMention = this.ref.value.substr(this.ref.selectionStart);
        this.ref.value = `${preMention}${mention}${postMention}`
        const {onChange} = this.props;
        if (onChange) {
          onChange(e);
        }
        const caretPosition = this.ref.value.length - postMention.length;
        this.ref.setSelectionRange(caretPosition, caretPosition);
        e.preventDefault();
        this.closeMenu();
      }
    }
  }

  render() {
    const {component, getRef, ...inputProps} = this.props;
    const {active, child, left, top, options} = this.state;
    const {item, className, style} = child;
    return (

        <textarea {...inputProps} ref={this.inputRef} onInput={this.handleInput} onKeyDown={this.handleKeyDown}/>
        {top !== undefined && <MentionMenu
          active={active}
          className={className}
          left={left}
          isOpen={Boolean(options.length)}
          item={item}
          options={options}
          style={style}
          top={top}
        />}

    );
  }
}

export default MentionWrapper;
<commit_msg>call parent onKeyDown if provided<commit_after>import React, {Component, Children} from 'react';
import getCaretCoords from 'textarea-caret';
import MentionMenu from "./MentionMenu";

const getMenuProps = (keystrokeTriggered, children) => {
  const child = Array.isArray(children) ? children[keystrokeTriggered] : children;
  return child ? child.props : {};
};

class MentionWrapper extends Component {
  constructor(props) {
    super(props);
    this.state = {
      child: {},
      options: []
    };
    const {children} = props;
    this.triggers = Children.map(children, (child) => child.props.trigger);
  }

  makeOptions = async(query, resolve) => {
    const options = await resolve(query);
    this.setState({
      options
    })
  };

  maybeMention() {
    const textBeforeCaret = this.ref.value.slice(0, this.ref.selectionStart);
    const triggerIdx = textBeforeCaret.lastIndexOf(' ') + 1;
    const maybeTrigger = textBeforeCaret[triggerIdx];
    const keystrokeTriggered = this.triggers.indexOf(maybeTrigger);
    if (keystrokeTriggered !== -1) {
      const query = textBeforeCaret.slice(triggerIdx + 1);
      const coords = getCaretCoords(this.ref, this.ref.selectionStart);
      const {top, left} = this.ref.getBoundingClientRect();
      const child = getMenuProps(keystrokeTriggered, this.props.children);
      const {resolve} = child;
      this.makeOptions(query, resolve);
      // that stupid bug where the caret moves to the end happens unless we do it next tick
      setTimeout(() => {
        this.setState({
          active: 0,
          child,
          left: coords.left + left + this.ref.scrollLeft,
          triggerIdx,
          top: coords.top + top + coords.height - this.ref.scrollTop
        })
      }, 0)
    } else {
      this.closeMenu();
    }
  }

  closeMenu() {
    setTimeout(() => {
      this.setState({
        child: {},
        options: [],
        left: undefined,
        top: undefined,
        triggerIdx: undefined
      })
    }, 0)
  }

  handleInput = (e) => {
    this.maybeMention();
    const {onInput} = this.props;
    if (onInput) {
      onInput(e);
    }
  };

  inputRef = (c) => {
    this.ref = c;
    const {getRef} = this.props;
    if (getRef) {
      getRef(c);
    }
  };

  handleKeyDown = (e) => {
    const {options, active, triggerIdx} = this.state;
    if (triggerIdx !== undefined) {
      if (e.key === 'ArrowDown') {
        this.setState({
          active: Math.min(active + 1, options.length - 1)
        })
        e.preventDefault();
      } else if (e.key === 'ArrowUp') {
        this.setState({
          active: Math.max(active - 1, 0)
        })
        e.preventDefault();
      } else if (e.key === 'Tab' || e.key === 'Enter') {
        const preMention = this.ref.value.substr(0, triggerIdx + 1);
        const mention = options[active].value + ' ';
        const postMention = this.ref.value.substr(this.ref.selectionStart);
        this.ref.value = `${preMention}${mention}${postMention}`
        const {onChange} = this.props;
        if (onChange) {
          onChange(e);
        }
        const caretPosition = this.ref.value.length - postMention.length;
        this.ref.setSelectionRange(caretPosition, caretPosition);
        e.preventDefault();
        this.closeMenu();
      }
    }
    const {onKeyDown} = this.props;
    if (onKeyDown) {
      onKeyDown(e);
    }
  }

  render() {
    const {component, getRef, ...inputProps} = this.props;
    const {active, child, left, top, options} = this.state;
    const {item, className, style} = child;
    return (

        <textarea {...inputProps} ref={this.inputRef} onInput={this.handleInput} onKeyDown={this.handleKeyDown}/>
        {top !== undefined && <MentionMenu
          active={active}
          className={className}
          left={left}
          isOpen={Boolean(options.length)}
          item={item}
          options={options}
          style={style}
          top={top}
        />}

    );
  }
}

export default MentionWrapper;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import cx from 'classnames';
import s from './Warranties.scss';
import withStyles from '../../decorators/withStyles';
import { fields, fields2 } from './FormFields';
import WarrantiesStore from '../../stores/WarrantiesStore';
import WarrantiesActions from '../../actions/WarrantiesActions';
import Form from '../Form/Form';
import connectToStores from 'alt-utils/lib/connectToStores';

@withStyles(s)
@connectToStores
class Warranties extends Component {

	static propTypes = {
		className: PropTypes.string,
		enabled: PropTypes.bool,
		inputs: PropTypes.object,
		success: PropTypes.bool,
		error: PropTypes.string,
	};

	constructor() {
		super();
		this.submit = this.submit.bind(this);
		this.enabled = false;
	}

	componentWillReceiveProps() {
		for (let i = 0; i < fields.length; i++) {
			if (!this.props.inputs[fields[i].name] || this.props.inputs[fields[i].name] === '') {
				return;
			}
		}
		for (let i = 0; i < fields2.length; i++) {
			if (!this.props.inputs[fields2[i].name] || this.props.inputs[fields2[i].name] === '') {
				return;
			}
		}
		this.enabled = true;
	}

	static getStores() {
		return [WarrantiesStore];
	}

	static getPropsFromStores() {
		return WarrantiesStore.getState();
	}

	getForm() {
		return (<Form fields={fields} />);
	}

	getForm2() {
		return (<Form fields={fields2} />);
	}

	submit(event) {
		event.preventDefault();
		WarrantiesActions.postData(this.props.inputs);
	}

	render() {
		return (

ONLINE WARRANTIES

In order to accept a warranty all ARIES products must have a warranty card completed with proof of purchase.

							<form name="contactForm" role="form" noValidate>

STEP 1 OF 4: PROVIDE PURCHASER INFORMATION

Please complete all fields.

									{this.getForm()}

STEP 2 OF 4: PRODUCT REGISTRATION INFORMATION

										Find your registration sticker:
										Bull Bar |
										Grill Guard |
										Side Bar

									{this.getForm2()}

STEP 3 OF 4: CLICK SUBMIT BELOW.

Click this button:

									<button type="submit" className={cx('btn btn-primary', s.submit)} disabled={!this.enabled} onClick={this.submit}>SUBMIT</button>
									{this.props.success ?
Success! Home
 : null}
									{this.props.error ?
Error: {this.props.error}
 : null}

STEP 4 OF 4: FAX, MAIL, OR EMAIL PROOF OF PURCHASE TO THE FOLLOWING LOCATION TO COMPLETE REGISTRATION.

ATTN: WARRANTY DEPT.

ARIES AUTOMOTIVE

2611 Regent Boulevard

Suite 300

DFW Airport, TX 75261 USA

Phone: (888) 635-9824

Fax: (972) 352-2617

Email: customerservice@ariesautomotive.com

Warranty registration is valid within 90 days from the date of purchase as stated on the proof of purchase receipt.

							</form>

		);
	}

}

export default Warranties;
<commit_msg>makes warranties success message consistent with contact form<commit_after>import React, { Component, PropTypes } from 'react';
import cx from 'classnames';
import s from './Warranties.scss';
import withStyles from '../../decorators/withStyles';
import { fields, fields2 } from './FormFields';
import WarrantiesStore from '../../stores/WarrantiesStore';
import WarrantiesActions from '../../actions/WarrantiesActions';
import Form from '../Form/Form';
import connectToStores from 'alt-utils/lib/connectToStores';

@withStyles(s)
@connectToStores
class Warranties extends Component {

	static propTypes = {
		className: PropTypes.string,
		enabled: PropTypes.bool,
		inputs: PropTypes.object,
		success: PropTypes.bool,
		error: PropTypes.string,
	};

	constructor() {
		super();
		this.submit = this.submit.bind(this);
		this.renderSuccess = this.renderSuccess.bind(this);
		this.enabled = false;
	}

	componentWillReceiveProps() {
		for (let i = 0; i < fields.length; i++) {
			if (!this.props.inputs[fields[i].name] || this.props.inputs[fields[i].name] === '') {
				return;
			}
		}
		for (let i = 0; i < fields2.length; i++) {
			if (!this.props.inputs[fields2[i].name] || this.props.inputs[fields2[i].name] === '') {
				return;
			}
		}
		this.enabled = true;
	}

	static getStores() {
		return [WarrantiesStore];
	}

	static getPropsFromStores() {
		return WarrantiesStore.getState();
	}

	getForm() {
		return (<Form fields={fields} />);
	}

	getForm2() {
		return (<Form fields={fields2} />);
	}

	submit(event) {
		event.preventDefault();
		WarrantiesActions.postData(this.props.inputs);
	}

	renderSuccess() {
		if (this.props.error) {
			return (

					Error: {this.props.error.message}

);
		}
		return (

				Thank you. We have received your request.

);
	}

	render() {
		return (

ONLINE WARRANTIES

In order to accept a warranty all ARIES products must have a warranty card completed with proof of purchase.

							<form name="contactForm" role="form" noValidate>

STEP 1 OF 4: PROVIDE PURCHASER INFORMATION

Please complete all fields.

									{this.getForm()}

STEP 2 OF 4: PRODUCT REGISTRATION INFORMATION

										Find your registration sticker:
										Bull Bar |
										Grill Guard |
										Side Bar

									{this.getForm2()}

STEP 3 OF 4: CLICK SUBMIT BELOW.

Click this button:

									<button type="submit" className={cx('btn btn-primary', s.submit)} disabled={!this.enabled} onClick={this.submit}>SUBMIT</button>
									{(this.props.success || this.props.error) ? this.renderSuccess() : null}

STEP 4 OF 4: FAX, MAIL, OR EMAIL PROOF OF PURCHASE TO THE FOLLOWING LOCATION TO COMPLETE REGISTRATION.

ATTN: WARRANTY DEPT.

ARIES AUTOMOTIVE

2611 Regent Boulevard

Suite 300

DFW Airport, TX 75261 USA

Phone: (888) 635-9824

Fax: (972) 352-2617

Email: customerservice@ariesautomotive.com

Warranty registration is valid within 90 days from the date of purchase as stated on the proof of purchase receipt.

							</form>

		);
	}

}

export default Warranties;
<|endoftext|>


<commit_before>import React from 'react';
import { text, select, boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { Link as RouterLink } from 'react-router';
import OptionsHelper from '../../utils/helpers/options-helper';
import Button from '.';

const getIconKnobs = () => {
  const defaultPosition = Button.defaultProps.iconPosition;
  const hasIcon = boolean('has icon', false);

  return {
    iconType: hasIcon ? select('iconType', [...OptionsHelper.icons, ''], '') : undefined,
    iconPosition: hasIcon ? select('iconPosition', [...OptionsHelper.buttonIconPositions], defaultPosition) : undefined
  };
};

const getKnobs = () => {
  const size = select('size', OptionsHelper.sizesRestricted, Button.defaultProps.size);
  return {
    children: text('children', 'Example Button'),
    disabled: boolean('disabled', Button.defaultProps.disabled),
    onClick: ev => action('click')(ev),
    size,
    subtext: (size === OptionsHelper.sizesRestricted[2]) ? text('subtext', Button.defaultProps.subtext) : undefined,
    buttonType: select('buttonType', OptionsHelper.buttonTypes, Button.defaultProps.as),
    href: text('href'),
    to: text('to'),
    destructive: boolean('destructive', false),
    ...getIconKnobs()
  };
};

export default {
  title: 'Button',
  component: Button,
  parameters: {
    info: {
      disable: true
    },
    docs: {
      disable: true
    }
  }
};

export const knobs = () => {
  const props = getKnobs();
  const { children } = props; // eslint-disable-line react/prop-types
  return (
    <Button { ...props } renderRouterLink={ routerProps => <RouterLink { ...routerProps } /> }>{ children }</Button>
  );
};

export const asASibling = () => {
  const props = getKnobs();
  const { children } = props; // eslint-disable-line react/prop-types
  return (

      <Button { ...props } renderRouterLink={ routerProps => <RouterLink { ...routerProps } /> }>{ children }</Button>
      <Button { ...props } renderRouterLink={ routerProps => <RouterLink { ...routerProps } /> }>{ children }</Button>

  );
};

const generateButtons = buttonType => () => {
  return (
    <>
      {OptionsHelper.buttonIconPositions.map(iconPosition => (
        ['', ...OptionsHelper.icons].map((iconType) => {
          const props = { iconPosition, buttonType, iconType };
          return (

              {OptionsHelper.sizesRestricted.map(size => (
                <>
                  <Button
                    size={ size }
                    { ...props }
                  >{size}
                  </Button>

                  {size === 'large' && (
                    <Button
                      size={ size }
                      subtext='line two'
                      { ...props }
                    >{size}
                    </Button>
                  )}
                </>
              ))}

              {OptionsHelper.sizesRestricted.map(size => (
                <>
                  <Button
                    size={ size }
                    destructive
                    { ...props }
                  >{size}
                  </Button>

                  {size === 'large' && (
                    <Button
                      size={ size }
                      destructive
                      subtext='line two'
                      { ...props }
                    >{size}
                    </Button>
                  )}
                </>
              ))}

              {OptionsHelper.sizesRestricted.map(size => (
                <>
                  <Button
                    size={ size }
                    disabled
                    { ...props }
                  >{size}
                  </Button>

                  {size === 'large' && (
                    <Button
                      size={ size }
                      disabled
                      subtext='line two'
                      { ...props }
                    >{size}
                    </Button>
                  )}
                </>
              ))}

          );
        })
      ))}
    </>
  );
};

export const primaryButtons = generateButtons('primary');
export const secondaryButtons = generateButtons('secondary');
export const tertiaryButtons = generateButtons('tertiary');
export const dashedButtons = generateButtons('dashed');
export const darkBackgroundButtons = generateButtons('darkBackground');
<commit_msg>chore: separate button story for regression tests<commit_after>import React from 'react';
import { text, select, boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { Link as RouterLink } from 'react-router';
import OptionsHelper from '../../utils/helpers/options-helper';
import Button from '.';

const getIconKnobs = () => {
  const defaultPosition = Button.defaultProps.iconPosition;
  const hasIcon = boolean('has icon', false);

  return {
    iconType: hasIcon ? select('iconType', [...OptionsHelper.icons, ''], '') : undefined,
    iconPosition: hasIcon ? select('iconPosition', [...OptionsHelper.buttonIconPositions], defaultPosition) : undefined
  };
};

const getKnobs = () => {
  const size = select('size', OptionsHelper.sizesRestricted, Button.defaultProps.size);
  return {
    children: text('children', 'Example Button'),
    disabled: boolean('disabled', Button.defaultProps.disabled),
    onClick: ev => action('click')(ev),
    size,
    subtext: (size === OptionsHelper.sizesRestricted[2]) ? text('subtext', Button.defaultProps.subtext) : undefined,
    buttonType: select('buttonType', OptionsHelper.buttonTypes, Button.defaultProps.as),
    href: text('href'),
    to: text('to'),
    destructive: boolean('destructive', false),
    ...getIconKnobs()
  };
};

export default {
  title: 'Button',
  component: Button,
  parameters: {
    info: {
      disable: true
    },
    docs: {
      disable: true
    }
  }
};

export const knobs = () => {
  const props = getKnobs();
  const { children } = props; // eslint-disable-line react/prop-types
  return (
    <Button { ...props } renderRouterLink={ routerProps => <RouterLink { ...routerProps } /> }>{ children }</Button>
  );
};

export const asASibling = () => {
  const props = getKnobs();
  const { children } = props; // eslint-disable-line react/prop-types
  return (

      <Button { ...props } renderRouterLink={ routerProps => <RouterLink { ...routerProps } /> }>{ children }</Button>
      <Button { ...props } renderRouterLink={ routerProps => <RouterLink { ...routerProps } /> }>{ children }</Button>

  );
};

const generateButtons = (buttonType, iconPosition) => () => {
  return (
    <>
      {
        ['', ...OptionsHelper.icons].map((iconType) => {
          const props = { iconPosition, buttonType, iconType };
          return (

              {OptionsHelper.sizesRestricted.map((size, index) => (
                <>
                  <Button
                    key={ String(index) }
                    size={ size }
                    { ...props }
                  >
                    {size}
                  </Button>

                  {size === 'large' && (
                    <Button
                      key={ String(index) }
                      size={ size }
                      subtext='line two'
                      { ...props }
                    >
                      {size}
                    </Button>
                  )}
                </>
              ))}

              {OptionsHelper.sizesRestricted.map((size, index) => (
                <>
                  <Button
                    key={ String(index) }
                    size={ size }
                    destructive
                    { ...props }
                  >
                    {size}
                  </Button>

                  {size === 'large' && (
                    <Button
                      key={ String(index) }
                      size={ size }
                      destructive
                      subtext='line two'
                      { ...props }
                    >
                      {size}
                    </Button>
                  )}
                </>
              ))}

              {OptionsHelper.sizesRestricted.map((size, index) => (
                <>
                  <Button
                    key={ String(index) }
                    size={ size }
                    disabled
                    { ...props }
                  >
                    {size}
                  </Button>

                  {size === 'large' && (
                    <Button
                      key={ String(index) }
                      size={ size }
                      disabled
                      subtext='line two'
                      { ...props }
                    >
                      {size}
                    </Button>
                  )}
                </>
              ))}

          );
        })
      }
    </>
  );
};

export const primaryButtonsIconsBefore = generateButtons('primary', 'before');
export const primaryButtonsIconsAfter = generateButtons('primary', 'after');
export const secondaryButtonsIconsBefore = generateButtons('secondary', 'before');
export const secondaryButtonsIconsAfter = generateButtons('secondary', 'after');
export const tertiaryButtonsIconsBefore = generateButtons('tertiary', 'before');
export const tertiaryButtonsIconsAfter = generateButtons('tertiary', 'after');
export const dashedButtonsIconsBefore = generateButtons('dashed', 'before');
export const dashedButtonsIconsAfter = generateButtons('dashed', 'after');
export const darkBackgroundButtonsIconsBefore = generateButtons('darkBackground', 'before');
export const darkBackgroundButtonsIconsAfter = generateButtons('darkBackground', 'after');
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedHTMLMessage } from 'react-intl';

var Footer = React.createClass({
  contextTypes: {
    intl: React.PropTypes.object
  },
  render: function() {
    var shareLink = "/" + this.context.intl.locale + "/net-neutrality/share";
    return (

              {this.context.intl.formatMessage({id: 'contact_us'})}

              {this.context.intl.formatMessage({id: 'share_this_page'})}

              {this.context.intl.formatMessage({id: 'follow'})}

              {this.context.intl.formatMessage({id: 'legal'})}

              {this.context.intl.formatMessage({id: 'privacy'})}

              {this.context.intl.formatMessage({id: 'donate'})}

              <FormattedHTMLMessage
                id='footer_description'
              />

    );
  }
});

module.exports = Footer;
<commit_msg>footer links<commit_after>import React from 'react';
import { FormattedHTMLMessage } from 'react-intl';

var Footer = React.createClass({
  contextTypes: {
    intl: React.PropTypes.object
  },
  render: function() {
    var shareLink = "/" + this.context.intl.locale + "/net-neutrality/share";
    return (

              Contact Us

              Share this page

              Follow

              Legal

              Privacy

              Donate

              <FormattedHTMLMessage
                id='footer_description'
              />

    );
  }
});

module.exports = Footer;
<|endoftext|>


<commit_before>var bind = require('../utils/bind');
var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

var trackedControlsUtils = require('../utils/tracked-controls');
var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup;
var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged;
var onButtonEvent = trackedControlsUtils.onButtonEvent;

var isOculusBrowser = require('../utils/').device.isOculusBrowser();
var isWebXRAvailable = require('../utils/').device.isWebXRAvailable;

var GAMEPAD_ID_WEBXR = 'oculus-touch';
var GAMEPAD_ID_WEBVR = 'Oculus Touch';

// Prefix for Gen1 and Gen2 Oculus Touch Controllers.
var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR;

// First generation model URL.
var TOUCH_CONTROLLER_MODEL_BASE_URL = 'https://cdn.aframe.io/controllers/oculus/oculus-touch-controller-';
// For now the generation 2 model is the same as the original until a new one is prepared for upload.
var TOUCH_GEN2_CONTROLLER_MODEL_BASE_URL = TOUCH_CONTROLLER_MODEL_BASE_URL;

var OCULUS_TOUCH_WEBVR = {
  left: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'left.gltf',
    rayOrigin: {origin: {x: 0.008, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(-0.005, 0.003, -0.055),
    modelPivotRotation: new THREE.Euler(0, 0, 0)
  },
  right: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'right.gltf',
    rayOrigin: {origin: {x: -0.008, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(0.005, 0.003, -0.055),
    modelPivotRotation: new THREE.Euler(0, 0, 0)
  }
};

var OCULUS_TOUCH_WEBXR = {
  left: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'left.gltf',
    rayOrigin: {origin: {x: 0.002, y: -0.005, z: -0.03}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(-0.005, 0.036, -0.037),
    modelPivotRotation: new THREE.Euler(Math.PI / 4.5, 0, 0)
  },
  right: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'right.gltf',
    rayOrigin: {origin: {x: -0.002, y: -0.005, z: -0.03}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(0.005, 0.036, -0.037),
    modelPivotRotation: new THREE.Euler(Math.PI / 4.5, 0, 0)
  }
};

var OCULUS_TOUCH_CONFIG = isWebXRAvailable ? OCULUS_TOUCH_WEBXR : OCULUS_TOUCH_WEBVR;

var CONTROLLER_DEFAULT = 'oculus-touch';
var CONTROLLER_PROPERTIES = {
  'oculus-touch': OCULUS_TOUCH_CONFIG,
  'oculus-touch-v2': {
    left: {
      modelUrl: TOUCH_GEN2_CONTROLLER_MODEL_BASE_URL + 'gen2-left.gltf',
      rayOrigin: {origin: {x: -0.01, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
      modelPivotOffset: new THREE.Vector3(0, 0, 0),
      modelPivotRotation: new THREE.Euler(-Math.PI / 4, 0, 0)
    },
    right: {
      modelUrl: TOUCH_GEN2_CONTROLLER_MODEL_BASE_URL + 'gen2-right.gltf',
      rayOrigin: {origin: {x: 0.01, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
      modelPivotOffset: new THREE.Vector3(0, 0, 0),
      modelPivotRotation: new THREE.Euler(-Math.PI / 4, 0, 0)
    }
  }
};

/**
 * Button indices:
 * 0 - thumbstick (which has separate axismove / thumbstickmoved events)
 * 1 - trigger (with analog value, which goes up to 1)
 * 2 - grip (with analog value, which goes up to 1)
 * 3 - X (left) or A (right)
 * 4 - Y (left) or B (right)
 * 5 - surface (touch only)
 */
var INPUT_MAPPING_WEBVR = {
  left: {
    axes: {thumbstick: [0, 1]},
    buttons: ['thumbstick', 'trigger', 'grip', 'xbutton', 'ybutton', 'surface']
  },
  right: {
    axes: {thumbstick: [0, 1]},
    buttons: ['thumbstick', 'trigger', 'grip', 'abutton', 'bbutton', 'surface']
  }
};

/**
 * Button indices:
 * 0 - trigger
 * 1 - grip
 * 2 - none
 * 3 - thumbstick
 * 4 - X or A button
 * 5 - Y or B button
 * 6 - surface
 *
 * Axis:
 * 0 - none
 * 1 - none
 * 2 - thumbstick
 * 3 - thumbstick
 * Reference: https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/oculus/oculus-touch.json
 */
var INPUT_MAPPING_WEBXR = {
  left: {
    axes: {thumbstick: [2, 3]},
    buttons: ['trigger', 'grip', 'none', 'thumbstick', 'xbutton', 'ybutton', 'surface']
  },
  right: {
    axes: {thumbstick: [2, 3]},
    buttons: ['trigger', 'grip', 'none', 'thumbstick', 'abutton', 'bbutton', 'surface']
  }
};

var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR;

/**
 * Oculus Touch controls.
 * Interface with Oculus Touch controllers and map Gamepad events to
 * controller buttons: thumbstick, trigger, grip, xbutton, ybutton, surface
 * Load a controller model and highlight the pressed buttons.
 */
module.exports.Component = registerComponent('oculus-touch-controls', {
  schema: {
    hand: {default: 'left'},
    buttonColor: {type: 'color', default: '#999'},  // Off-white.
    buttonTouchColor: {type: 'color', default: '#8AB'},
    buttonHighlightColor: {type: 'color', default: '#2DF'},  // Light blue.
    model: {default: true},
    controllerType: {default: 'auto', oneOf: ['auto', 'oculus-touch', 'oculus-touch-v2']},
    orientationOffset: {type: 'vec3', default: {x: 43, y: 0, z: 0}}
  },

  mapping: INPUT_MAPPING,

  bindMethods: function () {
    this.onModelLoaded = bind(this.onModelLoaded, this);
    this.onControllersUpdate = bind(this.onControllersUpdate, this);
    this.checkIfControllerPresent = bind(this.checkIfControllerPresent, this);
    this.onAxisMoved = bind(this.onAxisMoved, this);
  },

  init: function () {
    var self = this;
    this.onButtonChanged = bind(this.onButtonChanged, this);
    this.onButtonDown = function (evt) { onButtonEvent(evt.detail.id, 'down', self, self.data.hand); };
    this.onButtonUp = function (evt) { onButtonEvent(evt.detail.id, 'up', self, self.data.hand); };
    this.onButtonTouchStart = function (evt) { onButtonEvent(evt.detail.id, 'touchstart', self, self.data.hand); };
    this.onButtonTouchEnd = function (evt) { onButtonEvent(evt.detail.id, 'touchend', self, self.data.hand); };
    this.controllerPresent = false;
    this.lastControllerCheck = 0;
    this.previousButtonValues = {};
    this.rendererSystem = this.el.sceneEl.systems.renderer;
    this.bindMethods();
  },

  addEventListeners: function () {
    var el = this.el;
    el.addEventListener('buttonchanged', this.onButtonChanged);
    el.addEventListener('buttondown', this.onButtonDown);
    el.addEventListener('buttonup', this.onButtonUp);
    el.addEventListener('touchstart', this.onButtonTouchStart);
    el.addEventListener('touchend', this.onButtonTouchEnd);
    el.addEventListener('axismove', this.onAxisMoved);
    el.addEventListener('model-loaded', this.onModelLoaded);
    this.controllerEventsActive = true;
  },

  removeEventListeners: function () {
    var el = this.el;
    el.removeEventListener('buttonchanged', this.onButtonChanged);
    el.removeEventListener('buttondown', this.onButtonDown);
    el.removeEventListener('buttonup', this.onButtonUp);
    el.removeEventListener('touchstart', this.onButtonTouchStart);
    el.removeEventListener('touchend', this.onButtonTouchEnd);
    el.removeEventListener('axismove', this.onAxisMoved);
    el.removeEventListener('model-loaded', this.onModelLoaded);
    this.controllerEventsActive = false;
  },

  checkIfControllerPresent: function () {
    checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, {
      hand: this.data.hand
    });
  },

  play: function () {
    this.checkIfControllerPresent();
    this.addControllersUpdateListener();
  },

  pause: function () {
    this.removeEventListeners();
    this.removeControllersUpdateListener();
  },

  loadModel: function () {
    var data = this.data;
    if (!data.model) { return; }

    // Set the controller display model based on the data passed in.
    this.displayModel = CONTROLLER_PROPERTIES[data.controllerType] || CONTROLLER_PROPERTIES[CONTROLLER_DEFAULT];
    // If the developer is asking for auto-detection, see if the displayName can be retrieved to identify the specific unit.
    // This only works for WebVR currently.
    if (data.controllerType === 'auto') {
      var trackedControlsSystem = this.el.sceneEl.systems['tracked-controls-webvr'];
      if (trackedControlsSystem && trackedControlsSystem.vrDisplay) {
        var displayName = trackedControlsSystem.vrDisplay.displayName;
        // The Oculus Quest uses the updated generation 2 inside-out tracked controllers so update the displayModel.
        if (/^Oculus Quest$/.test(displayName)) {
          this.displayModel = CONTROLLER_PROPERTIES['oculus-touch-v2'];
        }
      }
      if (isOculusBrowser) { this.displayModel = CONTROLLER_PROPERTIES['oculus-touch-v2']; }
    }
    var modelUrl = this.displayModel[data.hand].modelUrl;
    this.el.setAttribute('gltf-model', 'url(' + modelUrl + ')');
  },

  injectTrackedControls: function () {
    var data = this.data;
    var webXRId = GAMEPAD_ID_WEBXR;
    var webVRId = data.hand === 'right' ? 'Oculus Touch (Right)' : 'Oculus Touch (Left)';
    var id = isWebXRAvailable ? webXRId : webVRId;
    this.el.setAttribute('tracked-controls', {
      id: id,
      controller: 0,
      hand: data.hand,
      orientationOffset: data.orientationOffset
    });
    this.loadModel();
  },

  addControllersUpdateListener: function () {
    this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false);
  },

  removeControllersUpdateListener: function () {
    this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false);
  },

  onControllersUpdate: function () {
    // Note that due to gamepadconnected event propagation issues, we don't rely on events.
    this.checkIfControllerPresent();
  },

  onButtonChanged: function (evt) {
    var button = this.mapping[this.data.hand].buttons[evt.detail.id];
    var buttonMeshes = this.buttonMeshes;
    var analogValue;
    if (!button) { return; }

    if (button === 'trigger' || button === 'grip') { analogValue = evt.detail.state.value; }

    // Update trigger and/or grip meshes, if any.
    if (buttonMeshes) {
      if (button === 'trigger' && buttonMeshes.trigger) {
        buttonMeshes.trigger.rotation.x = this.originalXRotationTrigger - analogValue * (Math.PI / 26);
      }
      if (button === 'grip' && buttonMeshes.grip) {
        buttonMeshes.grip.position.x = this.originalXPositionGrip + (this.data.hand === 'left' ? -1 : 1) * analogValue * 0.004;
      }
    }

    // Pass along changed event with button state, using the buttom mapping for convenience.
    this.el.emit(button + 'changed', evt.detail.state);
  },

  onModelLoaded: function (evt) {
    var controllerObject3D = evt.detail.model;
    var buttonMeshes;

    if (!this.data.model) { return; }

    buttonMeshes = this.buttonMeshes = {};

    buttonMeshes.grip = controllerObject3D.getObjectByName('buttonHand');
    this.originalXPositionGrip = buttonMeshes.grip.position.x;
    buttonMeshes.thumbstick = controllerObject3D.getObjectByName('stick');
    buttonMeshes.trigger = controllerObject3D.getObjectByName('buttonTrigger');
    this.originalXRotationTrigger = buttonMeshes.trigger.rotation.x;
    buttonMeshes.xbutton = controllerObject3D.getObjectByName('buttonX');
    buttonMeshes.abutton = controllerObject3D.getObjectByName('buttonA');
    buttonMeshes.ybutton = controllerObject3D.getObjectByName('buttonY');
    buttonMeshes.bbutton = controllerObject3D.getObjectByName('buttonB');

    // Offset pivot point
    controllerObject3D.position.copy(this.displayModel[this.data.hand].modelPivotOffset);
    controllerObject3D.rotation.copy(this.displayModel[this.data.hand].modelPivotRotation);

    this.el.emit('controllermodelready', {
      name: 'oculus-touch-controls',
      model: this.data.model,
      rayOrigin: this.displayModel[this.data.hand].rayOrigin
    });
  },

  onAxisMoved: function (evt) {
    emitIfAxesChanged(this, this.mapping[this.data.hand].axes, evt);
  },

  updateModel: function (buttonName, evtName) {
    if (!this.data.model) { return; }
    this.updateButtonModel(buttonName, evtName);
  },

  updateButtonModel: function (buttonName, state) {
    var button;
    var color = (state === 'up' || state === 'touchend') ? this.data.buttonColor : state === 'touchstart' ? this.data.buttonTouchColor : this.data.buttonHighlightColor;
    var buttonMeshes = this.buttonMeshes;
    if (!this.data.model) { return; }
    if (buttonMeshes && buttonMeshes[buttonName]) {
      button = buttonMeshes[buttonName];
      button.material.color.set(color);
      this.rendererSystem.applyColorCorrection(button.material.color);
    }
  }
});
<commit_msg>Set oculus-touch model url without CSS qualifier<commit_after>var bind = require('../utils/bind');
var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

var trackedControlsUtils = require('../utils/tracked-controls');
var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup;
var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged;
var onButtonEvent = trackedControlsUtils.onButtonEvent;

var isOculusBrowser = require('../utils/').device.isOculusBrowser();
var isWebXRAvailable = require('../utils/').device.isWebXRAvailable;

var GAMEPAD_ID_WEBXR = 'oculus-touch';
var GAMEPAD_ID_WEBVR = 'Oculus Touch';

// Prefix for Gen1 and Gen2 Oculus Touch Controllers.
var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR;

// First generation model URL.
var TOUCH_CONTROLLER_MODEL_BASE_URL = 'https://cdn.aframe.io/controllers/oculus/oculus-touch-controller-';
// For now the generation 2 model is the same as the original until a new one is prepared for upload.
var TOUCH_GEN2_CONTROLLER_MODEL_BASE_URL = TOUCH_CONTROLLER_MODEL_BASE_URL;

var OCULUS_TOUCH_WEBVR = {
  left: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'left.gltf',
    rayOrigin: {origin: {x: 0.008, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(-0.005, 0.003, -0.055),
    modelPivotRotation: new THREE.Euler(0, 0, 0)
  },
  right: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'right.gltf',
    rayOrigin: {origin: {x: -0.008, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(0.005, 0.003, -0.055),
    modelPivotRotation: new THREE.Euler(0, 0, 0)
  }
};

var OCULUS_TOUCH_WEBXR = {
  left: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'left.gltf',
    rayOrigin: {origin: {x: 0.002, y: -0.005, z: -0.03}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(-0.005, 0.036, -0.037),
    modelPivotRotation: new THREE.Euler(Math.PI / 4.5, 0, 0)
  },
  right: {
    modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'right.gltf',
    rayOrigin: {origin: {x: -0.002, y: -0.005, z: -0.03}, direction: {x: 0, y: -0.8, z: -1}},
    modelPivotOffset: new THREE.Vector3(0.005, 0.036, -0.037),
    modelPivotRotation: new THREE.Euler(Math.PI / 4.5, 0, 0)
  }
};

var OCULUS_TOUCH_CONFIG = isWebXRAvailable ? OCULUS_TOUCH_WEBXR : OCULUS_TOUCH_WEBVR;

var CONTROLLER_DEFAULT = 'oculus-touch';
var CONTROLLER_PROPERTIES = {
  'oculus-touch': OCULUS_TOUCH_CONFIG,
  'oculus-touch-v2': {
    left: {
      modelUrl: TOUCH_GEN2_CONTROLLER_MODEL_BASE_URL + 'gen2-left.gltf',
      rayOrigin: {origin: {x: -0.01, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
      modelPivotOffset: new THREE.Vector3(0, 0, 0),
      modelPivotRotation: new THREE.Euler(-Math.PI / 4, 0, 0)
    },
    right: {
      modelUrl: TOUCH_GEN2_CONTROLLER_MODEL_BASE_URL + 'gen2-right.gltf',
      rayOrigin: {origin: {x: 0.01, y: -0.01, z: 0}, direction: {x: 0, y: -0.8, z: -1}},
      modelPivotOffset: new THREE.Vector3(0, 0, 0),
      modelPivotRotation: new THREE.Euler(-Math.PI / 4, 0, 0)
    }
  }
};

/**
 * Button indices:
 * 0 - thumbstick (which has separate axismove / thumbstickmoved events)
 * 1 - trigger (with analog value, which goes up to 1)
 * 2 - grip (with analog value, which goes up to 1)
 * 3 - X (left) or A (right)
 * 4 - Y (left) or B (right)
 * 5 - surface (touch only)
 */
var INPUT_MAPPING_WEBVR = {
  left: {
    axes: {thumbstick: [0, 1]},
    buttons: ['thumbstick', 'trigger', 'grip', 'xbutton', 'ybutton', 'surface']
  },
  right: {
    axes: {thumbstick: [0, 1]},
    buttons: ['thumbstick', 'trigger', 'grip', 'abutton', 'bbutton', 'surface']
  }
};

/**
 * Button indices:
 * 0 - trigger
 * 1 - grip
 * 2 - none
 * 3 - thumbstick
 * 4 - X or A button
 * 5 - Y or B button
 * 6 - surface
 *
 * Axis:
 * 0 - none
 * 1 - none
 * 2 - thumbstick
 * 3 - thumbstick
 * Reference: https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/oculus/oculus-touch.json
 */
var INPUT_MAPPING_WEBXR = {
  left: {
    axes: {thumbstick: [2, 3]},
    buttons: ['trigger', 'grip', 'none', 'thumbstick', 'xbutton', 'ybutton', 'surface']
  },
  right: {
    axes: {thumbstick: [2, 3]},
    buttons: ['trigger', 'grip', 'none', 'thumbstick', 'abutton', 'bbutton', 'surface']
  }
};

var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR;

/**
 * Oculus Touch controls.
 * Interface with Oculus Touch controllers and map Gamepad events to
 * controller buttons: thumbstick, trigger, grip, xbutton, ybutton, surface
 * Load a controller model and highlight the pressed buttons.
 */
module.exports.Component = registerComponent('oculus-touch-controls', {
  schema: {
    hand: {default: 'left'},
    buttonColor: {type: 'color', default: '#999'},  // Off-white.
    buttonTouchColor: {type: 'color', default: '#8AB'},
    buttonHighlightColor: {type: 'color', default: '#2DF'},  // Light blue.
    model: {default: true},
    controllerType: {default: 'auto', oneOf: ['auto', 'oculus-touch', 'oculus-touch-v2']},
    orientationOffset: {type: 'vec3', default: {x: 43, y: 0, z: 0}}
  },

  mapping: INPUT_MAPPING,

  bindMethods: function () {
    this.onModelLoaded = bind(this.onModelLoaded, this);
    this.onControllersUpdate = bind(this.onControllersUpdate, this);
    this.checkIfControllerPresent = bind(this.checkIfControllerPresent, this);
    this.onAxisMoved = bind(this.onAxisMoved, this);
  },

  init: function () {
    var self = this;
    this.onButtonChanged = bind(this.onButtonChanged, this);
    this.onButtonDown = function (evt) { onButtonEvent(evt.detail.id, 'down', self, self.data.hand); };
    this.onButtonUp = function (evt) { onButtonEvent(evt.detail.id, 'up', self, self.data.hand); };
    this.onButtonTouchStart = function (evt) { onButtonEvent(evt.detail.id, 'touchstart', self, self.data.hand); };
    this.onButtonTouchEnd = function (evt) { onButtonEvent(evt.detail.id, 'touchend', self, self.data.hand); };
    this.controllerPresent = false;
    this.lastControllerCheck = 0;
    this.previousButtonValues = {};
    this.rendererSystem = this.el.sceneEl.systems.renderer;
    this.bindMethods();
  },

  addEventListeners: function () {
    var el = this.el;
    el.addEventListener('buttonchanged', this.onButtonChanged);
    el.addEventListener('buttondown', this.onButtonDown);
    el.addEventListener('buttonup', this.onButtonUp);
    el.addEventListener('touchstart', this.onButtonTouchStart);
    el.addEventListener('touchend', this.onButtonTouchEnd);
    el.addEventListener('axismove', this.onAxisMoved);
    el.addEventListener('model-loaded', this.onModelLoaded);
    this.controllerEventsActive = true;
  },

  removeEventListeners: function () {
    var el = this.el;
    el.removeEventListener('buttonchanged', this.onButtonChanged);
    el.removeEventListener('buttondown', this.onButtonDown);
    el.removeEventListener('buttonup', this.onButtonUp);
    el.removeEventListener('touchstart', this.onButtonTouchStart);
    el.removeEventListener('touchend', this.onButtonTouchEnd);
    el.removeEventListener('axismove', this.onAxisMoved);
    el.removeEventListener('model-loaded', this.onModelLoaded);
    this.controllerEventsActive = false;
  },

  checkIfControllerPresent: function () {
    checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, {
      hand: this.data.hand
    });
  },

  play: function () {
    this.checkIfControllerPresent();
    this.addControllersUpdateListener();
  },

  pause: function () {
    this.removeEventListeners();
    this.removeControllersUpdateListener();
  },

  loadModel: function () {
    var data = this.data;
    if (!data.model) { return; }

    // Set the controller display model based on the data passed in.
    this.displayModel = CONTROLLER_PROPERTIES[data.controllerType] || CONTROLLER_PROPERTIES[CONTROLLER_DEFAULT];
    // If the developer is asking for auto-detection, see if the displayName can be retrieved to identify the specific unit.
    // This only works for WebVR currently.
    if (data.controllerType === 'auto') {
      var trackedControlsSystem = this.el.sceneEl.systems['tracked-controls-webvr'];
      if (trackedControlsSystem && trackedControlsSystem.vrDisplay) {
        var displayName = trackedControlsSystem.vrDisplay.displayName;
        // The Oculus Quest uses the updated generation 2 inside-out tracked controllers so update the displayModel.
        if (/^Oculus Quest$/.test(displayName)) {
          this.displayModel = CONTROLLER_PROPERTIES['oculus-touch-v2'];
        }
      }
      if (isOculusBrowser) { this.displayModel = CONTROLLER_PROPERTIES['oculus-touch-v2']; }
    }
    var modelUrl = this.displayModel[data.hand].modelUrl;
    this.el.setAttribute('gltf-model', modelUrl);
  },

  injectTrackedControls: function () {
    var data = this.data;
    var webXRId = GAMEPAD_ID_WEBXR;
    var webVRId = data.hand === 'right' ? 'Oculus Touch (Right)' : 'Oculus Touch (Left)';
    var id = isWebXRAvailable ? webXRId : webVRId;
    this.el.setAttribute('tracked-controls', {
      id: id,
      controller: 0,
      hand: data.hand,
      orientationOffset: data.orientationOffset
    });
    this.loadModel();
  },

  addControllersUpdateListener: function () {
    this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false);
  },

  removeControllersUpdateListener: function () {
    this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false);
  },

  onControllersUpdate: function () {
    // Note that due to gamepadconnected event propagation issues, we don't rely on events.
    this.checkIfControllerPresent();
  },

  onButtonChanged: function (evt) {
    var button = this.mapping[this.data.hand].buttons[evt.detail.id];
    var buttonMeshes = this.buttonMeshes;
    var analogValue;
    if (!button) { return; }

    if (button === 'trigger' || button === 'grip') { analogValue = evt.detail.state.value; }

    // Update trigger and/or grip meshes, if any.
    if (buttonMeshes) {
      if (button === 'trigger' && buttonMeshes.trigger) {
        buttonMeshes.trigger.rotation.x = this.originalXRotationTrigger - analogValue * (Math.PI / 26);
      }
      if (button === 'grip' && buttonMeshes.grip) {
        buttonMeshes.grip.position.x = this.originalXPositionGrip + (this.data.hand === 'left' ? -1 : 1) * analogValue * 0.004;
      }
    }

    // Pass along changed event with button state, using the buttom mapping for convenience.
    this.el.emit(button + 'changed', evt.detail.state);
  },

  onModelLoaded: function (evt) {
    var controllerObject3D = evt.detail.model;
    var buttonMeshes;

    if (!this.data.model) { return; }

    buttonMeshes = this.buttonMeshes = {};

    buttonMeshes.grip = controllerObject3D.getObjectByName('buttonHand');
    this.originalXPositionGrip = buttonMeshes.grip.position.x;
    buttonMeshes.thumbstick = controllerObject3D.getObjectByName('stick');
    buttonMeshes.trigger = controllerObject3D.getObjectByName('buttonTrigger');
    this.originalXRotationTrigger = buttonMeshes.trigger.rotation.x;
    buttonMeshes.xbutton = controllerObject3D.getObjectByName('buttonX');
    buttonMeshes.abutton = controllerObject3D.getObjectByName('buttonA');
    buttonMeshes.ybutton = controllerObject3D.getObjectByName('buttonY');
    buttonMeshes.bbutton = controllerObject3D.getObjectByName('buttonB');

    // Offset pivot point
    controllerObject3D.position.copy(this.displayModel[this.data.hand].modelPivotOffset);
    controllerObject3D.rotation.copy(this.displayModel[this.data.hand].modelPivotRotation);

    this.el.emit('controllermodelready', {
      name: 'oculus-touch-controls',
      model: this.data.model,
      rayOrigin: this.displayModel[this.data.hand].rayOrigin
    });
  },

  onAxisMoved: function (evt) {
    emitIfAxesChanged(this, this.mapping[this.data.hand].axes, evt);
  },

  updateModel: function (buttonName, evtName) {
    if (!this.data.model) { return; }
    this.updateButtonModel(buttonName, evtName);
  },

  updateButtonModel: function (buttonName, state) {
    var button;
    var color = (state === 'up' || state === 'touchend') ? this.data.buttonColor : state === 'touchstart' ? this.data.buttonTouchColor : this.data.buttonHighlightColor;
    var buttonMeshes = this.buttonMeshes;
    if (!this.data.model) { return; }
    if (buttonMeshes && buttonMeshes[buttonName]) {
      button = buttonMeshes[buttonName];
      button.material.color.set(color);
      this.rendererSystem.applyColorCorrection(button.material.color);
    }
  }
});
<|endoftext|>