<commit_before>'use strict';

var path = process.cwd();
var ctrlPath = path + '/server/ctrl';

const UserController = require(ctrlPath + '/user-ctrl.server.js');
const BookController = require(ctrlPath + '/book-ctrl.server.js');
const TradeController = require(ctrlPath + '/trade-ctrl.server.js');
const debug = require('debug')('book-routes');

var _log = function(msg) {
    console.log('[LOG@SERVER]: ' + msg);
};

/* Function for debug logging requests.*/
var reqDebug = function(req) {
	_log('\t\tHeaders: ' + JSON.stringify(req.headers));
	_log('\t\tBody: ' + JSON.stringify(req.body));
	_log('\t\tParams: ' + JSON.stringify(req.params));
	_log('\t\tUrl:' + JSON.stringify(req.url));
	_log('\t\tText:' + JSON.stringify(req.text));
	_log('\t\tContent:' + JSON.stringify(req.content));
	_log('\t\tQuery:' + JSON.stringify(req.query));
};

var notAuthorized = {msg: 'Operation not authorized. Log in first.'};
var errorInternal = {msg: 'Server internal error.'};
var errorForbidden = {msg: 'Requested action forbidden.'};

module.exports = function(app, passport) {

    /* Renders a pug template.*/
    var renderPug = function(req, res, pugFile) {
        var isAuth = req.isAuthenticated();
        res.render(path + '/pug/' + pugFile, {isAuth: isAuth});
    };

    /* loggedIn func from clementine.js. */
	var isLoggedIn = function(req, res, next) {
		if (req.isAuthenticated()) {
			return next();
		}
        else {
			return res.redirect('/login');
		}
	};

    /* Returns the username from the request. Logs an error if username is not
     * present. */
    var getUserName = function(req, route) {
        if (req.user) {
            if (req.user.username) {
                return req.user.username;
            }
            else {
                logError(route, 'getUserName: No req.user.username exists.',
                    req);
            }
        }
        else {
            logError(route, 'getUserName: No req.user exists.', req);
        }
        return null;
    };

    var logErrorReq = function(req) {
        console.error('Error REQ headers: ');
        reqDebug(req);
    };

    /* Logs an error with route and request information included.*/
    var logError = function(route, err, req) {
        var date = new Date();
        console.error(date + ' [ERROR@SERVER] route ' + route + ' | ' + err);
        if (req) {
            logErrorReq(req);
        }
    };

    // CONTROLLERS
    var userController = new UserController(path);
    var bookController = new BookController(path);
    var tradeController = new TradeController(path);

    //----------------------------------------------------------------------
    // ROUTES
    //----------------------------------------------------------------------

	app.route('/')
		.get((req, res) => {
            renderPug(req, res, 'index.pug');
		});

	app.route('/about')
		.get((req, res) => {
            renderPug(req, res, 'about.pug');
		});

	app.route('/allbooks')
		.get((req, res) => {
            renderPug(req, res, 'books.pug');
		});

	app.route('/signup')
		.get((req, res) => {
            renderPug(req, res, 'signup.pug');
		});

	app.route('/login')
		.get((req, res) => {
            renderPug(req, res, 'login.pug');
		});

	app.route('/loginFailed')
		.get((req, res) => {
            res.render(path + '/pug/login.pug',
                {isAuth: false, loginFailed: true});
        });

    // If a user logs out, return to main page
	app.route('/logout')
		.get((req, res) => {
			req.logout();
			res.redirect('/');
		});

	app.route('/profile')
		.get(isLoggedIn, (req, res) => {
            renderPug(req, res, 'profile.pug');
		});

    // Handle registration of user
    app.route('/forms/signup')
        .post((req, res) => {
            debug('Got a signup form GET request..');
            // reqDebug(req);
            userController.addLocalUser(req, res);
        });

    // Called by client to check their authentication status
	app.route('/amiauth')
		.get((req, res) => {
            var data = {isAuth: false};
            if (req.isAuthenticated()) {
                data.isAuth = true;
                data.username = req.user.username;
                userController.getUserID(data.username, (err, userID) => {
                    if (err) {
                        logError('/amiauth', err, req);
                        res.json({error: 'Failed to authenticate'});
                    }
                    else {
                        data.userID = userID;
                        res.json(data);
                    }
                });
            }
            else {
                res.json(data);
            }
		});

    //----------------------------------
    // Routes for manipulating user data
    //----------------------------------

    app.route('/user/:name')
        .get(isLoggedIn, (req, res) => {
            var username = req.params.name;
            if (username === req.user.username) {
                userController.getUserByName(username, (err, data) => {
                    if (err) {
                        logError('/user/' + username, err, req);
                        res.status(500).json(errorInternal);
                    }
                    else {
                        delete data.local.password;
                        res.json(data);
                    }
                });
            }
            else {
                // Forbidden
                logError('/user/' + username,
                    'Forbidden action attempted.', req);
                res.status(403).json(errorForbidden);
            }
        });

    //--------------------------------------
    // Routes for the book data
    //--------------------------------------
    app.route('/book')
        .get(isLoggedIn, (req, res) => {
            var username = req.user.username;
            bookController.getBooks(username, (err, data) => {
                if (err) {
                    logError('GET /book/', err, req);
                    res.status(500).json(errorInternal);
                }
                else {
                    res.status(200).json(data);
                }
            });
        })
        .post(isLoggedIn, (req, res) => {
            var username = req.body.username;
            var bookTitle = req.body.title;
            _log('Server got POST-req to /book. User: ' + username);
            if (username === req.user.username) {
                var bookData = {username: username, title: bookTitle};
                bookController.addBook(bookData, (err, bookData) => {
                    if (err) {
                        logError('POST /book/' + bookTitle, err, req);
                        res.status(500).json(errorInternal);
                    }
                    else {
                        console.log('bookData is ' + JSON.stringify(bookData));
                        res.status(200).json(bookData);
                    }
                });
            }
            else {
                res.status(403).json(notAuthorized);
            }
        })
        .delete(isLoggedIn, (req, res) => {
            if (req.body) {
                var bookData = req.body;
                var updateObj = {
                    username: req.user.username,
                    book: bookData
                };
                debug('Server got DELETE-req to /book. Data: '
                    + JSON.stringify(bookData));
                bookController.deleteBook(updateObj, (err) => {
                    if (err) {
                        logError('DELETE /book/' + bookData, err, req);
                        res.status(500).json(errorInternal);
                    }
                    else {
                        res.status(200).json({msg: 'OK'});
                    }
                });
            }
            else {
                res.status(400).json({msg: 'No proper body in DELETE'});
            }

        });

    //----------------------------------
    // Routes for Trade Requests
    //----------------------------------

    app.route('/tradereq/accept')
        .post(isLoggedIn, (req, res) => {
            var username = getUserName(req, '/tradereq/accept');
            console.log('accept user ' + username);
            res.status(500).json(errorInternal);

        });

    app.route('/tradereq/reject')
        .post(isLoggedIn, (req, res) => {
            var username = getUserName(req, 'tradereq/reject');
            console.log('reject user ' + username);
            res.status(500).json(errorInternal);

        });

    app.route('/tradereq')
        .post(isLoggedIn, (req, res) => {
            var username = getUserName(req, '/tradereq');
            tradeController.addRequest(username, req.body, (err, data) => {
                if (err) {
                    logError('POST /tradereq', err, req);
                    res.status(500).json(errorInternal);
                }
                else {
                    res.status(200).json(data);
                }
            });
        })
        .delete(isLoggedIn, (req, res) => {
            var username = getUserName(req, '/tradereq');
            console.log('delete user ' + username);
            res.status(500).json(errorInternal);
        });

    //--------------------------------------
    // User registration and authentication
    //--------------------------------------

	app.route('/api/:id')
		.get((req, res) => {
            userController.getUser(req, res);
		});

    // Logs user in via form (after successful authentication
	app.route('/auth/userlogin')
        .post(passport.authenticate('local',
            { failureRedirect: '/loginFailed' }),
		(req, res) => {
			res.redirect('/');
		});

};
<commit_msg>Corrected addRequest -> addTradeReq.<commit_after>'use strict';

var path = process.cwd();
var ctrlPath = path + '/server/ctrl';

const UserController = require(ctrlPath + '/user-ctrl.server.js');
const BookController = require(ctrlPath + '/book-ctrl.server.js');
const TradeController = require(ctrlPath + '/trade-ctrl.server.js');
const debug = require('debug')('book-routes');

var _log = function(msg) {
    console.log('[LOG@SERVER]: ' + msg);
};

/* Function for debug logging requests.*/
var reqDebug = function(req) {
	_log('\t\tHeaders: ' + JSON.stringify(req.headers));
	_log('\t\tBody: ' + JSON.stringify(req.body));
	_log('\t\tParams: ' + JSON.stringify(req.params));
	_log('\t\tUrl:' + JSON.stringify(req.url));
	_log('\t\tText:' + JSON.stringify(req.text));
	_log('\t\tContent:' + JSON.stringify(req.content));
	_log('\t\tQuery:' + JSON.stringify(req.query));
};

var notAuthorized = {msg: 'Operation not authorized. Log in first.'};
var errorInternal = {msg: 'Server internal error.'};
var errorForbidden = {msg: 'Requested action forbidden.'};
var requestOk = {msg: 'Request was completed OK.'};

module.exports = function(app, passport) {

    /* Renders a pug template.*/
    var renderPug = function(req, res, pugFile) {
        var isAuth = req.isAuthenticated();
        res.render(path + '/pug/' + pugFile, {isAuth: isAuth});
    };

    /* loggedIn func from clementine.js. */
	var isLoggedIn = function(req, res, next) {
		if (req.isAuthenticated()) {
			return next();
		}
        else {
			return res.redirect('/login');
		}
	};

    /* Returns the username from the request. Logs an error if username is not
     * present. */
    var getUserName = function(req, route) {
        if (req.user) {
            if (req.user.username) {
                return req.user.username;
            }
            else {
                logError(route, 'getUserName: No req.user.username exists.',
                    req);
            }
        }
        else {
            logError(route, 'getUserName: No req.user exists.', req);
        }
        return null;
    };

    var logErrorReq = function(req) {
        console.error('Error REQ headers: ');
        reqDebug(req);
    };

    /* Logs an error with route and request information included.*/
    var logError = function(route, err, req) {
        var date = new Date();
        console.error(date + ' [ERROR@SERVER] route ' + route + ' | ' + err);
        if (req) {
            logErrorReq(req);
        }
    };

    // CONTROLLERS
    var userController = new UserController(path);
    var bookController = new BookController(path);
    var tradeController = new TradeController(path);

    //----------------------------------------------------------------------
    // ROUTES
    //----------------------------------------------------------------------

	app.route('/')
		.get((req, res) => {
            renderPug(req, res, 'index.pug');
		});

	app.route('/about')
		.get((req, res) => {
            renderPug(req, res, 'about.pug');
		});

	app.route('/allbooks')
		.get((req, res) => {
            renderPug(req, res, 'books.pug');
		});

	app.route('/signup')
		.get((req, res) => {
            renderPug(req, res, 'signup.pug');
		});

	app.route('/login')
		.get((req, res) => {
            renderPug(req, res, 'login.pug');
		});

	app.route('/loginFailed')
		.get((req, res) => {
            res.render(path + '/pug/login.pug',
                {isAuth: false, loginFailed: true});
        });

    // If a user logs out, return to main page
	app.route('/logout')
		.get((req, res) => {
			req.logout();
			res.redirect('/');
		});

	app.route('/profile')
		.get(isLoggedIn, (req, res) => {
            renderPug(req, res, 'profile.pug');
		});

    // Handle registration of user
    app.route('/forms/signup')
        .post((req, res) => {
            debug('Got a signup form GET request..');
            // reqDebug(req);
            userController.addLocalUser(req, res);
        });

    // Called by client to check their authentication status
	app.route('/amiauth')
		.get((req, res) => {
            var data = {isAuth: false};
            if (req.isAuthenticated()) {
                data.isAuth = true;
                data.username = req.user.username;
                userController.getUserID(data.username, (err, userID) => {
                    if (err) {
                        logError('/amiauth', err, req);
                        res.json({error: 'Failed to authenticate'});
                    }
                    else {
                        data.userID = userID;
                        res.json(data);
                    }
                });
            }
            else {
                res.json(data);
            }
		});

    //----------------------------------
    // Routes for manipulating user data
    //----------------------------------

    app.route('/user/:name')
        .get(isLoggedIn, (req, res) => {
            var username = req.params.name;
            if (username === req.user.username) {
                userController.getUserByName(username, (err, data) => {
                    if (err) {
                        logError('/user/' + username, err, req);
                        res.status(500).json(errorInternal);
                    }
                    else {
                        delete data.local.password;
                        res.json(data);
                    }
                });
            }
            else {
                // Forbidden
                logError('/user/' + username,
                    'Forbidden action attempted.', req);
                res.status(403).json(errorForbidden);
            }
        });

    //--------------------------------------
    // Routes for the book data
    //--------------------------------------
    app.route('/book')
        .get(isLoggedIn, (req, res) => {
            var username = req.user.username;
            bookController.getBooks(username, (err, data) => {
                if (err) {
                    logError('GET /book/', err, req);
                    res.status(500).json(errorInternal);
                }
                else {
                    res.status(200).json(data);
                }
            });
        })
        .post(isLoggedIn, (req, res) => {
            var username = req.body.username;
            var bookTitle = req.body.title;
            _log('Server got POST-req to /book. User: ' + username);
            if (username === req.user.username) {
                var bookData = {username: username, title: bookTitle};
                bookController.addBook(bookData, (err, bookData) => {
                    if (err) {
                        logError('POST /book/' + bookTitle, err, req);
                        res.status(500).json(errorInternal);
                    }
                    else {
                        console.log('bookData is ' + JSON.stringify(bookData));
                        res.status(200).json(bookData);
                    }
                });
            }
            else {
                res.status(403).json(notAuthorized);
            }
        })
        .delete(isLoggedIn, (req, res) => {
            if (req.body) {
                var bookData = req.body;
                var updateObj = {
                    username: req.user.username,
                    book: bookData
                };
                debug('Server got DELETE-req to /book. Data: '
                    + JSON.stringify(bookData));
                bookController.deleteBook(updateObj, (err) => {
                    if (err) {
                        logError('DELETE /book/' + bookData, err, req);
                        res.status(500).json(errorInternal);
                    }
                    else {
                        res.status(200).json({msg: 'OK'});
                    }
                });
            }
            else {
                res.status(400).json({msg: 'No proper body in DELETE'});
            }

        });

    //----------------------------------
    // Routes for Trade Requests
    //----------------------------------

    app.route('/tradereq/accept')
        .post(isLoggedIn, (req, res) => {
            var username = getUserName(req, '/tradereq/accept');
            console.log('accept user ' + username);
            res.status(500).json(errorInternal);

        });

    app.route('/tradereq/reject')
        .post(isLoggedIn, (req, res) => {
            var username = getUserName(req, 'tradereq/reject');
            console.log('reject user ' + username);
            res.status(500).json(errorInternal);

        });

    app.route('/tradereq')
        .post(isLoggedIn, (req, res) => {
            var username = getUserName(req, '/tradereq');
            tradeController.addTradeReq(username, req.body, err => {
                if (err) {
                    logError('POST /tradereq', err, req);
                    res.status(500).json(errorInternal);
                }
                else {
                    res.status(200).json(requestOk);
                }
            });
        })
        .delete(isLoggedIn, (req, res) => {
            var username = getUserName(req, '/tradereq');
            console.log('delete user ' + username);
            res.status(500).json(errorInternal);
        });

    //--------------------------------------
    // User registration and authentication
    //--------------------------------------

	app.route('/api/:id')
		.get((req, res) => {
            userController.getUser(req, res);
		});

    // Logs user in via form (after successful authentication
	app.route('/auth/userlogin')
        .post(passport.authenticate('local',
            { failureRedirect: '/loginFailed' }),
		(req, res) => {
			res.redirect('/');
		});

};
<|endoftext|>


<commit_before>import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";
import gql from "graphql-tag";
import { graphql, withApollo } from "react-apollo";
import { Row, Col, Container, Button, Input, ButtonGroup } from "reactstrap";
import Grid from "../GridDom";
import ExtraControls from "./extraControls";
import ContactsList from "./contactsList";
import MovementCore from "./movementCore";
import ContactSelect from "./contactSelect";
import SubscriptionHelper from "../../../../helpers/subscriptionHelper";
import "./gridCore.scss";

const SENSORS_OFFSET = 45;
function distance3d(coord2, coord1) {
  const { x: x1, y: y1, z: z1 } = coord1;
  let { x: x2, y: y2, z: z2 } = coord2;
  return Math.sqrt((x2 -= x1) * x2 + (y2 -= y1) * y2 + (z2 -= z1) * z2);
}

const SENSOR_SUB = gql`
  subscription SensorsChanged($id: ID) {
    sensorsUpdate(simulatorId: $id, domain: "external") {
      id
      type
      autoTarget
      autoThrusters
      interference
      movement {
        x
        y
        z
      }
      segments {
        segment
        state
      }
      armyContacts {
        id
        name
        size
        icon
        picture
        color
        infrared
        cloaked
        destroyed
        locked
        disabled
      }
    }
  }
`;
const padding = 0;

class GridCore extends Component {
  constructor(props) {
    super(props);
    this.state = {
      movingContact: {},
      contextContact: null,
      speed: 0.6,
      askForSpeed:
        localStorage.getItem("thorium-core-sensors-askforspeed") === "yes"
          ? true
          : false,
      currentControl: "contacts",
      selectedContacts: []
    };
  }
  componentDidMount() {
    if (!this.state.dimensions && ReactDOM.findDOMNode(this)) {
      const domNode = ReactDOM.findDOMNode(this).querySelector("#threeSensors");
      if (domNode) {
        const { width, height, left, top } = domNode.getBoundingClientRect();
        this.setState({
          dimensions: {
            width: width - SENSORS_OFFSET,
            height: height - SENSORS_OFFSET,
            left: left + SENSORS_OFFSET,
            top: top + SENSORS_OFFSET
          }
        });
      }
    }
  }
  componentDidUpdate() {
    if (!ReactDOM.findDOMNode(this)) return;
    const domNode = ReactDOM.findDOMNode(this).querySelector("#threeSensors");
    if (!domNode) return;
    if (
      !this.state.dimensions ||
      this.state.dimensions.width !==
        domNode.getBoundingClientRect().width - SENSORS_OFFSET
    ) {
      const { width, height, left, top } = domNode.getBoundingClientRect();
      this.setState({
        dimensions: {
          width: width - SENSORS_OFFSET,
          height: height - SENSORS_OFFSET,
          left: left + SENSORS_OFFSET,
          top: top + SENSORS_OFFSET
        }
      });
    }
  }
  dragStart = movingContact => {
    const self = this;
    this.setState({
      movingContact: Object.assign({}, movingContact, {
        location: null
      })
    });
    document.addEventListener("mousemove", this.moveMouse);
    document.addEventListener("mouseup", _mouseUp);
    function _mouseUp() {
      document.removeEventListener("mousemove", self.moveMouse);
      document.removeEventListener("mouseup", _mouseUp);
      self.dragStop();
    }
  };
  moveMouse = evt => {
    const { dimensions } = this.state;
    const { movingContact = {} } = this.state;
    const { width: dimWidth, height: dimHeight } = dimensions;
    const width = Math.min(dimWidth, dimHeight);
    console.log(
      (evt.clientY -
        dimensions.top +
        SENSORS_OFFSET / 2 -
        dimensions.height / 2) /
        (dimensions.width / 2),
      dimensions.top,
      dimensions.height
    );
    const destination = {
      x:
        (evt.clientX - dimensions.left + SENSORS_OFFSET / 2 - width / 2) /
          (dimensions.width / 2) -
        0.08,
      y:
        (evt.clientY - dimensions.top + SENSORS_OFFSET / 2 - width / 2) /
          (dimensions.height / 2) -
        0.08,
      z: 0
    };
    this.setState({
      movingContact: Object.assign({}, movingContact, { location: destination })
    });
  };
  dragStop = () => {
    const { movingContact } = this.state;
    this.setState({
      movingContact: null
    });
    const {
      location,
      icon,
      type,
      size,
      name,
      color,
      picture,
      cloaked,
      infrared,
      locked
    } = movingContact;
    if (!location) return;
    const distance = distance3d({ x: 0, y: 0, z: 0 }, location);
    // Max Distance should be the distance + the width of the contact
    const maxDistance = type === "planet" ? 1 + size / 2 : 1.1;
    if (distance > maxDistance) {
      return;
    }
    const mutation = gql`
      mutation CreateContact($id: ID!, $contact: SensorContactInput!) {
        createSensorContact(id: $id, contact: $contact)
      }
    `;
    const variables = {
      id: this.props.data.sensors[0].id,
      contact: {
        icon,
        type,
        size,
        name,
        color,
        picture,
        cloaked,
        location,
        infrared,
        locked,
        destination: location
      }
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  _clearContacts() {
    const mutation = gql`
      mutation DeleteContact($id: ID!) {
        removeAllSensorContacts(id: $id)
      }
    `;
    const { id } = this.props.data.sensors[0];
    const variables = {
      id
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  }
  _stopContacts() {
    const mutation = gql`
      mutation StopContacts($id: ID!) {
        stopAllSensorContacts(id: $id)
      }
    `;
    const { id } = this.props.data.sensors[0];
    const variables = {
      id
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  }
  _freezeContacts() {}
  _changeSpeed(e) {
    this.setState({
      speed: e.target.value
    });
  }
  render() {
    if (this.props.data.loading) return
Loading...
;
    if (!this.props.data.sensors[0]) return
No Sensor Grid
;
    const sensors = this.props.data.sensors[0];
    const {
      speed,
      movingContact,
      askForSpeed,
      currentControl,
      selectedContacts
    } = this.state;
    const speeds = [
      { value: "1000", label: "Instant" },
      { value: "5", label: "Warp" },
      { value: "1", label: "Very Fast" },
      { value: "0.6", label: "Fast" },
      { value: "0.4", label: "Moderate" },
      { value: "0.2", label: "Slow" },
      { value: "0.05", label: "Very Slow" }
    ];

    return (
      <Container className="sensorGridCore" fluid style={{ height: "100%" }}>
        <SubscriptionHelper
          subscribe={() =>
            this.props.data.subscribeToMore({
              document: SENSOR_SUB,
              variables: {
                id: this.props.simulator.id
              },
              updateQuery: (previousResult, { subscriptionData }) => {
                return Object.assign({}, previousResult, {
                  sensors: subscriptionData.data.sensorsUpdate
                });
              }
            })
          }
        />
        <Row style={{ height: "100%" }}>
          <Col
            sm={4}
            style={{
              display: "flex",
              justifyContent: "space-between",
              flexDirection: "column"
            }}
          >
            Speed
            <Input
              type="select"
              name="select"
              onChange={this._changeSpeed.bind(this)}
              defaultValue={speed}
            >
              {speeds.map(s => (
                <option key={s.value} value={s.value}>
                  {s.label}
                </option>
              ))}
              <option disabled>─────────</option>
              <option disabled>Timed</option>
            </Input>
            <Button
              size="sm"
              color="danger"
              block
              onClick={this._clearContacts.bind(this)}
            >
              Clear
            </Button>
            <Button
              size="sm"
              color="warning"
              block
              onClick={this._stopContacts.bind(this)}
            >
              Stop
            </Button>
            {selectedContacts.length > 0 ? (
              <ContactSelect
                id={sensors.id}
                clearSelection={() => this.setState({ selectedContacts: [] })}
                contacts={selectedContacts}
                client={this.props.client}
              />
            ) : (
              <Fragment>
                <ButtonGroup>
                  <Button
                    active={currentControl === "contacts"}
                    size="sm"
                    color="success"
                    onClick={() =>
                      this.setState({ currentControl: "contacts" })
                    }
                  >
                    Icons
                  </Button>
                  <Button
                    active={currentControl === "extras"}
                    size="sm"
                    color="info"
                    onClick={() => this.setState({ currentControl: "extras" })}
                  >
                    Extras
                  </Button>
                  <Button
                    active={currentControl === "movement"}
                    size="sm"
                    color="primary"
                    onClick={() =>
                      this.setState({ currentControl: "movement" })
                    }
                  >
                    Move
                  </Button>
                </ButtonGroup>
                {currentControl === "extras" && (
                  <ExtraControls
                    sensors={sensors}
                    askForSpeed={askForSpeed}
                    updateAskForSpeed={e => this.setState({ askForSpeed: e })}
                    client={this.props.client}
                    speed={speed}
                    dragStart={this.dragStart}
                  />
                )}
                {currentControl === "contacts" && (
                  <ContactsList
                    sensors={sensors}
                    dragStart={this.dragStart}
                    client={this.props.client}
                  />
                )}
                {currentControl === "movement" && (
                  <MovementCore sensors={sensors} client={this.props.client} />
                )}
              </Fragment>
            )}
          </Col>
          <Col sm={8}>

              <Grid
                mouseover={this.props.hoverContact}
                core
                dimensions={this.state.dimensions}
                offset={SENSORS_OFFSET}
                sensor={sensors.id}
                movement={sensors.movement}
                moveSpeed={speed}
                speeds={speeds}
                askForSpeed={askForSpeed}
                armyContacts={sensors.armyContacts}
                movingContact={movingContact}
                segments={sensors.segments}
                selectedContacts={selectedContacts.map(c => c.id)}
                updateSelectedContacts={contacts =>
                  this.setState({ selectedContacts: contacts })
                }
              />

          </Col>
        </Row>
      </Container>
    );
  }
}

const GRID_QUERY = gql`
  query GetSensors($simulatorId: ID) {
    sensors(simulatorId: $simulatorId, domain: "external") {
      id
      type
      autoTarget
      autoThrusters
      interference
      movement {
        x
        y
        z
      }
      segments {
        segment
        state
      }
      armyContacts {
        id
        name
        size
        icon
        picture
        color
        infrared
        cloaked
        destroyed
        locked
        disabled
      }
    }
  }
`;

export default graphql(GRID_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: { simulatorId: ownProps.simulator.id }
  })
})(withApollo(GridCore));
<commit_msg>Cleanup<commit_after>import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";
import gql from "graphql-tag";
import { graphql, withApollo } from "react-apollo";
import { Row, Col, Container, Button, Input, ButtonGroup } from "reactstrap";
import Grid from "../GridDom";
import ExtraControls from "./extraControls";
import ContactsList from "./contactsList";
import MovementCore from "./movementCore";
import ContactSelect from "./contactSelect";
import SubscriptionHelper from "../../../../helpers/subscriptionHelper";
import "./gridCore.scss";

const SENSORS_OFFSET = 45;
function distance3d(coord2, coord1) {
  const { x: x1, y: y1, z: z1 } = coord1;
  let { x: x2, y: y2, z: z2 } = coord2;
  return Math.sqrt((x2 -= x1) * x2 + (y2 -= y1) * y2 + (z2 -= z1) * z2);
}

const SENSOR_SUB = gql`
  subscription SensorsChanged($id: ID) {
    sensorsUpdate(simulatorId: $id, domain: "external") {
      id
      type
      autoTarget
      autoThrusters
      interference
      movement {
        x
        y
        z
      }
      segments {
        segment
        state
      }
      armyContacts {
        id
        name
        size
        icon
        picture
        color
        infrared
        cloaked
        destroyed
        locked
        disabled
      }
    }
  }
`;

class GridCore extends Component {
  constructor(props) {
    super(props);
    this.state = {
      movingContact: {},
      contextContact: null,
      speed: 0.6,
      askForSpeed:
        localStorage.getItem("thorium-core-sensors-askforspeed") === "yes"
          ? true
          : false,
      currentControl: "contacts",
      selectedContacts: []
    };
  }
  componentDidMount() {
    if (!this.state.dimensions && ReactDOM.findDOMNode(this)) {
      const domNode = ReactDOM.findDOMNode(this).querySelector("#threeSensors");
      if (domNode) {
        const { width, height, left, top } = domNode.getBoundingClientRect();
        this.setState({
          dimensions: {
            width: width - SENSORS_OFFSET,
            height: height - SENSORS_OFFSET,
            left: left + SENSORS_OFFSET,
            top: top + SENSORS_OFFSET
          }
        });
      }
    }
  }
  componentDidUpdate() {
    if (!ReactDOM.findDOMNode(this)) return;
    const domNode = ReactDOM.findDOMNode(this).querySelector("#threeSensors");
    if (!domNode) return;
    if (
      !this.state.dimensions ||
      this.state.dimensions.width !==
        domNode.getBoundingClientRect().width - SENSORS_OFFSET
    ) {
      const { width, height, left, top } = domNode.getBoundingClientRect();
      this.setState({
        dimensions: {
          width: width - SENSORS_OFFSET,
          height: height - SENSORS_OFFSET,
          left: left + SENSORS_OFFSET,
          top: top + SENSORS_OFFSET
        }
      });
    }
  }
  dragStart = movingContact => {
    const self = this;
    this.setState({
      movingContact: Object.assign({}, movingContact, {
        location: null
      })
    });
    document.addEventListener("mousemove", this.moveMouse);
    document.addEventListener("mouseup", _mouseUp);
    function _mouseUp() {
      document.removeEventListener("mousemove", self.moveMouse);
      document.removeEventListener("mouseup", _mouseUp);
      self.dragStop();
    }
  };
  moveMouse = evt => {
    const { dimensions } = this.state;
    const { movingContact = {} } = this.state;
    const { width: dimWidth, height: dimHeight } = dimensions;
    const width = Math.min(dimWidth, dimHeight);
    console.log(
      (evt.clientY -
        dimensions.top +
        SENSORS_OFFSET / 2 -
        dimensions.height / 2) /
        (dimensions.width / 2),
      dimensions.top,
      dimensions.height
    );
    const destination = {
      x:
        (evt.clientX - dimensions.left + SENSORS_OFFSET / 2 - width / 2) /
          (dimensions.width / 2) -
        0.08,
      y:
        (evt.clientY - dimensions.top + SENSORS_OFFSET / 2 - width / 2) /
          (dimensions.height / 2) -
        0.08,
      z: 0
    };
    this.setState({
      movingContact: Object.assign({}, movingContact, { location: destination })
    });
  };
  dragStop = () => {
    const { movingContact } = this.state;
    this.setState({
      movingContact: null
    });
    const {
      location,
      icon,
      type,
      size,
      name,
      color,
      picture,
      cloaked,
      infrared,
      locked
    } = movingContact;
    if (!location) return;
    const distance = distance3d({ x: 0, y: 0, z: 0 }, location);
    // Max Distance should be the distance + the width of the contact
    const maxDistance = type === "planet" ? 1 + size / 2 : 1.1;
    if (distance > maxDistance) {
      return;
    }
    const mutation = gql`
      mutation CreateContact($id: ID!, $contact: SensorContactInput!) {
        createSensorContact(id: $id, contact: $contact)
      }
    `;
    const variables = {
      id: this.props.data.sensors[0].id,
      contact: {
        icon,
        type,
        size,
        name,
        color,
        picture,
        cloaked,
        location,
        infrared,
        locked,
        destination: location
      }
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  _clearContacts() {
    const mutation = gql`
      mutation DeleteContact($id: ID!) {
        removeAllSensorContacts(id: $id)
      }
    `;
    const { id } = this.props.data.sensors[0];
    const variables = {
      id
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  }
  _stopContacts() {
    const mutation = gql`
      mutation StopContacts($id: ID!) {
        stopAllSensorContacts(id: $id)
      }
    `;
    const { id } = this.props.data.sensors[0];
    const variables = {
      id
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  }
  _freezeContacts() {}
  _changeSpeed(e) {
    this.setState({
      speed: e.target.value
    });
  }
  render() {
    if (this.props.data.loading) return
Loading...
;
    if (!this.props.data.sensors[0]) return
No Sensor Grid
;
    const sensors = this.props.data.sensors[0];
    const {
      speed,
      movingContact,
      askForSpeed,
      currentControl,
      selectedContacts
    } = this.state;
    const speeds = [
      { value: "1000", label: "Instant" },
      { value: "5", label: "Warp" },
      { value: "1", label: "Very Fast" },
      { value: "0.6", label: "Fast" },
      { value: "0.4", label: "Moderate" },
      { value: "0.2", label: "Slow" },
      { value: "0.05", label: "Very Slow" }
    ];

    return (
      <Container className="sensorGridCore" fluid style={{ height: "100%" }}>
        <SubscriptionHelper
          subscribe={() =>
            this.props.data.subscribeToMore({
              document: SENSOR_SUB,
              variables: {
                id: this.props.simulator.id
              },
              updateQuery: (previousResult, { subscriptionData }) => {
                return Object.assign({}, previousResult, {
                  sensors: subscriptionData.data.sensorsUpdate
                });
              }
            })
          }
        />
        <Row style={{ height: "100%" }}>
          <Col
            sm={4}
            style={{
              display: "flex",
              justifyContent: "space-between",
              flexDirection: "column"
            }}
          >
            Speed
            <Input
              type="select"
              name="select"
              onChange={this._changeSpeed.bind(this)}
              defaultValue={speed}
            >
              {speeds.map(s => (
                <option key={s.value} value={s.value}>
                  {s.label}
                </option>
              ))}
              <option disabled>─────────</option>
              <option disabled>Timed</option>
            </Input>
            <Button
              size="sm"
              color="danger"
              block
              onClick={this._clearContacts.bind(this)}
            >
              Clear
            </Button>
            <Button
              size="sm"
              color="warning"
              block
              onClick={this._stopContacts.bind(this)}
            >
              Stop
            </Button>
            {selectedContacts.length > 0 ? (
              <ContactSelect
                id={sensors.id}
                clearSelection={() => this.setState({ selectedContacts: [] })}
                contacts={selectedContacts}
                client={this.props.client}
              />
            ) : (
              <Fragment>
                <ButtonGroup>
                  <Button
                    active={currentControl === "contacts"}
                    size="sm"
                    color="success"
                    onClick={() =>
                      this.setState({ currentControl: "contacts" })
                    }
                  >
                    Icons
                  </Button>
                  <Button
                    active={currentControl === "extras"}
                    size="sm"
                    color="info"
                    onClick={() => this.setState({ currentControl: "extras" })}
                  >
                    Extras
                  </Button>
                  <Button
                    active={currentControl === "movement"}
                    size="sm"
                    color="primary"
                    onClick={() =>
                      this.setState({ currentControl: "movement" })
                    }
                  >
                    Move
                  </Button>
                </ButtonGroup>
                {currentControl === "extras" && (
                  <ExtraControls
                    sensors={sensors}
                    askForSpeed={askForSpeed}
                    updateAskForSpeed={e => this.setState({ askForSpeed: e })}
                    client={this.props.client}
                    speed={speed}
                    dragStart={this.dragStart}
                  />
                )}
                {currentControl === "contacts" && (
                  <ContactsList
                    sensors={sensors}
                    dragStart={this.dragStart}
                    client={this.props.client}
                  />
                )}
                {currentControl === "movement" && (
                  <MovementCore sensors={sensors} client={this.props.client} />
                )}
              </Fragment>
            )}
          </Col>
          <Col sm={8}>

              <Grid
                mouseover={this.props.hoverContact}
                core
                dimensions={this.state.dimensions}
                offset={SENSORS_OFFSET}
                sensor={sensors.id}
                movement={sensors.movement}
                moveSpeed={speed}
                speeds={speeds}
                askForSpeed={askForSpeed}
                armyContacts={sensors.armyContacts}
                movingContact={movingContact}
                segments={sensors.segments}
                selectedContacts={selectedContacts.map(c => c.id)}
                updateSelectedContacts={contacts =>
                  this.setState({ selectedContacts: contacts })
                }
              />

          </Col>
        </Row>
      </Container>
    );
  }
}

const GRID_QUERY = gql`
  query GetSensors($simulatorId: ID) {
    sensors(simulatorId: $simulatorId, domain: "external") {
      id
      type
      autoTarget
      autoThrusters
      interference
      movement {
        x
        y
        z
      }
      segments {
        segment
        state
      }
      armyContacts {
        id
        name
        size
        icon
        picture
        color
        infrared
        cloaked
        destroyed
        locked
        disabled
      }
    }
  }
`;

export default graphql(GRID_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: { simulatorId: ownProps.simulator.id }
  })
})(withApollo(GridCore));
<|endoftext|>


<commit_before>const common = require('../../../common/api/exports/electron')

// Import common modules.
common.defineProperties(exports)

Object.defineProperties(exports, {
  // Browser side modules, please sort with alphabet order.
  app: {
    enumerable: true,
    get: function () {
      return require('../app')
    }
  },
  autoUpdater: {
    enumerable: true,
    get: function () {
      return require('../auto-updater')
    }
  },
  BrowserWindow: {
    enumerable: true,
    get: function () {
      return require('../browser-window')
    }
  },
  contentTracing: {
    enumerable: true,
    get: function () {
      return require('../content-tracing')
    }
  },
  dialog: {
    enumerable: true,
    get: function () {
      return require('../dialog')
    }
  },
  ipcMain: {
    enumerable: true,
    get: function () {
      return require('../ipc-main')
    }
  },
  globalShortcut: {
    enumerable: true,
    get: function () {
      return require('../global-shortcut')
    }
  },
  Menu: {
    enumerable: true,
    get: function () {
      return require('../menu')
    }
  },
  MenuItem: {
    enumerable: true,
    get: function () {
      return require('../menu-item')
    }
  },
  powerMonitor: {
    enumerable: true,
    get: function () {
      return require('../power-monitor')
    }
  },
  powerSaveBlocker: {
    enumerable: true,
    get: function () {
      return require('../power-save-blocker')
    }
  },
  protocol: {
    enumerable: true,
    get: function () {
      return require('../protocol')
    }
  },
  screen: {
    enumerable: true,
    get: function () {
      return require('../screen')
    }
  },
  session: {
    enumerable: true,
    get: function () {
      return require('../session')
    }
  },
  systemPreferences: {
    enumerable: true,
    get: function () {
      return require('../system-preferences')
    }
  },
  Tray: {
    enumerable: true,
    get: function () {
      return require('../tray')
    }
  },

  // The internal modules, invisible unless you know their names.
  NavigationController: {
    get: function () {
      return require('../navigation-controller')
    }
  },
  webContents: {
    get: function () {
      return require('../web-contents')
    }
  }
})
<commit_msg>webContents is now a public module<commit_after>const common = require('../../../common/api/exports/electron')

// Import common modules.
common.defineProperties(exports)

Object.defineProperties(exports, {
  // Browser side modules, please sort with alphabet order.
  app: {
    enumerable: true,
    get: function () {
      return require('../app')
    }
  },
  autoUpdater: {
    enumerable: true,
    get: function () {
      return require('../auto-updater')
    }
  },
  BrowserWindow: {
    enumerable: true,
    get: function () {
      return require('../browser-window')
    }
  },
  contentTracing: {
    enumerable: true,
    get: function () {
      return require('../content-tracing')
    }
  },
  dialog: {
    enumerable: true,
    get: function () {
      return require('../dialog')
    }
  },
  ipcMain: {
    enumerable: true,
    get: function () {
      return require('../ipc-main')
    }
  },
  globalShortcut: {
    enumerable: true,
    get: function () {
      return require('../global-shortcut')
    }
  },
  Menu: {
    enumerable: true,
    get: function () {
      return require('../menu')
    }
  },
  MenuItem: {
    enumerable: true,
    get: function () {
      return require('../menu-item')
    }
  },
  powerMonitor: {
    enumerable: true,
    get: function () {
      return require('../power-monitor')
    }
  },
  powerSaveBlocker: {
    enumerable: true,
    get: function () {
      return require('../power-save-blocker')
    }
  },
  protocol: {
    enumerable: true,
    get: function () {
      return require('../protocol')
    }
  },
  screen: {
    enumerable: true,
    get: function () {
      return require('../screen')
    }
  },
  session: {
    enumerable: true,
    get: function () {
      return require('../session')
    }
  },
  systemPreferences: {
    enumerable: true,
    get: function () {
      return require('../system-preferences')
    }
  },
  Tray: {
    enumerable: true,
    get: function () {
      return require('../tray')
    }
  },
  webContents: {
    enumerable: true,
    get: function () {
      return require('../web-contents')
    }
  },

  // The internal modules, invisible unless you know their names.
  NavigationController: {
    get: function () {
      return require('../navigation-controller')
    }
  }
})
<|endoftext|>


<commit_before>'use strict'

const {BrowserWindow, ipcMain, webContents} = require('electron')
const {isSameOrigin} = process.atomBinding('v8_util')
const parseFeaturesString = require('../common/parse-features-string')

const hasProp = {}.hasOwnProperty
const frameToGuest = {}

// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) {
  // Check for circular reference.
  if (visited == null) visited = new Set()
  if (visited.has(parent)) return

  visited.add(parent)
  for (const key in parent) {
    if (!hasProp.call(parent, key)) continue
    if (key in child) continue

    const value = parent[key]
    if (typeof value === 'object') {
      child[key] = mergeOptions({}, value, visited)
    } else {
      child[key] = value
    }
  }
  visited.delete(parent)

  return child
}

// Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) {
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }
  if (embedder.browserWindowOptions != null) {
    // Inherit the original options if it is a BrowserWindow.
    mergeOptions(options, embedder.browserWindowOptions)
  } else {
    // Or only inherit web-preferences if it is a webview.
    mergeOptions(options.webPreferences, embedder.getWebPreferences())
  }

  // Disable node integration on child window if disabled on parent window
  if (embedder.getWebPreferences().nodeIntegration === false) {
    options.webPreferences.nodeIntegration = false
  }

  // Enable context isolation on child window if enable on parent window
  if (embedder.getWebPreferences().contextIsolation === true) {
    options.webPreferences.contextIsolation = true
  }

  // Sets correct openerId here to give correct options to 'new-window' event handler
  options.webPreferences.openerId = embedder.id

  return options
}

// Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) {
  // When |embedder| is destroyed we should also destroy attached guest, and if
  // guest is closed by user then we should prevent |embedder| from double
  // closing guest.
  const guestId = guest.webContents.id
  const closedByEmbedder = function () {
    guest.removeListener('closed', closedByUser)
    guest.destroy()
  }
  const closedByUser = function () {
    embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
    embedder.removeListener('render-view-deleted', closedByEmbedder)
  }
  if (!options.webPreferences.sandbox) {
    // These events should only be handled when the guest window is opened by a
    // non-sandboxed renderer for two reasons:
    //
    // - `render-view-deleted` is emitted when the popup is closed by the user,
    //   and that will eventually result in NativeWindow::NotifyWindowClosed
    //   using a dangling pointer since `destroy()` would have been called by
    //   `closeByEmbedded`
    // - No need to emit `ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_` since
    //   there's no renderer code listening to it.,
    embedder.once('render-view-deleted', closedByEmbedder)
    guest.once('closed', closedByUser)
  }
  if (frameName) {
    frameToGuest[frameName] = guest
    guest.frameName = frameName
    guest.once('closed', function () {
      delete frameToGuest[frameName]
    })
  }
  return guestId
}

// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, frameName, options, postData) {
  let guest = frameToGuest[frameName]
  if (frameName && (guest != null)) {
    guest.loadURL(url)
    return guest.webContents.id
  }

  // Remember the embedder window's id.
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }

  guest = new BrowserWindow(options)
  if (!options.webContents || url !== 'about:blank') {
    // We should not call `loadURL` if the window was constructed from an
    // existing webContents(window.open in a sandboxed renderer) and if the url
    // is not 'about:blank'.
    //
    // Navigating to the url when creating the window from an existing
    // webContents would not be necessary(it will navigate there anyway), but
    // apparently there's a bug that allows the child window to be scripted by
    // the opener, even when the child window is from another origin.
    //
    // That's why the second condition(url !== "about:blank") is required: to
    // force `OverrideSiteInstanceForNavigation` to be called and consequently
    // spawn a new renderer if the new window is targeting a different origin.
    //
    // If the URL is "about:blank", then it is very likely that the opener just
    // wants to synchronously script the popup, for example:
    //
    //     let popup = window.open()
    //     popup.document.body.write('
hello
')
    //
    // The above code would not work if a navigation to "about:blank" is done
    // here, since the window would be cleared of all changes in the next tick.
    const loadOptions = {}
    if (postData != null) {
      loadOptions.postData = postData
      loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
      if (postData.length > 0) {
        const postDataFront = postData[0].bytes.toString()
        const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
        if (boundary != null) {
          loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
        }
      }
    }
    guest.loadURL(url, loadOptions)
  }

  return setupGuest(embedder, frameName, guest, options)
}

const getGuestWindow = function (guestContents) {
  let guestWindow = BrowserWindow.fromWebContents(guestContents)
  if (guestWindow == null) {
    const hostContents = guestContents.hostWebContents
    if (hostContents != null) {
      guestWindow = BrowserWindow.fromWebContents(hostContents)
    }
  }
  return guestWindow
}

// Checks whether |sender| can access the |target|:
// 1. Check whether |sender| is the parent of |target|.
// 2. Check whether |sender| has node integration, if so it is allowed to
//    do anything it wants.
// 3. Check whether the origins match.
//
// However it allows a child window without node integration but with same
// origin to do anything it wants, when its opener window has node integration.
// The W3C does not have anything on this, but from my understanding of the
// security model of |window.opener|, this should be fine.
const canAccessWindow = function (sender, target) {
  return (target.getWebPreferences().openerId === sender.id) ||
         (sender.getWebPreferences().nodeIntegration === true) ||
         isSameOrigin(sender.getURL(), target.getURL())
}

// Routed window.open messages with raw options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
  if (url == null || url === '') url = 'about:blank'
  if (frameName == null) frameName = ''
  if (features == null) features = ''

  const options = {}

  const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
  const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload']
  const disposition = 'new-window'

  // Used to store additional features
  const additionalFeatures = []

  // Parse the features
  parseFeaturesString(features, function (key, value) {
    if (value === undefined) {
      additionalFeatures.push(key)
    } else {
      if (webPreferences.includes(key)) {
        if (options.webPreferences == null) {
          options.webPreferences = {}
        }
        options.webPreferences[key] = value
      } else {
        options[key] = value
      }
    }
  })
  if (options.left) {
    if (options.x == null) {
      options.x = options.left
    }
  }
  if (options.top) {
    if (options.y == null) {
      options.y = options.top
    }
  }
  if (options.title == null) {
    options.title = frameName
  }
  if (options.width == null) {
    options.width = 800
  }
  if (options.height == null) {
    options.height = 600
  }

  for (const name of ints) {
    if (options[name] != null) {
      options[name] = parseInt(options[name], 10)
    }
  }

  ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
               url, frameName, disposition, options, additionalFeatures)
})

// Routed window.open messages with fully parsed options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, frameName,
                                                                  disposition, options,
                                                                  additionalFeatures, postData) {
  options = mergeBrowserWindowOptions(event.sender, options)
  const {webContents} = options
  event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures)
  const newGuest = event.newGuest
  if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
    if (newGuest !== undefined && newGuest !== null) {
      if (webContents === newGuest.webContents) {
        event.defaultPrevented = false
      }
      event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
    } else {
      event.returnValue = null
    }
  } else {
    event.returnValue = createGuest(event.sender, url, frameName, options, postData)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) guestWindow.destroy()
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) {
    event.returnValue = guestWindow[method](...args)
  } else {
    event.returnValue = null
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
  if (targetOrigin == null) {
    targetOrigin = '*'
  }

  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  // The W3C does not seem to have word on how postMessage should work when the
  // origins do not match, so we do not do |canAccessWindow| check here since
  // postMessage across origins is useful and not harmful.
  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
    const sourceId = event.sender.id
    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (canAccessWindow(event.sender, guestContents)) {
    guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (canAccessWindow(event.sender, guestContents)) {
    event.returnValue = guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
  }
})
<commit_msg>Change webContents -> options.webContents<commit_after>'use strict'

const {BrowserWindow, ipcMain, webContents} = require('electron')
const {isSameOrigin} = process.atomBinding('v8_util')
const parseFeaturesString = require('../common/parse-features-string')

const hasProp = {}.hasOwnProperty
const frameToGuest = {}

// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) {
  // Check for circular reference.
  if (visited == null) visited = new Set()
  if (visited.has(parent)) return

  visited.add(parent)
  for (const key in parent) {
    if (!hasProp.call(parent, key)) continue
    if (key in child) continue

    const value = parent[key]
    if (typeof value === 'object') {
      child[key] = mergeOptions({}, value, visited)
    } else {
      child[key] = value
    }
  }
  visited.delete(parent)

  return child
}

// Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) {
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }
  if (embedder.browserWindowOptions != null) {
    // Inherit the original options if it is a BrowserWindow.
    mergeOptions(options, embedder.browserWindowOptions)
  } else {
    // Or only inherit web-preferences if it is a webview.
    mergeOptions(options.webPreferences, embedder.getWebPreferences())
  }

  // Disable node integration on child window if disabled on parent window
  if (embedder.getWebPreferences().nodeIntegration === false) {
    options.webPreferences.nodeIntegration = false
  }

  // Enable context isolation on child window if enable on parent window
  if (embedder.getWebPreferences().contextIsolation === true) {
    options.webPreferences.contextIsolation = true
  }

  // Sets correct openerId here to give correct options to 'new-window' event handler
  options.webPreferences.openerId = embedder.id

  return options
}

// Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) {
  // When |embedder| is destroyed we should also destroy attached guest, and if
  // guest is closed by user then we should prevent |embedder| from double
  // closing guest.
  const guestId = guest.webContents.id
  const closedByEmbedder = function () {
    guest.removeListener('closed', closedByUser)
    guest.destroy()
  }
  const closedByUser = function () {
    embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
    embedder.removeListener('render-view-deleted', closedByEmbedder)
  }
  if (!options.webPreferences.sandbox) {
    // These events should only be handled when the guest window is opened by a
    // non-sandboxed renderer for two reasons:
    //
    // - `render-view-deleted` is emitted when the popup is closed by the user,
    //   and that will eventually result in NativeWindow::NotifyWindowClosed
    //   using a dangling pointer since `destroy()` would have been called by
    //   `closeByEmbedded`
    // - No need to emit `ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_` since
    //   there's no renderer code listening to it.,
    embedder.once('render-view-deleted', closedByEmbedder)
    guest.once('closed', closedByUser)
  }
  if (frameName) {
    frameToGuest[frameName] = guest
    guest.frameName = frameName
    guest.once('closed', function () {
      delete frameToGuest[frameName]
    })
  }
  return guestId
}

// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, frameName, options, postData) {
  let guest = frameToGuest[frameName]
  if (frameName && (guest != null)) {
    guest.loadURL(url)
    return guest.webContents.id
  }

  // Remember the embedder window's id.
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }

  guest = new BrowserWindow(options)
  if (!options.webContents || url !== 'about:blank') {
    // We should not call `loadURL` if the window was constructed from an
    // existing webContents(window.open in a sandboxed renderer) and if the url
    // is not 'about:blank'.
    //
    // Navigating to the url when creating the window from an existing
    // webContents would not be necessary(it will navigate there anyway), but
    // apparently there's a bug that allows the child window to be scripted by
    // the opener, even when the child window is from another origin.
    //
    // That's why the second condition(url !== "about:blank") is required: to
    // force `OverrideSiteInstanceForNavigation` to be called and consequently
    // spawn a new renderer if the new window is targeting a different origin.
    //
    // If the URL is "about:blank", then it is very likely that the opener just
    // wants to synchronously script the popup, for example:
    //
    //     let popup = window.open()
    //     popup.document.body.write('
hello
')
    //
    // The above code would not work if a navigation to "about:blank" is done
    // here, since the window would be cleared of all changes in the next tick.
    const loadOptions = {}
    if (postData != null) {
      loadOptions.postData = postData
      loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
      if (postData.length > 0) {
        const postDataFront = postData[0].bytes.toString()
        const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
        if (boundary != null) {
          loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
        }
      }
    }
    guest.loadURL(url, loadOptions)
  }

  return setupGuest(embedder, frameName, guest, options)
}

const getGuestWindow = function (guestContents) {
  let guestWindow = BrowserWindow.fromWebContents(guestContents)
  if (guestWindow == null) {
    const hostContents = guestContents.hostWebContents
    if (hostContents != null) {
      guestWindow = BrowserWindow.fromWebContents(hostContents)
    }
  }
  return guestWindow
}

// Checks whether |sender| can access the |target|:
// 1. Check whether |sender| is the parent of |target|.
// 2. Check whether |sender| has node integration, if so it is allowed to
//    do anything it wants.
// 3. Check whether the origins match.
//
// However it allows a child window without node integration but with same
// origin to do anything it wants, when its opener window has node integration.
// The W3C does not have anything on this, but from my understanding of the
// security model of |window.opener|, this should be fine.
const canAccessWindow = function (sender, target) {
  return (target.getWebPreferences().openerId === sender.id) ||
         (sender.getWebPreferences().nodeIntegration === true) ||
         isSameOrigin(sender.getURL(), target.getURL())
}

// Routed window.open messages with raw options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
  if (url == null || url === '') url = 'about:blank'
  if (frameName == null) frameName = ''
  if (features == null) features = ''

  const options = {}

  const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
  const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload']
  const disposition = 'new-window'

  // Used to store additional features
  const additionalFeatures = []

  // Parse the features
  parseFeaturesString(features, function (key, value) {
    if (value === undefined) {
      additionalFeatures.push(key)
    } else {
      if (webPreferences.includes(key)) {
        if (options.webPreferences == null) {
          options.webPreferences = {}
        }
        options.webPreferences[key] = value
      } else {
        options[key] = value
      }
    }
  })
  if (options.left) {
    if (options.x == null) {
      options.x = options.left
    }
  }
  if (options.top) {
    if (options.y == null) {
      options.y = options.top
    }
  }
  if (options.title == null) {
    options.title = frameName
  }
  if (options.width == null) {
    options.width = 800
  }
  if (options.height == null) {
    options.height = 600
  }

  for (const name of ints) {
    if (options[name] != null) {
      options[name] = parseInt(options[name], 10)
    }
  }

  ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
               url, frameName, disposition, options, additionalFeatures)
})

// Routed window.open messages with fully parsed options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, frameName,
                                                                  disposition, options,
                                                                  additionalFeatures, postData) {
  options = mergeBrowserWindowOptions(event.sender, options)
  event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures)
  const newGuest = event.newGuest
  if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
    if (newGuest !== undefined && newGuest !== null) {
      if (options.webContents === newGuest.webContents) {
        event.defaultPrevented = false
      }
      event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
    } else {
      event.returnValue = null
    }
  } else {
    event.returnValue = createGuest(event.sender, url, frameName, options, postData)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) guestWindow.destroy()
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) {
    event.returnValue = guestWindow[method](...args)
  } else {
    event.returnValue = null
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
  if (targetOrigin == null) {
    targetOrigin = '*'
  }

  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  // The W3C does not seem to have word on how postMessage should work when the
  // origins do not match, so we do not do |canAccessWindow| check here since
  // postMessage across origins is useful and not harmful.
  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
    const sourceId = event.sender.id
    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (canAccessWindow(event.sender, guestContents)) {
    guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (canAccessWindow(event.sender, guestContents)) {
    event.returnValue = guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
  }
})
<|endoftext|>


<commit_before>"use strict";
module.exports = function (SIP) {

var RegisterContext;

RegisterContext = function (ua) {
  var params = {},
      regId = 1;

  this.registrar = ua.configuration.registrarServer;
  this.expires = ua.configuration.registerExpires;

  // Contact header
  this.contact = ua.contact.toString();

  if(regId) {
    this.contact += ';reg-id='+ regId;
    this.contact += ';+sip.instance="<urn:uuid:'+ ua.configuration.instanceId+'>"';
  }

  // Call-ID and CSeq values RFC3261 10.2
  this.call_id = SIP.Utils.createRandomToken(22);
  this.cseq = 80;

  this.to_uri = ua.configuration.uri;

  params.to_uri = this.to_uri;
  params.call_id = this.call_id;
  params.cseq = this.cseq;

  // Extends ClientContext
  SIP.Utils.augment(this, SIP.ClientContext, [ua, 'REGISTER', this.registrar, {params: params}]);

  this.registrationTimer = null;
  this.registrationExpiredTimer = null;

  // Set status
  this.registered = false;

  this.logger = ua.getLogger('sip.registercontext');
};

RegisterContext.prototype = {
  register: function (options) {
    var self = this, extraHeaders;

    // Handle Options
    this.options = options || {};
    extraHeaders = (this.options.extraHeaders || []).slice();
    extraHeaders.push('Contact: ' + this.contact + ';expires=' + this.expires);
    extraHeaders.push('Allow: ' + SIP.Utils.getAllowedMethods(this.ua));

    // Save original extraHeaders to be used in .close
    this.closeHeaders = options.closeWithHeaders ?
      (this.options.extraHeaders || []).slice() : [];

    this.receiveResponse = function(response) {
      var contact, expires,
        contacts = response.getHeaders('contact').length,
        cause;

      // Discard responses to older REGISTER/un-REGISTER requests.
      if(response.cseq !== this.cseq) {
        return;
      }

      // Clear registration timer
      if (this.registrationTimer !== null) {
        SIP.Timers.clearTimeout(this.registrationTimer);
        this.registrationTimer = null;
      }

      switch(true) {
        case /^1[0-9]{2}$/.test(response.status_code):
          this.emit('progress', response);
          break;
        case /^2[0-9]{2}$/.test(response.status_code):
          this.emit('accepted', response);

          if(response.hasHeader('expires')) {
            expires = response.getHeader('expires');
          }

          if (this.registrationExpiredTimer !== null) {
            SIP.Timers.clearTimeout(this.registrationExpiredTimer);
            this.registrationExpiredTimer = null;
          }

          // Search the Contact pointing to us and update the expires value accordingly.
          if (!contacts) {
            this.logger.warn('no Contact header in response to REGISTER, response ignored');
            break;
          }

          while(contacts--) {
            contact = response.parseHeader('contact', contacts);
            if(contact.uri.user === this.ua.contact.uri.user) {
              expires = contact.getParam('expires');
              break;
            } else {
              contact = null;
            }
          }

          if (!contact) {
            this.logger.warn('no Contact header pointing to us, response ignored');
            break;
          }

          if(!expires) {
            expires = this.expires;
          }

          // Re-Register before the expiration interval has elapsed.
          // For that, decrease the expires value. ie: 3 seconds
          this.registrationTimer = SIP.Timers.setTimeout(function() {
            self.registrationTimer = null;
            self.register(self.options);
          }, (expires * 1000) - 3000);
          this.registrationExpiredTimer = SIP.Timers.setTimeout(function () {
            self.logger.warn('registration expired');
            if (self.registered) {
              self.unregistered(null, SIP.C.causes.EXPIRES);
            }
          }, expires * 1000);

          //Save gruu values
          if (contact.hasParam('temp-gruu')) {
            this.ua.contact.temp_gruu = SIP.URI.parse(contact.getParam('temp-gruu').replace(/"/g,''));
          }
          if (contact.hasParam('pub-gruu')) {
            this.ua.contact.pub_gruu = SIP.URI.parse(contact.getParam('pub-gruu').replace(/"/g,''));
          }

          this.registered = true;
          this.emit('registered', response || null);
          break;
        // Interval too brief RFC3261 10.2.8
        case /^423$/.test(response.status_code):
          if(response.hasHeader('min-expires')) {
            // Increase our registration interval to the suggested minimum
            this.expires = response.getHeader('min-expires');
            // Attempt the registration again immediately
            this.register(this.options);
          } else { //This response MUST contain a Min-Expires header field
            this.logger.warn('423 response received for REGISTER without Min-Expires');
            this.registrationFailure(response, SIP.C.causes.SIP_FAILURE_CODE);
          }
          break;
        default:
          cause = SIP.Utils.sipErrorCause(response.status_code);
          this.registrationFailure(response, cause);
      }
    };

    this.onRequestTimeout = function() {
      this.registrationFailure(null, SIP.C.causes.REQUEST_TIMEOUT);
    };

    this.onTransportError = function() {
      this.registrationFailure(null, SIP.C.causes.CONNECTION_ERROR);
    };

    this.cseq++;
    this.request.cseq = this.cseq;
    this.request.setHeader('cseq', this.cseq + ' REGISTER');
    this.request.extraHeaders = extraHeaders;
    this.send();
  },

  registrationFailure: function (response, cause) {
    this.emit('failed', response || null, cause || null);
  },

  onTransportClosed: function() {
    this.registered_before = this.registered;
    if (this.registrationTimer !== null) {
      SIP.Timers.clearTimeout(this.registrationTimer);
      this.registrationTimer = null;
    }

    if (this.registrationExpiredTimer !== null) {
      SIP.Timers.clearTimeout(this.registrationExpiredTimer);
      this.registrationExpiredTimer = null;
    }

    if(this.registered) {
      this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
    }
  },

  onTransportConnected: function() {
    this.register(this.options);
  },

  close: function() {
    var options = {
      all: false,
      extraHeaders: this.closeHeaders
    };

    this.registered_before = this.registered;
    this.unregister(options);
  },

  unregister: function(options) {
    var extraHeaders;

    if(!this.registered) {
      this.logger.warn('already unregistered');
      return;
    }

    options = options || {};
    extraHeaders = (options.extraHeaders || []).slice();

    this.registered = false;

    // Clear the registration timer.
    if (this.registrationTimer !== null) {
      SIP.Timers.clearTimeout(this.registrationTimer);
      this.registrationTimer = null;
    }

    if(options.all) {
      extraHeaders.push('Contact: *');
      extraHeaders.push('Expires: 0');
    } else {
      extraHeaders.push('Contact: '+ this.contact + ';expires=0');
    }

    this.receiveResponse = function(response) {
      var cause;

      switch(true) {
        case /^1[0-9]{2}$/.test(response.status_code):
          this.emit('progress', response);
          break;
        case /^2[0-9]{2}$/.test(response.status_code):
          this.emit('accepted', response);
          if (this.registrationExpiredTimer !== null) {
            SIP.Timers.clearTimeout(this.registrationExpiredTimer);
            this.registrationExpiredTimer = null;
          }
          this.unregistered(response);
          break;
        default:
          cause = SIP.Utils.sipErrorCause(response.status_code);
          this.unregistered(response,cause);
      }
    };

    this.onRequestTimeout = function() {
      // Not actually unregistered...
      //this.unregistered(null, SIP.C.causes.REQUEST_TIMEOUT);
    };

    this.onTransportError = function() {
      // Not actually unregistered...
      //this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
    };

    this.cseq++;
    this.request.cseq = this.cseq;
    this.request.setHeader('cseq', this.cseq + ' REGISTER');
    this.request.extraHeaders = extraHeaders;

    this.send();
  },

  unregistered: function(response, cause) {
    this.registered = false;
    this.emit('unregistered', response || null, cause || null);
  }

};

SIP.RegisterContext = RegisterContext;
};
<commit_msg>RegisterContext closeWithHeaders failed without options object<commit_after>"use strict";
module.exports = function (SIP) {

var RegisterContext;

RegisterContext = function (ua) {
  var params = {},
      regId = 1;

  this.registrar = ua.configuration.registrarServer;
  this.expires = ua.configuration.registerExpires;

  // Contact header
  this.contact = ua.contact.toString();

  if(regId) {
    this.contact += ';reg-id='+ regId;
    this.contact += ';+sip.instance="<urn:uuid:'+ ua.configuration.instanceId+'>"';
  }

  // Call-ID and CSeq values RFC3261 10.2
  this.call_id = SIP.Utils.createRandomToken(22);
  this.cseq = 80;

  this.to_uri = ua.configuration.uri;

  params.to_uri = this.to_uri;
  params.call_id = this.call_id;
  params.cseq = this.cseq;

  // Extends ClientContext
  SIP.Utils.augment(this, SIP.ClientContext, [ua, 'REGISTER', this.registrar, {params: params}]);

  this.registrationTimer = null;
  this.registrationExpiredTimer = null;

  // Set status
  this.registered = false;

  this.logger = ua.getLogger('sip.registercontext');
};

RegisterContext.prototype = {
  register: function (options) {
    var self = this, extraHeaders;

    // Handle Options
    this.options = options || {};
    extraHeaders = (this.options.extraHeaders || []).slice();
    extraHeaders.push('Contact: ' + this.contact + ';expires=' + this.expires);
    extraHeaders.push('Allow: ' + SIP.Utils.getAllowedMethods(this.ua));

    // Save original extraHeaders to be used in .close
    this.closeHeaders = this.options.closeWithHeaders ?
      (this.options.extraHeaders || []).slice() : [];

    this.receiveResponse = function(response) {
      var contact, expires,
        contacts = response.getHeaders('contact').length,
        cause;

      // Discard responses to older REGISTER/un-REGISTER requests.
      if(response.cseq !== this.cseq) {
        return;
      }

      // Clear registration timer
      if (this.registrationTimer !== null) {
        SIP.Timers.clearTimeout(this.registrationTimer);
        this.registrationTimer = null;
      }

      switch(true) {
        case /^1[0-9]{2}$/.test(response.status_code):
          this.emit('progress', response);
          break;
        case /^2[0-9]{2}$/.test(response.status_code):
          this.emit('accepted', response);

          if(response.hasHeader('expires')) {
            expires = response.getHeader('expires');
          }

          if (this.registrationExpiredTimer !== null) {
            SIP.Timers.clearTimeout(this.registrationExpiredTimer);
            this.registrationExpiredTimer = null;
          }

          // Search the Contact pointing to us and update the expires value accordingly.
          if (!contacts) {
            this.logger.warn('no Contact header in response to REGISTER, response ignored');
            break;
          }

          while(contacts--) {
            contact = response.parseHeader('contact', contacts);
            if(contact.uri.user === this.ua.contact.uri.user) {
              expires = contact.getParam('expires');
              break;
            } else {
              contact = null;
            }
          }

          if (!contact) {
            this.logger.warn('no Contact header pointing to us, response ignored');
            break;
          }

          if(!expires) {
            expires = this.expires;
          }

          // Re-Register before the expiration interval has elapsed.
          // For that, decrease the expires value. ie: 3 seconds
          this.registrationTimer = SIP.Timers.setTimeout(function() {
            self.registrationTimer = null;
            self.register(self.options);
          }, (expires * 1000) - 3000);
          this.registrationExpiredTimer = SIP.Timers.setTimeout(function () {
            self.logger.warn('registration expired');
            if (self.registered) {
              self.unregistered(null, SIP.C.causes.EXPIRES);
            }
          }, expires * 1000);

          //Save gruu values
          if (contact.hasParam('temp-gruu')) {
            this.ua.contact.temp_gruu = SIP.URI.parse(contact.getParam('temp-gruu').replace(/"/g,''));
          }
          if (contact.hasParam('pub-gruu')) {
            this.ua.contact.pub_gruu = SIP.URI.parse(contact.getParam('pub-gruu').replace(/"/g,''));
          }

          this.registered = true;
          this.emit('registered', response || null);
          break;
        // Interval too brief RFC3261 10.2.8
        case /^423$/.test(response.status_code):
          if(response.hasHeader('min-expires')) {
            // Increase our registration interval to the suggested minimum
            this.expires = response.getHeader('min-expires');
            // Attempt the registration again immediately
            this.register(this.options);
          } else { //This response MUST contain a Min-Expires header field
            this.logger.warn('423 response received for REGISTER without Min-Expires');
            this.registrationFailure(response, SIP.C.causes.SIP_FAILURE_CODE);
          }
          break;
        default:
          cause = SIP.Utils.sipErrorCause(response.status_code);
          this.registrationFailure(response, cause);
      }
    };

    this.onRequestTimeout = function() {
      this.registrationFailure(null, SIP.C.causes.REQUEST_TIMEOUT);
    };

    this.onTransportError = function() {
      this.registrationFailure(null, SIP.C.causes.CONNECTION_ERROR);
    };

    this.cseq++;
    this.request.cseq = this.cseq;
    this.request.setHeader('cseq', this.cseq + ' REGISTER');
    this.request.extraHeaders = extraHeaders;
    this.send();
  },

  registrationFailure: function (response, cause) {
    this.emit('failed', response || null, cause || null);
  },

  onTransportClosed: function() {
    this.registered_before = this.registered;
    if (this.registrationTimer !== null) {
      SIP.Timers.clearTimeout(this.registrationTimer);
      this.registrationTimer = null;
    }

    if (this.registrationExpiredTimer !== null) {
      SIP.Timers.clearTimeout(this.registrationExpiredTimer);
      this.registrationExpiredTimer = null;
    }

    if(this.registered) {
      this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
    }
  },

  onTransportConnected: function() {
    this.register(this.options);
  },

  close: function() {
    var options = {
      all: false,
      extraHeaders: this.closeHeaders
    };

    this.registered_before = this.registered;
    this.unregister(options);
  },

  unregister: function(options) {
    var extraHeaders;

    if(!this.registered) {
      this.logger.warn('already unregistered');
      return;
    }

    options = options || {};
    extraHeaders = (options.extraHeaders || []).slice();

    this.registered = false;

    // Clear the registration timer.
    if (this.registrationTimer !== null) {
      SIP.Timers.clearTimeout(this.registrationTimer);
      this.registrationTimer = null;
    }

    if(options.all) {
      extraHeaders.push('Contact: *');
      extraHeaders.push('Expires: 0');
    } else {
      extraHeaders.push('Contact: '+ this.contact + ';expires=0');
    }

    this.receiveResponse = function(response) {
      var cause;

      switch(true) {
        case /^1[0-9]{2}$/.test(response.status_code):
          this.emit('progress', response);
          break;
        case /^2[0-9]{2}$/.test(response.status_code):
          this.emit('accepted', response);
          if (this.registrationExpiredTimer !== null) {
            SIP.Timers.clearTimeout(this.registrationExpiredTimer);
            this.registrationExpiredTimer = null;
          }
          this.unregistered(response);
          break;
        default:
          cause = SIP.Utils.sipErrorCause(response.status_code);
          this.unregistered(response,cause);
      }
    };

    this.onRequestTimeout = function() {
      // Not actually unregistered...
      //this.unregistered(null, SIP.C.causes.REQUEST_TIMEOUT);
    };

    this.onTransportError = function() {
      // Not actually unregistered...
      //this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
    };

    this.cseq++;
    this.request.cseq = this.cseq;
    this.request.setHeader('cseq', this.cseq + ' REGISTER');
    this.request.extraHeaders = extraHeaders;

    this.send();
  },

  unregistered: function(response, cause) {
    this.registered = false;
    this.emit('unregistered', response || null, cause || null);
  }

};

SIP.RegisterContext = RegisterContext;
};
<|endoftext|>


<commit_before>if(!this.JSON){JSON=function(){function f(n){return n<10?'0'+n:n;}
Date.prototype.toJSON=function(){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function stringify(value,whitelist){var a,i,k,l,r=/["\\\x00-\x1f\x7f-\x9f]/g,v;switch(typeof value){case'string':return r.test(value)?'"'+value.replace(r,function(a){var c=m[a];if(c){return c;}
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+
(c%16).toString(16);})+'"':'"'+value+'"';case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
if(typeof value.toJSON==='function'){return stringify(value.toJSON());}
a=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){l=value.length;for(i=0;i<l;i+=1){a.push(stringify(value[i],whitelist)||'null');}
return'['+a.join(',')+']';}
if(whitelist){l=whitelist.length;for(i=0;i<l;i+=1){k=whitelist[i];if(typeof k==='string'){v=stringify(value[k],whitelist);if(v){a.push(stringify(k)+':'+v);}}}}else{for(k in value){if(typeof k==='string'){v=stringify(value[k],whitelist);if(v){a.push(stringify(k)+':'+v);}}}}
return'{'+a.join(',')+'}';}}
return{stringify:stringify,parse:function(text,filter){var j;function walk(k,v){var i,n;if(v&&typeof v==='object'){for(i in v){if(Object.prototype.hasOwnProperty.apply(v,[i])){n=walk(i,v[i]);if(n!==undefined){v[i]=n;}}}}
return filter(k,v);}
if(/^[\],:{}\s]*$/.test(text.replace(/\\./g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof filter==='function'?walk('',j):j;}
throw new SyntaxError('parseJSON');}};}();}<commit_msg>feat(shim): Dropping JSON shim<commit_after><|endoftext|>


<commit_before>import express from 'express'
import _ from 'lodash'
import dotty from 'dotty'
import bug from 'debug'
import changeCase from 'change-case'
import validate from '../../lib/validate'
import rateLimit from '../../lib/rate-limit'
import slackApi from '../../lib/slack'
import { exitWithError, getStrings } from '../../lib/helpers'

const debug = bug('SIR:apply')
const router = express.Router()

const channel = process.env.SLACK_CHANNEL;
const botName = process.env.SLACK_BOT_NAME || 'SIR';
const slackUrl = process.env.SLACK_WEBHOOK_URL
const slack = slackUrl ? slackApi(slackUrl) : exitWithError('Please set SLACK_WEBHOOK_URL environment variable.')

const getUser = _.partialRight(dotty.get, 'session.passport.user')

router.get('/', validate, (req, res) => {
  const user = getUser(req)
  const strings = getStrings()

  strings.apply.form.fullName.value = user.displayName
  strings.apply.form.email.value = user.emails[0].value

  res.render('apply', _.assign({}, strings.apply, user))
})

router.post('/', validate, rateLimit(), (req, res) => {
  const user = getUser(req)

  debug('Received application from "%s <%s>"', user.displayName, user.emails[0].value)

  res.redirect('/thanks')

  slack({
    channel: channel,
    username: botName,
    icon_url: req.originUri + 'images/bot.png',
    attachments: [{
      fallback: user.displayName + ' wants to join Slack',
      author_name: user.displayName,
      author_link: user.url,
      author_icon: dotty.get(user, 'image.url'),
      color: '#28f428',
      pretext: 'New invite request:',
      text: req.body.comments ? '"' + req.body.comments + '"' : undefined,
      fields: _.map(
        _.pairs(_.omit(req.body, 'comments')),
        _.flow(
          x => x,
          _.partialRight(_.map, (str, i) => {
            return i ? str : changeCase.title(str)
          }),
          _.partial(_.zipObject, ['title', 'value']),
          _.partialRight(_.assign, { short: true })
        )
      )
    }]
  })
})

export default router
<commit_msg>add some comments and transform arrays<commit_after>import express from 'express'
import _ from 'lodash'
import dotty from 'dotty'
import bug from 'debug'
import changeCase from 'change-case'
import validate from '../../lib/validate'
import rateLimit from '../../lib/rate-limit'
import slackApi from '../../lib/slack'
import { exitWithError, getStrings } from '../../lib/helpers'

const debug = bug('SIR:apply')
const router = express.Router()

const channel = process.env.SLACK_CHANNEL;
const botName = process.env.SLACK_BOT_NAME || 'SIR';
const slackUrl = process.env.SLACK_WEBHOOK_URL
const slack = slackUrl ? slackApi(slackUrl) : exitWithError('Please set SLACK_WEBHOOK_URL environment variable.')

const getUser = _.partialRight(dotty.get, 'session.passport.user')

router.get('/', validate, (req, res) => {
  const user = getUser(req)
  const strings = getStrings()

  strings.apply.form.fullName.value = user.displayName
  strings.apply.form.email.value = user.emails[0].value

  res.render('apply', _.assign({}, strings.apply, user))
})

router.post('/', validate, rateLimit(), (req, res) => {
  const user = getUser(req)

  debug('Received application from "%s <%s>"', user.displayName, user.emails[0].value)

  res.redirect('/thanks')

  slack({
    channel: channel,
    username: botName,
    icon_url: req.originUri + 'images/bot.png',
    attachments: [{
      fallback: user.displayName + ' wants to join Slack',
      author_name: user.displayName,
      author_link: user.url,
      author_icon: dotty.get(user, 'image.url'),
      color: '#28f428',
      pretext: 'New invite request:',
      text: req.body.comments ? `${req.body.comments}` : undefined,
      fields: _.map(
        // omit comments because it's used for "text" above
        _.pairs(_.omit(req.body, 'comments')),
        // transform the field data
        _.flow(
          // necessary for unknown reasons
          x => x,
          // convert field names to title case
          _.partialRight(_.map, (str, i) => {
            return i ? str : changeCase.title(str)
          }),
          // convert array values (e.g. checbox fields) into a string
          x => {
            if (_.isArray(x[1])) {
              x[1] = x[1].join(', ')
            }
            return x
          },
          // combine title and value into an object
          _.partial(_.zipObject, ['title', 'value']),
          // add "short" property to object
          _.partialRight(_.assign, { short: true })
        )
      )
    }]
  })
})

export default router
<|endoftext|>


<commit_before>/*
 * Chrome-Last.fm-Scrobbler archive.org Connector by Malachi Soord
 *                (based on George Pollard's bandcamp connctor)
 * v0.1
 */
'use strict';

var lastTrack = null;

$(function () {
	// bind page unload function to discard current "now listening"
	cancel();
});

var durationElapsed = '#jw6_controlbar_elapsed';
var durationTotal = '#jw6_controlbar_duration';
var durationRegex = /(\d+):(\d+)/;

function parseDuration(elapsed, total) {
	try {
		var mEla = durationRegex.exec(elapsed);
		var mTot = durationRegex.exec(total);
		return {
			current: parseInt(mEla[1], 10) * 60 + parseInt(mEla[2], 10),
			total: parseInt(mTot[1], 10) * 60 + parseInt(mTot[2], 10)
		};
	} catch (err) {
		return 0;
	}
}

function parseArtist() {
	return $('span.key:contains("Artist/Composer:"), span.key:contains("Band/Artist:")').next().text();
}

function parseTitle() {

	// Get title directly from player
	var title = $('.playing > .ttl').text();

	// Some titles are stored as artist - track # - title so strip out non-title elements
	var parts = title.split('-');
	if (parts.length === 3 && parts[0].trim() === parseArtist()) {
		title = parts[2].trim();
	}

	return title;
}

function parseAlbum() {
	var album = $('.x-archive-meta-title').text();

	// Remove artist from album
	var parts = album.split('-');
	if (parts.length > 0 && parts[0].trim() === parseArtist()) {
		album = album.substr(album.indexOf('-') + 1).trim();
	}

	return album;
}

function cancel() {
	$(window).unload(function () {
		// reset the background scrobbler song data
		chrome.runtime.sendMessage({
			type: 'reset'
		});
		return true;
	});
}

// console.log('Archive.org: loaded');

$(durationElapsed).live('DOMSubtreeModified', function () {

	var duration = parseDuration($(durationElapsed).text(), $(durationTotal).text());

	// console.log('duration - ' + duration.current + ' / ' + duration.total);

	if (duration.current > 0) { // it's playing

		var artist = parseArtist();
		var track = parseTitle();
		var album = parseAlbum();

		if (lastTrack !== track) {
			lastTrack = track;

			console.log('Archive.org: scrobbling - Artist: ' + artist + '; Album:  ' + album + '; Track: ' + track + '; duration: ' + duration.total);
			chrome.runtime.sendMessage({
				type: 'validate',
				artist: artist,
				track: track
			}, function (response) {
				if (response !== false) {
					chrome.runtime.sendMessage({
						type: 'nowPlaying',
						artist: response.artist,
						track: response.track,
						duration: duration.total,
						album: album
					});
				} else {
					chrome.runtime.sendMessage({
						type: 'nowPlaying',
						duration: duration.total
					});
				}
			});
		}
	}
});
<commit_msg>Delete old connector<commit_after><|endoftext|>