<commit_before>module.exports = {
  plugins: ["eslint-comments", "import", "prettier", "unicorn"],
  extends: [
    "eslint:recommended",
    "problems",
    "plugin:eslint-comments/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",

    "plugin:prettier/recommended", // Must be last
  ],
  rules: {
    // Force removal of unused eslint-disable directives, which can help prevent
    // developers from reintroducing a bad pattern that was previously fixed but
    // where the directive was left in place.
    "eslint-comments/no-unused-disable": "error",

    // Enforce consistency in where import groups appear. The linter should just
    // take care of this kind of thing.
    "import/first": "error",

    // Enforce conventions around import groups. (Source files like js and ts
    // shouldn't require an extension while assets like json, image files, etc.
    // should.) Another thing the linter should just take care of for you.
    "import/extensions": ["error", "never", { json: "always" }],

    // Enforce conventions around import groups. The linter should just take
    // care of this kind of thing.
    "import/newline-after-import": "error",

    // Enforce conventions around import group ordering. The linter should just
    // take care of this kind of thing.
    "import/order": [
      "error",
      {
        alphabetize: { order: "asc" },
        groups: ["builtin", "external", "parent", "sibling", "index"],
        "newlines-between": "never",
      },
    ],

    // Strip path segments that don't modify the final import, e.g.
    // "./..//app" => "../app"
    "import/no-useless-path-segments": "error",

    "no-unused-vars": [
      "error",
      {
        // Ignore unused arguments that are prefixed with an underscore; naming
        // unused variables is sometimes helpful for forcing variadic functions
        // to behave the way you want, or for documenting arguments that are
        // likely to come into use in the future.
        args: "after-used",
        argsIgnorePattern: "^_",
      },
    ],

    // FIXME(ndhoule): enabled by eslint-config-problems. Unfortunately, an
    // issue in @typescript-eslint/eslint-config prevents this rule from being
    // applied consistently across both JavaScript and TypeScript, so it's
    // disabled for now. Enable this and the matching TypeScript rule when the
    // issue is fixed:
    // https://github.com/typescript-eslint/typescript-eslint/issues/1856
    "no-use-before-define": "off",

    "no-useless-constructor": "error",

    // `return await` is almost always a mistake. The only time when it's
    // acceptable is within a `try` block (which this rule permits).
    "no-return-await": "error",

    // This rule is pretty broken; for more info, see:
    // https://github.com/eslint/eslint/issues/11899
    "require-atomic-updates": "off",

    // Sort import members (and destructured `require`s) alphabetically for
    // consistency. (For declaration sorting, see `import/order`, which does a
    // more comprehensive job of sorting import declarations.)
    "sort-imports": [
      "error",
      {
        ignoreCase: false,
        ignoreDeclarationSort: true,
        ignoreMemberSort: false,
      },
    ],

    // Enabled by eslint-config-problems; because it's enabled automatically by
    // tools like TypeScript, Babel/Webpack, etc., it's generally not necessary
    // to enable it explicitly.
    strict: "off",

    "unicorn/expiring-todo-comments": "error",
  },
};
<commit_msg>feat: ban global isNaN, parseInt, parseFloat functions<commit_after>module.exports = {
  plugins: ["eslint-comments", "import", "prettier", "unicorn"],
  extends: [
    "eslint:recommended",
    "problems",
    "plugin:eslint-comments/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",

    "plugin:prettier/recommended", // Must be last
  ],
  rules: {
    // Force removal of unused eslint-disable directives, which can help prevent
    // developers from reintroducing a bad pattern that was previously fixed but
    // where the directive was left in place.
    "eslint-comments/no-unused-disable": "error",

    // Enforce consistency in where import groups appear. The linter should just
    // take care of this kind of thing.
    "import/first": "error",

    // Enforce conventions around import groups. (Source files like js and ts
    // shouldn't require an extension while assets like json, image files, etc.
    // should.) Another thing the linter should just take care of for you.
    "import/extensions": ["error", "never", { json: "always" }],

    // Enforce conventions around import groups. The linter should just take
    // care of this kind of thing.
    "import/newline-after-import": "error",

    // Enforce conventions around import group ordering. The linter should just
    // take care of this kind of thing.
    "import/order": [
      "error",
      {
        alphabetize: { order: "asc" },
        groups: ["builtin", "external", "parent", "sibling", "index"],
        "newlines-between": "never",
      },
    ],

    // Strip path segments that don't modify the final import, e.g.
    // "./..//app" => "../app"
    "import/no-useless-path-segments": "error",

    "no-unused-vars": [
      "error",
      {
        // Ignore unused arguments that are prefixed with an underscore; naming
        // unused variables is sometimes helpful for forcing variadic functions
        // to behave the way you want, or for documenting arguments that are
        // likely to come into use in the future.
        args: "after-used",
        argsIgnorePattern: "^_",
      },
    ],

    // FIXME(ndhoule): enabled by eslint-config-problems. Unfortunately, an
    // issue in @typescript-eslint/eslint-config prevents this rule from being
    // applied consistently across both JavaScript and TypeScript, so it's
    // disabled for now. Enable this and the matching TypeScript rule when the
    // issue is fixed:
    // https://github.com/typescript-eslint/typescript-eslint/issues/1856
    "no-use-before-define": "off",

    "no-useless-constructor": "error",

    "no-restricted-globals": [
      "error",
      {
        name: "isNaN",
        message:
          "Prefer Number.isNaN over the global isNaN. (isNaN coerces a non-numeric value to a number before performing its comparsion, which is confusing in the general case. If you intend to coerce the value to a number before performing the NaN check, explicitly cast the value first.)",
      },
      {
        name: "parseFloat",
        message: "Prefer Number.parseFloat over the global parseFloat.",
      },
      {
        name: "parseInt",
        message: "Prefer Number.parseInt over the global parseInt.",
      },
    ],

    // `return await` is almost always a mistake. The only time when it's
    // acceptable is within a `try` block (which this rule permits).
    "no-return-await": "error",

    // This rule is pretty broken; for more info, see:
    // https://github.com/eslint/eslint/issues/11899
    "require-atomic-updates": "off",

    // Sort import members (and destructured `require`s) alphabetically for
    // consistency. (For declaration sorting, see `import/order`, which does a
    // more comprehensive job of sorting import declarations.)
    "sort-imports": [
      "error",
      {
        ignoreCase: false,
        ignoreDeclarationSort: true,
        ignoreMemberSort: false,
      },
    ],

    // Enabled by eslint-config-problems; because it's enabled automatically by
    // tools like TypeScript, Babel/Webpack, etc., it's generally not necessary
    // to enable it explicitly.
    strict: "off",

    "unicorn/expiring-todo-comments": "error",
  },
};
<|endoftext|>


<commit_before>import * as vlActions from './vacuumlabs/actions'
import * as testActions from './test/actions'
import * as wincentActions from './wincent/actions'

const variantMap = {
  vacuumlabs: vlActions,
  // currently, the test actions are the same as VL actions
  test: testActions,
  wincent: wincentActions,
}

// user-facing actions
export const getUserActions = (variant, order) => {
  return variantMap[variant].getOrderActions(order)
}

// admin-facing sections
const getStatusSection = (orderId) => ({
  text: 'Status',
  actions: [{
    type: 'select',
    name: 'status',
    text: 'Set status...',
    options: ['requested', 'ordered', 'canceled', 'delivered', 'lost', 'sold', 'used', 'gift'].map((status) => ({
      text: status,
      value: status,
    })),
  }],
  callback_id: `O${orderId}`,
})

export const getArchiveSection = (orderId, toArchive) => ({
  text: 'Archive',
  actions: [{
    type: 'button',
    name: toArchive ? 'unarchive' : 'archive',
    text: toArchive ? 'Unarchive' : 'Archive',
    value: orderId,
    style: 'default',
  }],
  callback_id: `O${orderId}`,
})

export const getAdminSections = (variant, orderId, primaryBtn, msgButtons) => {
  const variantMap = {
    vacuumlabs: vlActions.getActionsSection,
    test: testActions.getActionsSection,
    wincent: wincentActions.getActionsSection,
  }
  const getActionsSection = variantMap[variant]

  return [
    getStatusSection(orderId),
    getActionsSection(orderId, primaryBtn, msgButtons),
    getArchiveSection(orderId, false),
  ]
}

export const getNewOrderAdminSections = (variant, orderAttachment, orderId, orderOffice) => {
  return variantMap[variant].getNewOrderAdminSections(orderAttachment, orderId, orderOffice)
}
<commit_msg>cut titles of status and archive too<commit_after>import * as vlActions from './vacuumlabs/actions'
import * as testActions from './test/actions'
import * as wincentActions from './wincent/actions'

const variantMap = {
  vacuumlabs: vlActions,
  test: testActions,
  wincent: wincentActions,
}

// user-facing actions
export const getUserActions = (variant, order) => {
  return variantMap[variant].getOrderActions(order)
}

// admin-facing sections
const getStatusSection = (orderId) => ({
  text: '',
  actions: [{
    type: 'select',
    name: 'status',
    text: 'Set status...',
    options: ['requested', 'ordered', 'canceled', 'delivered', 'lost', 'sold', 'used', 'gift'].map((status) => ({
      text: status,
      value: status,
    })),
  }],
  callback_id: `O${orderId}`,
})

export const getArchiveSection = (orderId, toArchive) => ({
  text: '',
  actions: [{
    type: 'button',
    name: toArchive ? 'unarchive' : 'archive',
    text: toArchive ? 'Unarchive' : 'Archive',
    value: orderId,
    style: 'default',
  }],
  callback_id: `O${orderId}`,
})

export const getAdminSections = (variant, orderId, primaryBtn, msgButtons) => {
  const variantMap = {
    vacuumlabs: vlActions.getActionsSection,
    test: testActions.getActionsSection,
    wincent: wincentActions.getActionsSection,
  }
  const getActionsSection = variantMap[variant]

  return [
    getStatusSection(orderId),
    getActionsSection(orderId, primaryBtn, msgButtons),
    getArchiveSection(orderId, false),
  ]
}

export const getNewOrderAdminSections = (variant, orderAttachment, orderId, orderOffice) => {
  return variantMap[variant].getNewOrderAdminSections(orderAttachment, orderId, orderOffice)
}
<|endoftext|>


<commit_before>import colors from '../settings/colors';

export default {
  sliderRoot: {
    height: '18px',
    width: '100%',
    position: 'relative',
    marginTop: '24px',
    marginBottom: '48px'
  },
  slider: {
    position: 'absolute',
    top: '8px',
    left: '0px',
    width: '100%',
    height: '2px'
  },
  sliderFilled: {
    backgroundColor: colors.theme,
    height: '100%',
    position: 'absolute',
    left: 0,
    top: 0
  },
  sliderRemaining: {
    backgroundColor: colors.grey,
    height: '100%',
    position: 'absolute',
    right: 0,
    top: 0
  },
  sliderButton: {
    position: 'absolute',
    width: '10px',
    height: '10px',
    borderRadius: '50%',
    border: `2px solid ${colors.theme}`,
    top: '1px',
    backgroundColor: colors.theme,
    transform: 'translate(-50%, -50%)',
    transition: 'boreder .1s ease-in-out, background-color .1s ease-in-out'
  }
};
<commit_msg>:pencil2: Made a typo.<commit_after>import colors from '../settings/colors';

export default {
  sliderRoot: {
    height: '18px',
    width: '100%',
    position: 'relative',
    marginTop: '24px',
    marginBottom: '48px'
  },
  slider: {
    position: 'absolute',
    top: '8px',
    left: '0px',
    width: '100%',
    height: '2px'
  },
  sliderFilled: {
    backgroundColor: colors.theme,
    height: '100%',
    position: 'absolute',
    left: 0,
    top: 0
  },
  sliderRemaining: {
    backgroundColor: colors.grey,
    height: '100%',
    position: 'absolute',
    right: 0,
    top: 0
  },
  sliderButton: {
    position: 'absolute',
    width: '10px',
    height: '10px',
    borderRadius: '50%',
    border: `2px solid ${colors.theme}`,
    top: '1px',
    backgroundColor: colors.theme,
    transform: 'translate(-50%, -50%)',
    transition: 'border .1s ease-in-out, background-color .1s ease-in-out'
  }
};
<|endoftext|>


<commit_before>const fs = require('fs')
const cp = require('child_process')
const mkdirp = require('mkdirp')
const untildify = require('untildify')

const dir = untildify('~/.config/autostart')

module.exports = {
  dir,
  getFile,
  add,
  create,
  remove
}

function spawn (cmd, args = [], out) {
  const opts = {
    detached: true
  }

  if (out) {
    const fd = fs.openSync(out, 'w')
    opts.stdio = ['ignore', fd, fd]
  }

  cp
    .spawn(cmd, args, opts)
    .on('error', console.log)
    .unref()
}

function getFile (name) {
  return `${dir}/${name}.desktop`
}

function add (name, cmd, args = [], out) {
  const file = getFile(name)

  let command = cmd

  if (args.length) {
    command += ` ${args.join(' ')}`
  }

  if (out) {
    command += ` > ${out}`
  }

  const data = [
    '[Desktop Entry]',
    'Type=Application',
    'Vestion=1.0',
    `Name=${name}`,
    `Comment=${name} startup script`,
    `Exec=${command}`,
    'StartupNotify=false',
    'Terminal=false'
  ].join('\n')

  mkdirp.sync(dir)
  fs.writeFileSync(file, data)
  return file
}

function create (name, cmd, args = [], out) {
  add(name, cmd, args, out)
  spawn(cmd, args, out)
}

function remove (name) {
  const file = getFile(name)
  if (fs.existsSync(file)) fs.unlinkSync(file)
}
<commit_msg>Typo linux.js<commit_after>const fs = require('fs')
const cp = require('child_process')
const mkdirp = require('mkdirp')
const untildify = require('untildify')

const dir = untildify('~/.config/autostart')

module.exports = {
  dir,
  getFile,
  add,
  create,
  remove
}

function spawn (cmd, args = [], out) {
  const opts = {
    detached: true
  }

  if (out) {
    const fd = fs.openSync(out, 'w')
    opts.stdio = ['ignore', fd, fd]
  }

  cp
    .spawn(cmd, args, opts)
    .on('error', console.log)
    .unref()
}

function getFile (name) {
  return `${dir}/${name}.desktop`
}

function add (name, cmd, args = [], out) {
  const file = getFile(name)

  let command = cmd

  if (args.length) {
    command += ` ${args.join(' ')}`
  }

  if (out) {
    command += ` > ${out}`
  }

  const data = [
    '[Desktop Entry]',
    'Type=Application',
    'Version=1.0',
    `Name=${name}`,
    `Comment=${name} startup script`,
    `Exec=${command}`,
    'StartupNotify=false',
    'Terminal=false'
  ].join('\n')

  mkdirp.sync(dir)
  fs.writeFileSync(file, data)
  return file
}

function create (name, cmd, args = [], out) {
  add(name, cmd, args, out)
  spawn(cmd, args, out)
}

function remove (name) {
  const file = getFile(name)
  if (fs.existsSync(file)) fs.unlinkSync(file)
}
<|endoftext|>


<commit_before>const ytutil = require("../util/youtubeHandler.js");
const yt     = require("ytdl-core")
const bufst  = require("buffered2").BufferedStream;

exports.play = async function play(guild, client) {
	if (!client.guilds.get(guild.id)                  ||
		!client.voiceConnections.get(guild.id)        ||
		client.voiceConnections.get(guild.id).playing ||
		client.voiceConnections.get(guild.id).paused
	) return;

	let song = guild.queue[0].src === "youtube" ? new bufst() : guild.queue[0].id;

	if (guild.queue[0].src === "youtube")
		yt(guild.queue[0].id, { filter: "audioonly" }).pipe(song);

/*
		song.duration = await ytutil.getDuration(song.id);
		song.started = Date.now();
*/

		client.voiceConnections.get(guild.id).play(song);

		if (client.getChannel(guild.msgc)) client.getChannel(guild.msgc).createMessage({embed: {
			color: 0x1E90FF,
			title: "Now Playing",
			description: `${guild.queue[0].title}` //(https://youtu.be/${guild.queue[0].id})`
		}}).catch(err => { console.log("Couldn't post to " + guild.msgc )});

		client.voiceConnections.get(guild.id).once("end", () => {
			guild.queue.shift();
			guild.svotes = [];
			if (guild.queue.length > 0)
				return exports.play(guild, client);
			if (client.getChannel(guild.msgc)) client.getChannel(guild.msgc).createMessage({embed: {
				color: 0x1E90FF,
				title: "Queue concluded!",
				description: "[Enjoying the music? Help keep JukeBot alive!](https://patreon.com/crimsonxv)",
				footer: {
					text: "Becoming a patron will also bag you some nice benefits too!"
				}
			}});
			if (client.voiceConnections.get(guild.id) && client.voiceConnections.get(guild.id).channelID) client.leaveVoiceChannel(guild.id);
		});

};
<commit_msg>pls stop killing my wam<commit_after>const ytutil = require("../util/youtubeHandler.js");
const yt     = require("ytdl-core")
const bufst  = require("buffered2").BufferedStream;

exports.play = async function play(guild, client) {
	if (!client.guilds.get(guild.id)                  ||
		!client.voiceConnections.get(guild.id)        ||
		client.voiceConnections.get(guild.id).playing ||
		client.voiceConnections.get(guild.id).paused
	) return;

	let song = guild.queue[0].src === "youtube" ? new bufst() : guild.queue[0].id;

	if (guild.queue[0].src === "youtube")
		yt(guild.queue[0].id, { filter: "audioonly" }).pipe(song);

		let duration = await ytutil.getDuration(guild.queue[0].id);
		if (duration > 3600 && !permissions.isDonator(guild.queue[0].req)) {
			if (client.getChannel(guild.msgc)) client.getChannel(guild.msgc).createMessage({ embed: {
				color: 0x1E90FF,
				title: "This song exceeds the duration limit"
			}});
			guild.queue.shift();
			return exports.play(guild, client);
		}
		//song.started = Date.now();

		client.voiceConnections.get(guild.id).play(song);

		if (client.getChannel(guild.msgc)) client.getChannel(guild.msgc).createMessage({embed: {
			color: 0x1E90FF,
			title: "Now Playing",
			description: `${guild.queue[0].title}` //(https://youtu.be/${guild.queue[0].id})`
		}}).catch(err => { console.log("Couldn't post to " + guild.msgc )});

		client.voiceConnections.get(guild.id).once("end", () => {
			song = undefined;
			guild.queue.shift();
			guild.svotes = [];
			if (guild.queue.length > 0)
				return exports.play(guild, client);
			if (client.getChannel(guild.msgc)) client.getChannel(guild.msgc).createMessage({embed: {
				color: 0x1E90FF,
				title: "Queue concluded!",
				description: "[Enjoying the music? Help keep JukeBot alive!](https://patreon.com/crimsonxv)",
				footer: {
					text: "Becoming a patron will also bag you some nice benefits too!"
				}
			}});
			if (client.voiceConnections.get(guild.id) && client.voiceConnections.get(guild.id).channelID) client.leaveVoiceChannel(guild.id);
		});

};
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 */

/**
 * A function scope holding all the functionality needed to convert a
 * Paper.js DOM to a Paper.js DOM.
 */
new function() {

	// Shortcut to Base.formatNumber
	var formatNumber = Base.formatNumber;

	function formatPoint(point) {
		return formatNumber(point.x) + ',' + formatNumber(point.y);
	}

	function setAttributes(svg, attrs) {
		for (var key in attrs) {
			var val = attrs[key];
			if (typeof val === 'number')
				val = formatNumber(val);
			svg.setAttribute(key, val);
		}
		return svg;
	}

	function createElement(tag, attrs) {
		return setAttributes(
			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
	}

	function getDistance(segments, index1, index2) {
		return segments[index1]._point.getDistance(segments[index2]._point);
	}

	function getTransform(item) {
		// Note, we're taking out the translation part of the matrix and move it
		// to x, y attributes, to produce more readable markup, and not have to
		// use center points in rotate(). To do so, SVG requries us to inverse
		// transform the translation point by the matrix itself, since they are
		// provided in local coordinates.
		var matrix = item._matrix.createShiftless(),
			trans =  matrix._inverseTransform(item._matrix.getTranslation()),
			attrs = {
				x: trans.x,
				y: trans.y
			};
		if (matrix.isIdentity())
			return attrs;
		// See if we can formulate this matrix as simple scale / rotate commands
		// Note: getScaling() returns values also when it's not a simple scale,
		// but angle is only != null if it is, so check for that.
		var transform = [],
			angle = matrix.getRotation(),
			scale = matrix.getScaling();
		if (angle != null) {
			transform.push(angle
					? 'rotate(' + formatNumber(angle) + ')'
					: 'scale(' + formatPoint(scale) +')');
		} else {
			transform.push('matrix(' + matrix.getValues().join(',') + ')');
		}
		attrs.transform = transform.join(' ');
		return attrs;
	}

	function getPath(path, segments) {
		var parts = [],
			style = path._style;

		function addCurve(seg1, seg2, skipLine) {
			var point1 = seg1._point,
				point2 = seg2._point,
				handle1 = seg1._handleOut,
				handle2 = seg2._handleIn;
			if (handle1.isZero() && handle2.isZero()) {
				if (!skipLine) {
					// L = lineto: moving to a point with drawing
					parts.push('L' + formatPoint(point2));
				}
			} else {
				// c = relative curveto: handle1, handle2 + end - start, end - start
				var end = point2.subtract(point1);
				parts.push('c' + formatPoint(handle1),
					formatPoint(end.add(handle2)),
					formatPoint(end));
			}
		}

		parts.push('M' + formatPoint(segments[0]._point));
		for (i = 0, l = segments.length  - 1; i < l; i++)
			addCurve(segments[i], segments[i + 1], false);
		// We only need to draw the connecting curve if it is not a line, and if
		// the path is cosed and has a stroke color, or if it is filled.
		if (path._closed && style._strokeColor || style._fillColor)
			addCurve(segments[segments.length - 1], segments[0], true);
		if (path._closed)
			parts.push('z');
		return parts.join(' ');
	}

	function determineAngle(path, segments, type, center) {
		// If the object is a circle, ellipse, rectangle, or rounded rectangle,
		// see if it is placed at an angle, by figuring out its topCenter point
		// and measuring the angle to its center.
		var topCenter = type === 'rect'
				? segments[1]._point.add(segments[2]._point).divide(2)
				: type === 'roundrect'
				? segments[3]._point.add(segments[4]._point).divide(2)
				: type === 'circle' || type === 'ellipse'
				? segments[1]._point
				: null;
		var angle = topCenter && topCenter.subtract(center).getAngle() + 90;
		return Numerical.isZero(angle || 0) ? 0 : angle;
	}

	function determineType(path, segments) {
		// Returns true if the the two segment indices are the beggining of two
		// lines and if the wto lines are parallel.
		function isColinear(i, j) {
			var seg1 = segments[i],
				seg2 = seg1.getNext(),
				seg3 = segments[j],
				seg4 = seg3.getNext();
			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
					&& seg3._handleOut.isZero() && seg4._handleIn.isZero()
					&& seg2._point.subtract(seg1._point).isColinear(
						seg4._point.subtract(seg3._point));
		}

		// Kappa, see: http://www.whizkidtech.redprince.net/bezier/circle/kappa/
		var kappa = 4 * (Math.sqrt(2) - 1) / 3;

		// Returns true if the segment at the given index is the beginning of
		// a orthogonal arc segment. The code is looking at the length of the
		// handles and their relation to the distance to the imaginary corner
		// point. If the relation is kappa (see above), then it's an arc.
		function isArc(i) {
			var segment = segments[i],
				next = segment.getNext(),
				handle1 = segment._handleOut,
				handle2 = next._handleIn;
			if (handle1.isOrthogonal(handle2)) {
				var from = segment._point,
					to = next._point,
					// Find hte corner point by intersecting the lines described
					// by both handles:
					corner = new Line(from, handle1).intersect(
							new Line(to, handle2));
				return corner && Numerical.isZero(handle1.getLength() /
						corner.subtract(from).getLength() - kappa)
					&& Numerical.isZero(handle2.getLength() /
						corner.subtract(to).getLength() - kappa);
			}
		}

		// See if actually have any curves in the path. Differentiate
		// between straight objects (line, polyline, rect, and  polygon) and
		// objects with curves(circle, ellipse, roundedRectangle).
		if (path.isPolygon()) {
			return  segments.length === 4 && path._closed
					&& isColinear(0, 2) && isColinear(1, 3)
					? 'rect'
					: segments.length >= 3
						? path._closed ? 'polygon' : 'polyline'
						: 'line';
		} else if (path._closed) {
			if (segments.length === 8
					&& isArc(0) && isArc(2) && isArc(4) && isArc(6)
					&& isColinear(1, 5) && isColinear(3, 7)) {
				return 'roundrect';
			} else if (segments.length === 4
					&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
				// If the distance between (point0 and point2) and (point1
				// and point3) are equal, then it is a circle
				return Numerical.isZero(getDistance(segments, 0, 2)
						- getDistance(segments, 1, 3))
						? 'circle'
						: 'ellipse';
			}
		}
		return 'path';
	}

	function exportGroup(group) {
		var attrs = getTransform(group),
			children = group._children;
		// Override default SVG style on groups, then apply style.
		attrs.fill = 'none';
		var svg = createElement('g', attrs);
		for (var i = 0, l = children.length; i < l; i++)
			svg.appendChild(exportItem(children[i]));
		return svg;
	}

	function exportText(item) {
		var attrs = getTransform(item),
			style = item._style;
		if (style._font != null)
			attrs['font-family'] = style._font;
		if (style._fontSize != null)
			attrs['font-size'] = style._fontSize;
		var svg = createElement('text', attrs);
		svg.textContent = item._content;
		return svg;
	}

	function exportPath(path) {
		var segments = path._segments,
			center = path.getPosition(true),
			type = determineType(path, segments),
			angle = determineAngle(path, segments, type, center),
			attrs;
		switch (type) {
		case 'path':
			attrs = {
				d: getPath(path, segments)
			};
			break;
		case 'polyline':
		case 'polygon':
			var parts = [];
			for(i = 0, l = segments.length; i < l; i++)
				parts.push(formatPoint(segments[i]._point));
			attrs = {
				points: parts.join(' ')
			};
			break;
		case 'rect':
			var width = getDistance(segments, 0, 3),
				height = getDistance(segments, 0, 1),
				// Counter-compensate the determined rotation angle
				point = segments[1]._point.rotate(-angle, center);
			attrs = {
				x: point.x,
				y: point.y,
				width: width,
				height: height
			};
			break;
		case 'roundrect':
			type = 'rect';
			// d-variables and point are used to determine the rounded corners
			// for the rounded rectangle
			var width = getDistance(segments, 1, 6),
				height = getDistance(segments, 0, 3),
				// Subtract side lengths from total width and divide by 2 to get
				// corner radius size
				rx = (width - getDistance(segments, 0, 7)) / 2,
				ry = (height - getDistance(segments, 1, 2)) / 2,
				// Calculate topLeft corner point, by using sides vectors and
				// subtracting normalized rx vector to calculate arc corner.
				left = segments[3]._point, // top-left side point
				right = segments[4]._point, // top-right side point
				point = left.subtract(right.subtract(left).normalize(rx))
						// Counter-compensate the determined rotation angle
						.rotate(-angle, center);
			attrs = {
				x: point.x,
				y: point.y,
				width: width,
				height: height,
				rx: rx,
				ry: ry
			};
			break;
		case'line':
			var first = segments[0]._point,
				last = segments[segments.length - 1]._point;
			attrs = {
				x1: first._x,
				y1: first._y,
				x2: last._x,
				y2: last._y
			};
			break;
		case 'circle':
			var radius = getDistance(segments, 0, 2) / 2;
			attrs = {
				cx: center.x,
				cy: center.y,
				r: radius
			};
			break;
		case 'ellipse':
			var rx = getDistance(segments, 2, 0) / 2,
				ry = getDistance(segments, 3, 1) / 2;
			attrs = {
				cx: center.x,
				cy: center.y,
				rx: rx,
				ry: ry
			};
			break;
		}
		if (angle) {
			attrs.transform = 'rotate(' + formatNumber(angle) + ','
					+ formatPoint(center) + ')';
		}
		var svg = createElement(type, attrs);
		return svg;
	}

	var exporters = {
		group: exportGroup,
		layer: exportGroup,
		path: exportPath,
		pointtext: exportText
		// TODO:
		// raster:
		// placedsymbol:
		// compoundpath:
	};

	function applyStyle(item, svg) {
		var attrs = {},
			style = item._style,
			parent = item.getParent(),
			parentStyle = parent && parent._style;

		if (item._name != null)
			attrs.id = item._name;

		Base.each(SvgStyles.properties, function(entry) {
			// Get a given style only if it differs from the value on the parent
			// (A layer or group which can have style values in SVG).
			var value = style[entry.get]();
			if (!parentStyle || !Base.equals(parentStyle[entry.get](), value)) {
				attrs[entry.attribute] = value == null
					? 'none'
					: entry.type === 'color'
						? value.toCssString()
						: entry.type === 'array'
							? value.join(',')
							: entry.type === 'number'
								? formatNumber(value)
								: value;
			}
		});

		if (item._opacity != null)
			attrs.opacity = item._opacity;

		if (item._visibility != null)
			attrs._visibility = item._visibility ? 'visible' : 'hidden';

		return setAttributes(svg, attrs);
	}

	function exportProject(project) {
		var svg = createElement('svg'),
			layers = project.layers;
		for (var i = 0, l = layers.length; i < l; i++) {
			svg.appendChild(exportItem(layers[i]));
		}
		return svg;
	}

	function exportItem(item) {
		var exporter = exporters[item._type];
		var svg = exporter && exporter(item, item._type);
		return svg && applyStyle(item, svg);
	}

	Item.inject(/** @Lends Item# */{
		/**
		 * Exports the item and all its child items as an SVG DOM, all contained
		 * in one top level SVG group node.
		 *
		 * @return {SVGSVGElement} the item converted to an SVG node
		 */
		exportSvg: function() {
			return exportItem(this);
		}
	});

	Project.inject(/** @Lends Project# */{
		/**
		 * Exports the project and all its layers and child items as an SVG DOM,
		 * all contained in one top level SVG group node.
		 *
		 * @return {SVGSVGElement} the project converted to an SVG node
		 */
		exportSvg: function() {
			return exportProject(this);
		}
	});
};
<commit_msg>No need for private exportItem() / exportProject() functions.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 */

/**
 * A function scope holding all the functionality needed to convert a
 * Paper.js DOM to a Paper.js DOM.
 */
new function() {

	// Shortcut to Base.formatNumber
	var formatNumber = Base.formatNumber;

	function formatPoint(point) {
		return formatNumber(point.x) + ',' + formatNumber(point.y);
	}

	function setAttributes(svg, attrs) {
		for (var key in attrs) {
			var val = attrs[key];
			if (typeof val === 'number')
				val = formatNumber(val);
			svg.setAttribute(key, val);
		}
		return svg;
	}

	function createElement(tag, attrs) {
		return setAttributes(
			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
	}

	function getDistance(segments, index1, index2) {
		return segments[index1]._point.getDistance(segments[index2]._point);
	}

	function getTransform(item) {
		// Note, we're taking out the translation part of the matrix and move it
		// to x, y attributes, to produce more readable markup, and not have to
		// use center points in rotate(). To do so, SVG requries us to inverse
		// transform the translation point by the matrix itself, since they are
		// provided in local coordinates.
		var matrix = item._matrix.createShiftless(),
			trans =  matrix._inverseTransform(item._matrix.getTranslation()),
			attrs = {
				x: trans.x,
				y: trans.y
			};
		if (matrix.isIdentity())
			return attrs;
		// See if we can formulate this matrix as simple scale / rotate commands
		// Note: getScaling() returns values also when it's not a simple scale,
		// but angle is only != null if it is, so check for that.
		var transform = [],
			angle = matrix.getRotation(),
			scale = matrix.getScaling();
		if (angle != null) {
			transform.push(angle
					? 'rotate(' + formatNumber(angle) + ')'
					: 'scale(' + formatPoint(scale) +')');
		} else {
			transform.push('matrix(' + matrix.getValues().join(',') + ')');
		}
		attrs.transform = transform.join(' ');
		return attrs;
	}

	function getPath(path, segments) {
		var parts = [],
			style = path._style;

		function addCurve(seg1, seg2, skipLine) {
			var point1 = seg1._point,
				point2 = seg2._point,
				handle1 = seg1._handleOut,
				handle2 = seg2._handleIn;
			if (handle1.isZero() && handle2.isZero()) {
				if (!skipLine) {
					// L = lineto: moving to a point with drawing
					parts.push('L' + formatPoint(point2));
				}
			} else {
				// c = relative curveto: handle1, handle2 + end - start, end - start
				var end = point2.subtract(point1);
				parts.push('c' + formatPoint(handle1),
					formatPoint(end.add(handle2)),
					formatPoint(end));
			}
		}

		parts.push('M' + formatPoint(segments[0]._point));
		for (i = 0, l = segments.length  - 1; i < l; i++)
			addCurve(segments[i], segments[i + 1], false);
		// We only need to draw the connecting curve if it is not a line, and if
		// the path is cosed and has a stroke color, or if it is filled.
		if (path._closed && style._strokeColor || style._fillColor)
			addCurve(segments[segments.length - 1], segments[0], true);
		if (path._closed)
			parts.push('z');
		return parts.join(' ');
	}

	function determineAngle(path, segments, type, center) {
		// If the object is a circle, ellipse, rectangle, or rounded rectangle,
		// see if it is placed at an angle, by figuring out its topCenter point
		// and measuring the angle to its center.
		var topCenter = type === 'rect'
				? segments[1]._point.add(segments[2]._point).divide(2)
				: type === 'roundrect'
				? segments[3]._point.add(segments[4]._point).divide(2)
				: type === 'circle' || type === 'ellipse'
				? segments[1]._point
				: null;
		var angle = topCenter && topCenter.subtract(center).getAngle() + 90;
		return Numerical.isZero(angle || 0) ? 0 : angle;
	}

	function determineType(path, segments) {
		// Returns true if the the two segment indices are the beggining of two
		// lines and if the wto lines are parallel.
		function isColinear(i, j) {
			var seg1 = segments[i],
				seg2 = seg1.getNext(),
				seg3 = segments[j],
				seg4 = seg3.getNext();
			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
					&& seg3._handleOut.isZero() && seg4._handleIn.isZero()
					&& seg2._point.subtract(seg1._point).isColinear(
						seg4._point.subtract(seg3._point));
		}

		// Kappa, see: http://www.whizkidtech.redprince.net/bezier/circle/kappa/
		var kappa = 4 * (Math.sqrt(2) - 1) / 3;

		// Returns true if the segment at the given index is the beginning of
		// a orthogonal arc segment. The code is looking at the length of the
		// handles and their relation to the distance to the imaginary corner
		// point. If the relation is kappa (see above), then it's an arc.
		function isArc(i) {
			var segment = segments[i],
				next = segment.getNext(),
				handle1 = segment._handleOut,
				handle2 = next._handleIn;
			if (handle1.isOrthogonal(handle2)) {
				var from = segment._point,
					to = next._point,
					// Find hte corner point by intersecting the lines described
					// by both handles:
					corner = new Line(from, handle1).intersect(
							new Line(to, handle2));
				return corner && Numerical.isZero(handle1.getLength() /
						corner.subtract(from).getLength() - kappa)
					&& Numerical.isZero(handle2.getLength() /
						corner.subtract(to).getLength() - kappa);
			}
		}

		// See if actually have any curves in the path. Differentiate
		// between straight objects (line, polyline, rect, and  polygon) and
		// objects with curves(circle, ellipse, roundedRectangle).
		if (path.isPolygon()) {
			return  segments.length === 4 && path._closed
					&& isColinear(0, 2) && isColinear(1, 3)
					? 'rect'
					: segments.length >= 3
						? path._closed ? 'polygon' : 'polyline'
						: 'line';
		} else if (path._closed) {
			if (segments.length === 8
					&& isArc(0) && isArc(2) && isArc(4) && isArc(6)
					&& isColinear(1, 5) && isColinear(3, 7)) {
				return 'roundrect';
			} else if (segments.length === 4
					&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
				// If the distance between (point0 and point2) and (point1
				// and point3) are equal, then it is a circle
				return Numerical.isZero(getDistance(segments, 0, 2)
						- getDistance(segments, 1, 3))
						? 'circle'
						: 'ellipse';
			}
		}
		return 'path';
	}

	function exportGroup(group) {
		var attrs = getTransform(group),
			children = group._children;
		// Override default SVG style on groups, then apply style.
		attrs.fill = 'none';
		var svg = createElement('g', attrs);
		for (var i = 0, l = children.length; i < l; i++)
			svg.appendChild(children[i].exportSvg());
		return svg;
	}

	function exportText(item) {
		var attrs = getTransform(item),
			style = item._style;
		if (style._font != null)
			attrs['font-family'] = style._font;
		if (style._fontSize != null)
			attrs['font-size'] = style._fontSize;
		var svg = createElement('text', attrs);
		svg.textContent = item._content;
		return svg;
	}

	function exportPath(path) {
		var segments = path._segments,
			center = path.getPosition(true),
			type = determineType(path, segments),
			angle = determineAngle(path, segments, type, center),
			attrs;
		switch (type) {
		case 'path':
			attrs = {
				d: getPath(path, segments)
			};
			break;
		case 'polyline':
		case 'polygon':
			var parts = [];
			for(i = 0, l = segments.length; i < l; i++)
				parts.push(formatPoint(segments[i]._point));
			attrs = {
				points: parts.join(' ')
			};
			break;
		case 'rect':
			var width = getDistance(segments, 0, 3),
				height = getDistance(segments, 0, 1),
				// Counter-compensate the determined rotation angle
				point = segments[1]._point.rotate(-angle, center);
			attrs = {
				x: point.x,
				y: point.y,
				width: width,
				height: height
			};
			break;
		case 'roundrect':
			type = 'rect';
			// d-variables and point are used to determine the rounded corners
			// for the rounded rectangle
			var width = getDistance(segments, 1, 6),
				height = getDistance(segments, 0, 3),
				// Subtract side lengths from total width and divide by 2 to get
				// corner radius size
				rx = (width - getDistance(segments, 0, 7)) / 2,
				ry = (height - getDistance(segments, 1, 2)) / 2,
				// Calculate topLeft corner point, by using sides vectors and
				// subtracting normalized rx vector to calculate arc corner.
				left = segments[3]._point, // top-left side point
				right = segments[4]._point, // top-right side point
				point = left.subtract(right.subtract(left).normalize(rx))
						// Counter-compensate the determined rotation angle
						.rotate(-angle, center);
			attrs = {
				x: point.x,
				y: point.y,
				width: width,
				height: height,
				rx: rx,
				ry: ry
			};
			break;
		case'line':
			var first = segments[0]._point,
				last = segments[segments.length - 1]._point;
			attrs = {
				x1: first._x,
				y1: first._y,
				x2: last._x,
				y2: last._y
			};
			break;
		case 'circle':
			var radius = getDistance(segments, 0, 2) / 2;
			attrs = {
				cx: center.x,
				cy: center.y,
				r: radius
			};
			break;
		case 'ellipse':
			var rx = getDistance(segments, 2, 0) / 2,
				ry = getDistance(segments, 3, 1) / 2;
			attrs = {
				cx: center.x,
				cy: center.y,
				rx: rx,
				ry: ry
			};
			break;
		}
		if (angle) {
			attrs.transform = 'rotate(' + formatNumber(angle) + ','
					+ formatPoint(center) + ')';
		}
		var svg = createElement(type, attrs);
		return svg;
	}

	var exporters = {
		group: exportGroup,
		layer: exportGroup,
		path: exportPath,
		pointtext: exportText
		// TODO:
		// raster:
		// placedsymbol:
		// compoundpath:
	};

	function applyStyle(item, svg) {
		var attrs = {},
			style = item._style,
			parent = item.getParent(),
			parentStyle = parent && parent._style;

		if (item._name != null)
			attrs.id = item._name;

		Base.each(SvgStyles.properties, function(entry) {
			// Get a given style only if it differs from the value on the parent
			// (A layer or group which can have style values in SVG).
			var value = style[entry.get]();
			if (!parentStyle || !Base.equals(parentStyle[entry.get](), value)) {
				attrs[entry.attribute] = value == null
					? 'none'
					: entry.type === 'color'
						? value.toCssString()
						: entry.type === 'array'
							? value.join(',')
							: entry.type === 'number'
								? formatNumber(value)
								: value;
			}
		});

		if (item._opacity != null)
			attrs.opacity = item._opacity;

		if (item._visibility != null)
			attrs._visibility = item._visibility ? 'visible' : 'hidden';

		return setAttributes(svg, attrs);
	}

	Item.inject(/** @Lends Item# */{
		/**
		 * Exports the item and all its child items as an SVG DOM, all contained
		 * in one top level SVG group node.
		 *
		 * @return {SVGSVGElement} the item converted to an SVG node
		 */
		exportSvg: function() {
			var exporter = exporters[this._type];
			var svg = exporter && exporter(this, this._type);
			return svg && applyStyle(this, svg);
		}
	});

	Project.inject(/** @Lends Project# */{
		/**
		 * Exports the project and all its layers and child items as an SVG DOM,
		 * all contained in one top level SVG group node.
		 *
		 * @return {SVGSVGElement} the project converted to an SVG node
		 */
		exportSvg: function() {
			var svg = createElement('svg'),
				layers = this.layers;
			for (var i = 0, l = layers.length; i < l; i++)
				svg.appendChild(layers[i].exportSvg());
			return svg;
		}
	});
};
<|endoftext|>


<commit_before>function MultistateHandler(event) {
  const multistate = event.target;
  if (multistate.localName.toLowerCase() === "span")
    multistate = multistate.parentNode;
  if (!multistate.classList.contains("multistate"))
    return;
  multistate.appendChild(multistate.firstElementChild);
  updateMultistate(multistate);
}

function updateMultistate(multistate) {
  multistate.value = multistate.firstElementChild.dataset.state;
}
<commit_msg>#100 GUI, multistate, try a third time to fix for Google Chrome.<commit_after>function MultistateHandler(event) {
  let multistate = event.target;
  if (multistate.localName.toLowerCase() === "span")
    multistate = multistate.parentNode;
  if (!multistate.classList.contains("multistate"))
    return;
  multistate.appendChild(multistate.firstElementChild);
  updateMultistate(multistate);
}

function updateMultistate(multistate) {
  multistate.value = multistate.firstElementChild.dataset.state;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import EventEmitter from 'eventemitter3'
import InteractionModes from './ray-interaction-modes'
import {isMobile} from './util'

const DRAG_DISTANCE_PX = 10;

/**
 * Enumerates all possible interaction modes. Sets up all event handlers (mouse,
 * touch, etc), interfaces with gamepad API.
 *
 * Emits events:
 *    action: Input is activated (mousedown, touchstart, daydream click, vive
 *    trigger).
 *    release: Input is deactivated (mouseup, touchend, daydream release, vive
 *    release).
 *    cancel: Input is canceled (eg. we scrolled instead of tapping on
 *    mobile/desktop).
 *    pointermove(2D position): The pointer is moved (mouse or touch).
 */
export default class RayController extends EventEmitter {
  constructor(opt_el) {
    super();
    let el = opt_el || window;

    // Handle interactions.
    el.addEventListener('mousedown', this.onMouseDown_.bind(this));
    el.addEventListener('mousemove', this.onMouseMove_.bind(this));
    el.addEventListener('mouseup', this.onMouseUp_.bind(this));
    el.addEventListener('touchstart', this.onTouchStart_.bind(this));
    el.addEventListener('touchmove', this.onTouchMove_.bind(this));
    el.addEventListener('touchend', this.onTouchEnd_.bind(this));

    // The position of the pointer.
    this.pointer = new THREE.Vector2();
    // The previous position of the pointer.
    this.lastPointer = new THREE.Vector2();
    // Position of pointer in Normalized Device Coordinates (NDC).
    this.pointerNdc = new THREE.Vector2();
    // How much we have dragged (if we are dragging).
    this.dragDistance = 0;
    // Are we dragging or not.
    this.isDragging = false;
    // Is pointer active or not.
    this.isTouchActive = false;
    // Is this a synthetic mouse event?
    this.isSyntheticMouseEvent = false;

    // Gamepad events.
    this.gamepad = null;

    // VR Events.
    if (!navigator.getVRDisplays) {
      console.warn('WebVR API not available! Consider using the webvr-polyfill.');
    } else {
      navigator.getVRDisplays().then((displays) => {
        this.vrDisplay = displays[0];
      });
    }
  }

  getInteractionMode() {
    // TODO: Debugging only.
    //return InteractionModes.DAYDREAM;

    var gamepad = this.getVRGamepad_();

    if (gamepad) {
      let pose = gamepad.pose;
      // If there's a gamepad connected, determine if it's Daydream or a Vive.
      if (pose.hasPosition) {
        return InteractionModes.VR_6DOF;
      }

      if (pose.hasOrientation) {
        return InteractionModes.VR_3DOF;
      }

    } else {
      // If there's no gamepad, it might be Cardboard, magic window or desktop.
      if (isMobile()) {
        // Either Cardboard or magic window, depending on whether we are
        // presenting.
        if (this.vrDisplay && this.vrDisplay.isPresenting) {
          return InteractionModes.VR_0DOF;
        } else {
          return InteractionModes.TOUCH;
        }
      } else {
        // We must be on desktop.
        return InteractionModes.MOUSE;
      }
    }
    // By default, use TOUCH.
    return InteractionModes.TOUCH;
  }

  getGamepadPose() {
    var gamepad = this.getVRGamepad_();
    return gamepad.pose;
  }

  /**
   * Get if there is an active touch event going on.
   * Only relevant on touch devices
   */
  getIsTouchActive() {
    return this.isTouchActive;
  }

  /**
   * Checks if this click is the cardboard-compatible fallback
   * click on Daydream controllers so that we can deduplicate it.
   * TODO(klausw): It would be nice to be able to move interactions
   * to this event since it counts as a user action while controller
   * clicks don't. But that would require larger refactoring.
   */
  isCardboardCompatClick(e) {
    let mode = this.getInteractionMode();
    if (mode == InteractionModes.VR_3DOF && e.screenX == 0 && e.screenY == 0) {
      return true;
    }
    return false;
  }

  setSize(size) {
    this.size = size;
  }

  update() {
    let mode = this.getInteractionMode();
    if (mode == InteractionModes.VR_3DOF || mode == InteractionModes.VR_6DOF) {
      // If we're dealing with a gamepad, check every animation frame for a
      // pressed action.
      let isGamepadPressed = this.getGamepadButtonPressed_();
      if (isGamepadPressed && !this.wasGamepadPressed) {
        this.emit('raydown');
      }
      if (!isGamepadPressed && this.wasGamepadPressed) {
        this.emit('rayup');
      }
      this.wasGamepadPressed = isGamepadPressed;
    }
  }

  getGamepadButtonPressed_() {
    var gamepad = this.getVRGamepad_();
    if (!gamepad) {
      // If there's no gamepad, the button was not pressed.
      return false;
    }
    // Check for clicks.
    for (var j = 0; j < gamepad.buttons.length; ++j) {
      if (gamepad.buttons[j].pressed) {
        return true;
      }
    }
    return false;
  }

  onMouseDown_(e) {
    if (this.isSyntheticMouseEvent) return;
    if (this.isCardboardCompatClick(e)) return;

    this.startDragging_(e);
    this.emit('raydown');
  }

  onMouseMove_(e) {
    if (this.isSyntheticMouseEvent) return;

    this.updatePointer_(e);
    this.updateDragDistance_();
    this.emit('pointermove', this.pointerNdc);
  }

  onMouseUp_(e) {
    if (this.isSyntheticMouseEvent) return;
    this.isSyntheticMouseEvent = false;
    if (this.isCardboardCompatClick(e)) return;

    this.endDragging_();
  }

  onTouchStart_(e) {
    this.isTouchActive = true;
    var t = e.touches[0];
    this.startDragging_(t);
    this.updateTouchPointer_(e);

    this.emit('pointermove', this.pointerNdc);
    this.emit('raydown');
  }

  onTouchMove_(e) {
    this.updateTouchPointer_(e);
    this.updateDragDistance_();
  }

  onTouchEnd_(e) {
    this.endDragging_();

    // Suppress duplicate events from synthetic mouse events.
    this.isSyntheticMouseEvent = true;
    this.isTouchActive = false;
  }

  updateTouchPointer_(e) {
    // If there's no touches array, ignore.
    if (e.touches.length === 0) {
      console.warn('Received touch event with no touches.');
      return;
    }
    var t = e.touches[0];
    this.updatePointer_(t);
  }

  updatePointer_(e) {
    // How much the pointer moved.
    this.pointer.set(e.clientX, e.clientY);
    this.pointerNdc.x = (e.clientX / this.size.width) * 2 - 1;
    this.pointerNdc.y = - (e.clientY / this.size.height) * 2 + 1;
  }

  updateDragDistance_() {
    if (this.isDragging) {
      var distance = this.lastPointer.sub(this.pointer).length();
      this.dragDistance += distance;
      this.lastPointer.copy(this.pointer);

      //console.log('dragDistance', this.dragDistance);
      if (this.dragDistance > DRAG_DISTANCE_PX) {
        this.emit('raycancel');
        this.isDragging = false;
      }
    }
  }

  startDragging_(e) {
    this.isDragging = true;
    this.lastPointer.set(e.clientX, e.clientY);
  }

  endDragging_() {
    if (this.dragDistance < DRAG_DISTANCE_PX) {
      this.emit('rayup');
    }
    this.dragDistance = 0;
    this.isDragging = false;
  }

  /**
   * Gets the first VR-enabled gamepad.
   */
  getVRGamepad_() {
    // If there's no gamepad API, there's no gamepad.
    if (!navigator.getGamepads) {
      return null;
    }

    var gamepads = navigator.getGamepads();
    for (var i = 0; i < gamepads.length; ++i) {
      var gamepad = gamepads[i];

      // The array may contain undefined gamepads, so check for that as well as
      // a non-null pose.
      if (gamepad && gamepad.pose) {
        return gamepad;
      }
    }
    return null;
  }
}
<commit_msg>Fix alternating mouse / touch inputs<commit_after>/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import EventEmitter from 'eventemitter3'
import InteractionModes from './ray-interaction-modes'
import {isMobile} from './util'

const DRAG_DISTANCE_PX = 10;

/**
 * Enumerates all possible interaction modes. Sets up all event handlers (mouse,
 * touch, etc), interfaces with gamepad API.
 *
 * Emits events:
 *    action: Input is activated (mousedown, touchstart, daydream click, vive
 *    trigger).
 *    release: Input is deactivated (mouseup, touchend, daydream release, vive
 *    release).
 *    cancel: Input is canceled (eg. we scrolled instead of tapping on
 *    mobile/desktop).
 *    pointermove(2D position): The pointer is moved (mouse or touch).
 */
export default class RayController extends EventEmitter {
  constructor(opt_el) {
    super();
    let el = opt_el || window;

    // Handle interactions.
    el.addEventListener('mousedown', this.onMouseDown_.bind(this));
    el.addEventListener('mousemove', this.onMouseMove_.bind(this));
    el.addEventListener('mouseup', this.onMouseUp_.bind(this));
    el.addEventListener('touchstart', this.onTouchStart_.bind(this));
    el.addEventListener('touchmove', this.onTouchMove_.bind(this));
    el.addEventListener('touchend', this.onTouchEnd_.bind(this));

    // The position of the pointer.
    this.pointer = new THREE.Vector2();
    // The previous position of the pointer.
    this.lastPointer = new THREE.Vector2();
    // Position of pointer in Normalized Device Coordinates (NDC).
    this.pointerNdc = new THREE.Vector2();
    // How much we have dragged (if we are dragging).
    this.dragDistance = 0;
    // Are we dragging or not.
    this.isDragging = false;
    // Is pointer active or not.
    this.isTouchActive = false;
    // Is this a synthetic mouse event?
    this.isSyntheticMouseEvent = false;

    // Gamepad events.
    this.gamepad = null;

    // VR Events.
    if (!navigator.getVRDisplays) {
      console.warn('WebVR API not available! Consider using the webvr-polyfill.');
    } else {
      navigator.getVRDisplays().then((displays) => {
        this.vrDisplay = displays[0];
      });
    }
  }

  getInteractionMode() {
    // TODO: Debugging only.
    //return InteractionModes.DAYDREAM;

    var gamepad = this.getVRGamepad_();

    if (gamepad) {
      let pose = gamepad.pose;
      // If there's a gamepad connected, determine if it's Daydream or a Vive.
      if (pose.hasPosition) {
        return InteractionModes.VR_6DOF;
      }

      if (pose.hasOrientation) {
        return InteractionModes.VR_3DOF;
      }

    } else {
      // If there's no gamepad, it might be Cardboard, magic window or desktop.
      if (isMobile()) {
        // Either Cardboard or magic window, depending on whether we are
        // presenting.
        if (this.vrDisplay && this.vrDisplay.isPresenting) {
          return InteractionModes.VR_0DOF;
        } else {
          return InteractionModes.TOUCH;
        }
      } else {
        // We must be on desktop.
        return InteractionModes.MOUSE;
      }
    }
    // By default, use TOUCH.
    return InteractionModes.TOUCH;
  }

  getGamepadPose() {
    var gamepad = this.getVRGamepad_();
    return gamepad.pose;
  }

  /**
   * Get if there is an active touch event going on.
   * Only relevant on touch devices
   */
  getIsTouchActive() {
    return this.isTouchActive;
  }

  /**
   * Checks if this click is the cardboard-compatible fallback
   * click on Daydream controllers so that we can deduplicate it.
   * TODO(klausw): It would be nice to be able to move interactions
   * to this event since it counts as a user action while controller
   * clicks don't. But that would require larger refactoring.
   */
  isCardboardCompatClick(e) {
    let mode = this.getInteractionMode();
    if (mode == InteractionModes.VR_3DOF && e.screenX == 0 && e.screenY == 0) {
      return true;
    }
    return false;
  }

  setSize(size) {
    this.size = size;
  }

  update() {
    let mode = this.getInteractionMode();
    if (mode == InteractionModes.VR_3DOF || mode == InteractionModes.VR_6DOF) {
      // If we're dealing with a gamepad, check every animation frame for a
      // pressed action.
      let isGamepadPressed = this.getGamepadButtonPressed_();
      if (isGamepadPressed && !this.wasGamepadPressed) {
        this.emit('raydown');
      }
      if (!isGamepadPressed && this.wasGamepadPressed) {
        this.emit('rayup');
      }
      this.wasGamepadPressed = isGamepadPressed;
    }
  }

  getGamepadButtonPressed_() {
    var gamepad = this.getVRGamepad_();
    if (!gamepad) {
      // If there's no gamepad, the button was not pressed.
      return false;
    }
    // Check for clicks.
    for (var j = 0; j < gamepad.buttons.length; ++j) {
      if (gamepad.buttons[j].pressed) {
        return true;
      }
    }
    return false;
  }

  onMouseDown_(e) {
    if (this.isSyntheticMouseEvent) return;
    if (this.isCardboardCompatClick(e)) return;

    this.startDragging_(e);
    this.emit('raydown');
  }

  onMouseMove_(e) {
    if (this.isSyntheticMouseEvent) return;

    this.updatePointer_(e);
    this.updateDragDistance_();
    this.emit('pointermove', this.pointerNdc);
  }

  onMouseUp_(e) {
    var isSynthetic = this.isSyntheticMouseEvent;
    this.isSyntheticMouseEvent = false;
    if (isSynthetic) return;
    if (this.isCardboardCompatClick(e)) return;

    this.endDragging_();
  }

  onTouchStart_(e) {
    this.isTouchActive = true;
    var t = e.touches[0];
    this.startDragging_(t);
    this.updateTouchPointer_(e);

    this.emit('pointermove', this.pointerNdc);
    this.emit('raydown');
  }

  onTouchMove_(e) {
    this.updateTouchPointer_(e);
    this.updateDragDistance_();
  }

  onTouchEnd_(e) {
    this.endDragging_();

    // Suppress duplicate events from synthetic mouse events.
    this.isSyntheticMouseEvent = true;
    this.isTouchActive = false;
  }

  updateTouchPointer_(e) {
    // If there's no touches array, ignore.
    if (e.touches.length === 0) {
      console.warn('Received touch event with no touches.');
      return;
    }
    var t = e.touches[0];
    this.updatePointer_(t);
  }

  updatePointer_(e) {
    // How much the pointer moved.
    this.pointer.set(e.clientX, e.clientY);
    this.pointerNdc.x = (e.clientX / this.size.width) * 2 - 1;
    this.pointerNdc.y = - (e.clientY / this.size.height) * 2 + 1;
  }

  updateDragDistance_() {
    if (this.isDragging) {
      var distance = this.lastPointer.sub(this.pointer).length();
      this.dragDistance += distance;
      this.lastPointer.copy(this.pointer);

      //console.log('dragDistance', this.dragDistance);
      if (this.dragDistance > DRAG_DISTANCE_PX) {
        this.emit('raycancel');
        this.isDragging = false;
      }
    }
  }

  startDragging_(e) {
    this.isDragging = true;
    this.lastPointer.set(e.clientX, e.clientY);
  }

  endDragging_() {
    if (this.dragDistance < DRAG_DISTANCE_PX) {
      this.emit('rayup');
    }
    this.dragDistance = 0;
    this.isDragging = false;
  }

  /**
   * Gets the first VR-enabled gamepad.
   */
  getVRGamepad_() {
    // If there's no gamepad API, there's no gamepad.
    if (!navigator.getGamepads) {
      return null;
    }

    var gamepads = navigator.getGamepads();
    for (var i = 0; i < gamepads.length; ++i) {
      var gamepad = gamepads[i];

      // The array may contain undefined gamepads, so check for that as well as
      // a non-null pose.
      if (gamepad && gamepad.pose) {
        return gamepad;
      }
    }
    return null;
  }
}
<|endoftext|>


<commit_before>const DEFAULT_STATE = [{members: [], addMemberName: ''}];

const updateAddMemberName = (state, payload) => {
  return state.map((group, index) => {
    if (index !== payload.groupIdx) {
      return group;
    }
    else {
      let updatedGroup = {...group};
      updatedGroup.addMemberName = payload.name;
      return updatedGroup;
  }});
}

const addMemberToGroup = (state, payload) => {
  console.log(payload);
  console.log(state);
  return state.map((group, index) => {
    if (index !== payload.groupIdx) {
      return group;
    }
    else {
      if (group.addMemberName === '') { // do not add empty name as member
        return group
      }
      let updatedGroup = {...group};
      updatedGroup.members = [...group.members, {name:
        group.addMemberName, groupIdx: index}];
      updatedGroup.addMemberName = '';
      return updatedGroup;
  }});
}

export default(state = DEFAULT_STATE, payload) => {
  switch(payload.type) {
    //case 'addGroup':
    //  return [...state, payload.group];
    case 'addMemberToGroup':
      return addMemberToGroup(state, payload);
    //case 'removeMemberFromGroup':
    //  return state;
    //case 'removeGroup':
    //  return state;
    case 'updateAddMemberName':
      return updateAddMemberName(state, payload);
    default:
      return state;
  }
};

<commit_msg>dedup name globally<commit_after>const DEFAULT_STATE = [{members: [], addMemberName: ''}];

const updateAddMemberName = (state, payload) => {
  return state.map((group, index) => {
    if (index !== payload.groupIdx) {
      return group;
    }
    else {
      let updatedGroup = {...group};
      updatedGroup.addMemberName = payload.name;
      return updatedGroup;
  }});
}

const isDuplicatedNameGlobally = (state, groupIdx) => {
  const nameToCheck = state[groupIdx].addMemberName;
  return state.some((group) => {
    console.log(group);
    return group.members.some((member) => {
      return nameToCheck === member.name;});
  });
}

const addMemberToGroup = (state, payload) => {
  if (isDuplicatedNameGlobally(state, payload.groupIdx)) {
    return state;
  }
  return state.map((group, index) => {
    if (index !== payload.groupIdx) {
      return group;
    }
    else {
      if (group.addMemberName === '') { // do not add empty name as member
        return group
      }
      let updatedGroup = {...group};
      updatedGroup.members = [...group.members, {name:
        group.addMemberName, groupIdx: index}];
      updatedGroup.addMemberName = '';
      return updatedGroup;
  }});
}

export default(state = DEFAULT_STATE, payload) => {
  switch(payload.type) {
    //case 'addGroup':
    //  return [...state, payload.group];
    case 'addMemberToGroup':
      return addMemberToGroup(state, payload);
    //case 'removeMemberFromGroup':
    //  return state;
    //case 'removeGroup':
    //  return state;
    case 'updateAddMemberName':
      return updateAddMemberName(state, payload);
    default:
      return state;
  }
};

<|endoftext|>


<commit_before>import {
  GET_THEME,
} from 'Constants/actionTypes';
import { handleAction } from 'redux-actions';

const defaultState = {
};

const reducer = handleAction(GET_THEME, (state, action) => ({
  ...state,
  header: {
    ...action.payload.items[0].fields.header.fields,
    icons: action.payload.items[0].fields.header.fields.icons.map(value => ({
      alt: value.fields.alt,
      internal: value.fields.internal,
      url: value.fields.url,
      assetUrl: value.fields.icon.fields.file.url,
    })),
  },
  mainMenu: {
    ...action.payload.items[0].fields.mainMenu.fields,
    icons: action.payload.items[0].fields.mainMenu.fields.icons.map(value => ({
      alt: value.fields.alt,
      internal: value.fields.internal,
      url: value.fields.url,
      assetUrl: value.fields.icon.fields.file.url,
    })),
  },
}), defaultState);

export { reducer, defaultState };
<commit_msg>feat(src/reducers/theme.js): add footer state<commit_after>import {
  GET_THEME,
} from 'Constants/actionTypes';
import { handleAction } from 'redux-actions';

const defaultState = {
};

const reducer = handleAction(GET_THEME, (state, action) => ({
  ...state,
  header: {
    ...action.payload.items[0].fields.header.fields,
    icons: action.payload.items[0].fields.header.fields.icons.map(value => ({
      alt: value.fields.alt,
      internal: value.fields.internal,
      url: value.fields.url,
      assetUrl: value.fields.icon.fields.file.url,
    })),
  },
  mainMenu: {
    ...action.payload.items[0].fields.mainMenu.fields,
    icons: action.payload.items[0].fields.mainMenu.fields.icons.map(value => ({
      alt: value.fields.alt,
      internal: value.fields.internal,
      url: value.fields.url,
      assetUrl: value.fields.icon.fields.file.url,
    })),
  },
  footer: {
    ...action.payload.items[0].fields.footer.fields,
    icons: action.payload.items[0].fields.footer.fields.icons.map(value => ({
      alt: value.fields.alt,
      internal: value.fields.internal,
      url: value.fields.url,
      assetUrl: value.fields.icon.fields.file.url,
    })),
    locationIcon: {
      alt: action.payload.items[0].fields.footer
        .fields.locationIcon.fields.alt,
      internal: action.payload.items[0].fields.footer
        .fields.locationIcon.fields.internal,
      url: action.payload.items[0].fields.footer
        .fields.locationIcon.fields.url,
      assetUrl: action.payload.items[0].fields.footer
        .fields.locationIcon.fields.icon.fields.file.url,
    },
  },
}), defaultState);

export { reducer, defaultState };
<|endoftext|>


<commit_before>/*jslint node:true*/
var request = require('request');
var config = require('../utils/config');

module.exports = function (data) {
  request.post({
    url: config.tcBuildUrl,
    body: data,
    json: true,
    'auth': {
      'user': config.tcUser,
      'pass': config.tcPassword,
      'sendImmediately': true
    }
  }, function (error, response, body) {
    if (error) {
      console.log(error);
    }
  });
};
<commit_msg>Improve logs on the build post.<commit_after>/*jslint node:true*/
var request = require('request');
var config = require('../utils/config');

module.exports = function (data) {
  request.post({
    url: config.tcBuildUrl,
    body: data,
    json: true,
    'auth': {
      'user': config.tcUser,
      'pass': config.tcPassword,
      'sendImmediately': true
    }
  }, function (error, response, body) {
    if (error || response.statusCode != 200) {
      console.log('StatusCode: ' + response.statusCode);
      console.log('Error: ' + error);
      console.log('Body: ' + body);
    }
    else {
      console.log('Successfully queued build ' + data.buildTypeId
        + ' on branch ' + data.branchName);
    }
  });
};
<|endoftext|>


<commit_before>'use strict';

var app = angular.module('app', [
    'ngRoute',      //$routeProvider
    'mgcrea.ngStrap',//bs-navbar, data-match-route directives
    'ngResource'
]);

app.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/', {
                templateUrl: 'views/index.html',
                controller:'HomeCtrl'
            }).
            when('/about', {
                templateUrl: 'views/about.html'
            }).
            when('/contact', {
                templateUrl: 'views/contact.html'
            }).
            when('/login', {
                templateUrl: 'views/login.html'
            })
            .when('/posts', {
                templateUrl: 'views/posts.html',
                controller:'PostCtrl'
            }).
            when('/post/:postId', {
                templateUrl: 'views/postdetail.html',
                controller:'DetailCtrl'
            }).
            otherwise({
                templateUrl: '/'
            });
    }
]);

/* Factory*/
app.factory('Posts',[
    '$resource',function($resource){
        return $resource('index.php?r=site/:phoneId',{
            phoneId: 'phones',
            //format: 'json'
        })
    }
])
app.value('SITE_URL', 'http://apartment/frontend/web/');

app.controller('PostCtrl',['$scope','$http','$location','Posts',function($scope,$http,$location,Posts){
    Posts.query({phoneId: 'posts'},function(data){
        $scope.posts = data;
    });
}]);

app.controller('DetailCtrl',['$scope','$http','$location','$routeParams','Posts',function($scope,$http,$location,$routeParams,Posts){

    console.log($routeParams.postId);
    Posts.get({phoneId: 'post&id='+$routeParams.postId},function(data){
        $scope.post = data;
    });
}]);

<commit_msg>chengea<commit_after>'use strict';

var app = angular.module('app', [
    'ngRoute',      //$routeProvider
    'mgcrea.ngStrap',//bs-navbar, data-match-route directives
    'ngResource'
]);

app.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/', {
                templateUrl: 'views/index.html',
                controller:'HomeCtrl'
            }).
            when('/about', {
                templateUrl: 'views/about.html'
            }).
            when('/contact', {
                templateUrl: 'views/contact.html'
            }).
            when('/login', {
                templateUrl: 'views/login.html'
            })
            .when('/posts', {
                templateUrl: 'views/posts.html',
                controller:'PostCtrl'
            }).
            when('/post/:postId', {
                templateUrl: 'views/postdetail.html',
                controller:'DetailCtrl'
            }).
            when('/blog', {
                templateUrl: 'views/blog.html'

            }).
            otherwise({
                templateUrl: '/'
            });
    }
]);

/* Factory*/
app.factory('Posts',[
    '$resource',function($resource){
        return $resource('index.php?r=site/:phoneId',{
            phoneId: 'phones',
            //format: 'json'
        })
    }
])
app.value('SITE_URL', 'http://apartment/frontend/web/');

app.controller('PostCtrl',['$scope','$http','$location','Posts',function($scope,$http,$location,Posts){
    Posts.query({phoneId: 'posts'},function(data){
        $scope.posts = data;
    });
}]);

app.controller('DetailCtrl',['$scope','$http','$location','$routeParams','Posts',function($scope,$http,$location,$routeParams,Posts){

    console.log($routeParams.postId);
    Posts.get({phoneId: 'post&id='+$routeParams.postId},function(data){
        $scope.post = data;
    });
}]);

<|endoftext|>


<commit_before>const Bot = require('slackbots');
const util = require('util');
const _ = require('lodash');

const RoundpiecesBot = function Constructor(settings) {
  this.settings = settings;
};

RoundpiecesBot.prototype.run = function () {
  RoundpiecesBot.super_.call(this, this.settings);

  this.on('start', this._onStart);
  this.on('message', this._onMessage);
};

RoundpiecesBot.prototype._onStart = function () {
  this.postMessageToUser(this.settings.adminUserName,
      'RoundpiecesBot fully activated! Type help for a full list of commands.');
};

RoundpiecesBot.prototype._onMessage = function (message) {
  if (message.type === 'message') {
    switch (message.text) {
      case 'help':
      case '?':
        this._printHelp(message.user);
        break;
      default:
        console.log(message);
        break;
    }
  }
};

RoundpiecesBot.prototype._printHelp = function (userId) {
  const userName = this._getUserNameFromUserId(userId);
  if (userName) {
    this.postMessageToUser(userName, 'Here is a list of commands:');
  }
  else {
    console.error('Unknown user id', userId);
  }
};

RoundpiecesBot.prototype._getUserNameFromUserId = function (userId) {
  const user = _.find(this.users, (user) => user.id === userId);
  return user ? user.name : null;
};

util.inherits(RoundpiecesBot, Bot);

module.exports = RoundpiecesBot;
<commit_msg>Add command to print uptime. Handle unknown commands. Use slack markdown in messages.<commit_after>const Bot = require('slackbots');
const util = require('util');
const _ = require('lodash');

const RoundpiecesBot = function Constructor(settings) {
  this.settings = settings;
};

RoundpiecesBot.prototype.run = function () {
  RoundpiecesBot.super_.call(this, this.settings);

  this.on('start', this._onStart);
  this.on('message', this._onMessage);
};

RoundpiecesBot.prototype._onStart = function () {
  this.startTime = Date.now();
  this.postMessageToUser(this.settings.adminUserName,
      'RoundpiecesBot fully activated! Type `help` for a full list of commands.');
};

RoundpiecesBot.prototype._onMessage = function (message) {
  if (message.type === 'message' && message.user) {
    const userName = this._getUserNameFromUserId(message.user);
    if (!userName) {
      console.error('Unknown user id', message.user);
      console.log(message);
      return;
    }
    switch (message.text) {
      case 'help':
      case '?':
        this._printHelp(userName);
        break;
      case 'status':
        this._printStatus(userName);
        break;
      default:
        this._printUnknownCommand(userName);
        break;
    }
  }
};

RoundpiecesBot.prototype._printHelp = function (userName) {
  this.postMessageToUser(userName, `Here is a list of commands:
  • \`help\`, \`?\`: Prints this help
  • \`status\`: Prints how long I've been alive`);
};

RoundpiecesBot.prototype._printStatus = function (userName) {
  const uptime = Date.now() - this.startTime;
  this.postMessageToUser(userName, `I have been alive for *${uptime} ms!*`);
};

RoundpiecesBot.prototype._printUnknownCommand = function (userName) {
  this.postMessageToUser(userName, 'I don\'t understand what you\'re asking :disappointed: Type `help` for a full list of commands that I understand.');
};

RoundpiecesBot.prototype._getUserNameFromUserId = function (userId) {
  const user = _.find(this.users, (user) => user.id === userId);
  return user ? user.name : null;
};

util.inherits(RoundpiecesBot, Bot);

module.exports = RoundpiecesBot;
<|endoftext|>


<commit_before>'use strict';

const Bot = require('slackbots');
const fs = require('fs');
const CronJob = require('cron').CronJob;
const Model = require('./model');
const MessageService = require('./messageservice');
const AttendanceEnum = require('./participant').AttendanceEnum;

const States = Object.freeze({
  IDLE: 'idle',
  ASKED_RESPONSBILE: 'asked responsible',
  FOUND_RESPONSIBLE: 'found responsible',
  NO_RESPONSIBLE: 'no responsible',
  AWAITING_MEETING: 'awaiting meeting',
  SKIPPED: 'skipped'
});

class RoundpiecesBot extends Bot {
  constructor(settings) {
    super(settings);
    this.settings = settings;
    this.state = States.IDLE;
    this.model = new Model(this.settings.adminUserName);
    this.messageService = new MessageService((userName, message) => this.postMessageToUser(userName, message), this.model);
  }

  run() {
    this.on('start', this._onStart);
    this.on('message', this._onMessage);
  }

  get state() {
    return this._state;
  }

  set state(state) {
    this._state = state;
  }

  _onStart() {
    this.startTime = Date.now();

    fs.readFile(this.settings.listPath, 'utf8', (error, data) => this._setup(error, data));
  }

  _setup(error, data) {
    if (error) {
      this._reportError(error);
    }
    else {
      this.model.users = this.users;
      this.model.participants = data;
      if (this.model.getParticipantCount() < 1) {
        this._reportError('No participants in list');
        return;
      }
      this.model.setResponsible(this.model.participants[0]);
      this._setupCronJobs();

      this.messageService.botActivated(this.settings.name);
    }
  }

  _setupCronJobs() {
    const cronRanges = this.settings.cronRanges;
    new CronJob(cronRanges.start, () => this._startResponsibleSearch(), null, true);
    new CronJob(cronRanges.end, () => this._endResponsibleSearch(), null, true);
    new CronJob(cronRanges.reset, () => this._reset(), null, true);
  }

  _setupOneTimeJobs() {
    const endDate = this._getSearchEndTime();
    const resetDate = this._getResetTime();

    this._startResponsibleSearch();
    new CronJob(endDate, () => this._endResponsibleSearch(), null, true);
    new CronJob(resetDate, () => this._reset(), null, true);
  }

  /**
   * End time defaults to today at 12.00. If the search is started after 12.00, it defaults to an hour after the script
   * was invoked.
   * Doesn't handle the case where the script is invoked after 23.00.
   */
  _getSearchEndTime() {
    const now = new Date();
    const endDate = new Date();
    endDate.setHours(12);
    endDate.setMinutes(0);

    if (endDate < now) {
      endDate.setHours(now.getHours() + 1);
      endDate.setMinutes(now.getMinutes());
    }
    return endDate;
  }

  /**
   * Reset time defaults to today at 23.00.
   * Doesn't handle the case where the script is invoked after 23.00.
   */
  _getResetTime() {
    const resetDate = new Date();
    resetDate.setHours(23);
    return resetDate;
  }

  _startResponsibleSearch() {
    if (this.state !== States.SKIPPED) {
      this.state = States.ASKED_RESPONSBILE;
      this._notifyParticipants();
    }
  }

  _endResponsibleSearch() {
    switch (this.state) {
      case States.FOUND_RESPONSIBLE:
        this.messageService.participationList();
        break;
      case States.ASKED_RESPONSBILE:
        this.messageService.noResponsibleResponse();
        break;
      case States.NO_RESPONSIBLE:
      case States.SKIPPED:
        //No attendance - don't do anything
        break;
    }
    this.state = States.AWAITING_MEETING;
  }

  _reset() {
    this.model.reset();
    this.state = States.IDLE;
  }

  _onMessage(message) {
    if (message.type === 'message' && message.user) {
      const participant = this.model.getParticipantFromId(message.user);
      if (!participant) {
        this._reportError(`Unknown user id: ${message.user}`);
        console.log(message);
        return;
      }

      if (message.text.startsWith('admin')) {
        if (participant.admin) {
          const messageParts = message.text.split(' ');
          switch (messageParts[1]) {
            case 'help':
              this.messageService.adminHelp();
              break;
            case 'setResponsible':
              this._changeResponsible(messageParts[2]);
              break;
            case 'skip':
              this._skipNextMeeting();
              break;
            case 'start':
              this._setupOneTimeJobs();
              break;
            default:
              this.messageService.unknownCommand(participant.username);
              break;
          }
        }
        else {
          this.messageService.notAdmin(participant.username);
        }
      }
      else {
        switch (message.text.toLowerCase()) {
          case 'help':
          case '?':
            this.messageService.help(participant.username);
            break;
          case 'status':
            this._printStatus(participant.username);
            break;
          case 'next':
            this.messageService.next(participant.username);
            break;
          case 'list':
            this.messageService.list(participant.username);
            break;
          case 'accept':
            this._accept(participant);
            break;
          case 'reject':
            this._reject(participant);
            break;
          case 'yes':
            this._attending(participant);
            break;
          case 'no':
            this._notAttending(participant);
            break;
          default:
            this.messageService.unknownCommand(participant.username);
            break;
        }
      }
    }
  }

  _printStatus(userName) {
    const uptime = Date.now() - this.startTime;
    this.messageService.status(userName, uptime);
  }

  _accept(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.accepted(participant.username);
    participant.attending = AttendanceEnum.ATTENDING;
    this.state = States.FOUND_RESPONSIBLE;
    this._updateList();
  }

  _updateList() {
    this.model.updateList();
    //Persist list
    const list = this.model.getParticipantUserNames().join('\n');
    fs.writeFile(this.settings.listPath, list, (error) => {
      if (error) {
        this._reportError(`Failed to save list due to ${error}. List is currently: ${list}`);
      }
    });
  }

  _reject(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.rejected(participant.username);
    participant.attending = AttendanceEnum.NOT_ATTENDING;
    const nextUser = this.model.getNextParticipant(participant);
    if (!nextUser) {
      this.state = States.NO_RESPONSIBLE;
      this.messageService.nobodyAttending();
      this.model.setResponsible(this.model.participants[0]);
    }
    else {
      this.model.setResponsible(nextUser);
      this.messageService.notifyResponsible();
    }
  }

  _attending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.ATTENDING;
    this.messageService.attending(participant.username);
  }

  _notAttending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.NOT_ATTENDING;
    this.messageService.notAttending(participant.username);
  }

  _canChangeAttendanceStatus() {
    return this.state === States.ASKED_RESPONSBILE || this.state === States.FOUND_RESPONSIBLE;
  }

  _changeResponsible(newResposibleUserName) {
    const responsible = this.model.getParticipantFromUserName(newResposibleUserName);
    if (responsible) {
      if (responsible !== this.model.getResponsible()) {
        this.messageService.noLongerResponsible();
        this.model.setResponsible(responsible);
      }
      this._updateList();
      this.messageService.participationList();
      this.messageService.responsibleChanged(responsible.username);
    }
    else {
      this.messageService.notParticipant(newResposibleUserName);
    }
  }

  _skipNextMeeting() {
    this.state = States.SKIPPED;
    this.messageService.skipping();
  }

  _notifyParticipants() {
    this.messageService.notifyResponsible();
    this.messageService.queryForAttendance();
  }

  _reportError(error) {
    console.error(error);
    this.messageService.error(error);
  }
}

module.exports = RoundpiecesBot;
<commit_msg>Set minutes correctly for reset date<commit_after>'use strict';

const Bot = require('slackbots');
const fs = require('fs');
const CronJob = require('cron').CronJob;
const Model = require('./model');
const MessageService = require('./messageservice');
const AttendanceEnum = require('./participant').AttendanceEnum;

const States = Object.freeze({
  IDLE: 'idle',
  ASKED_RESPONSBILE: 'asked responsible',
  FOUND_RESPONSIBLE: 'found responsible',
  NO_RESPONSIBLE: 'no responsible',
  AWAITING_MEETING: 'awaiting meeting',
  SKIPPED: 'skipped'
});

class RoundpiecesBot extends Bot {
  constructor(settings) {
    super(settings);
    this.settings = settings;
    this.state = States.IDLE;
    this.model = new Model(this.settings.adminUserName);
    this.messageService = new MessageService((userName, message) => this.postMessageToUser(userName, message), this.model);
  }

  run() {
    this.on('start', this._onStart);
    this.on('message', this._onMessage);
  }

  get state() {
    return this._state;
  }

  set state(state) {
    this._state = state;
  }

  _onStart() {
    this.startTime = Date.now();

    fs.readFile(this.settings.listPath, 'utf8', (error, data) => this._setup(error, data));
  }

  _setup(error, data) {
    if (error) {
      this._reportError(error);
    }
    else {
      this.model.users = this.users;
      this.model.participants = data;
      if (this.model.getParticipantCount() < 1) {
        this._reportError('No participants in list');
        return;
      }
      this.model.setResponsible(this.model.participants[0]);
      this._setupCronJobs();

      this.messageService.botActivated(this.settings.name);
    }
  }

  _setupCronJobs() {
    const cronRanges = this.settings.cronRanges;
    new CronJob(cronRanges.start, () => this._startResponsibleSearch(), null, true);
    new CronJob(cronRanges.end, () => this._endResponsibleSearch(), null, true);
    new CronJob(cronRanges.reset, () => this._reset(), null, true);
  }

  _setupOneTimeJobs() {
    const endDate = this._getSearchEndTime();
    const resetDate = this._getResetTime();

    this._startResponsibleSearch();
    new CronJob(endDate, () => this._endResponsibleSearch(), null, true);
    new CronJob(resetDate, () => this._reset(), null, true);
  }

  /**
   * End time defaults to today at 12.00. If the search is started after 12.00, it defaults to an hour after the script
   * was invoked.
   * Doesn't handle the case where the script is invoked after 23.00.
   */
  _getSearchEndTime() {
    const now = new Date();
    const endDate = new Date();
    endDate.setHours(12);
    endDate.setMinutes(0);

    if (endDate < now) {
      endDate.setHours(now.getHours() + 1);
      endDate.setMinutes(now.getMinutes());
    }
    return endDate;
  }

  /**
   * Reset time defaults to today at 23.00.
   * Doesn't handle the case where the script is invoked after 23.00.
   */
  _getResetTime() {
    const resetDate = new Date();
    resetDate.setHours(23);
    resetDate.setMinutes(0);
    return resetDate;
  }

  _startResponsibleSearch() {
    if (this.state !== States.SKIPPED) {
      this.state = States.ASKED_RESPONSBILE;
      this._notifyParticipants();
    }
  }

  _endResponsibleSearch() {
    switch (this.state) {
      case States.FOUND_RESPONSIBLE:
        this.messageService.participationList();
        break;
      case States.ASKED_RESPONSBILE:
        this.messageService.noResponsibleResponse();
        break;
      case States.NO_RESPONSIBLE:
      case States.SKIPPED:
        //No attendance - don't do anything
        break;
    }
    this.state = States.AWAITING_MEETING;
  }

  _reset() {
    this.model.reset();
    this.state = States.IDLE;
  }

  _onMessage(message) {
    if (message.type === 'message' && message.user) {
      const participant = this.model.getParticipantFromId(message.user);
      if (!participant) {
        this._reportError(`Unknown user id: ${message.user}`);
        console.log(message);
        return;
      }

      if (message.text.startsWith('admin')) {
        if (participant.admin) {
          const messageParts = message.text.split(' ');
          switch (messageParts[1]) {
            case 'help':
              this.messageService.adminHelp();
              break;
            case 'setResponsible':
              this._changeResponsible(messageParts[2]);
              break;
            case 'skip':
              this._skipNextMeeting();
              break;
            case 'start':
              this._setupOneTimeJobs();
              break;
            default:
              this.messageService.unknownCommand(participant.username);
              break;
          }
        }
        else {
          this.messageService.notAdmin(participant.username);
        }
      }
      else {
        switch (message.text.toLowerCase()) {
          case 'help':
          case '?':
            this.messageService.help(participant.username);
            break;
          case 'status':
            this._printStatus(participant.username);
            break;
          case 'next':
            this.messageService.next(participant.username);
            break;
          case 'list':
            this.messageService.list(participant.username);
            break;
          case 'accept':
            this._accept(participant);
            break;
          case 'reject':
            this._reject(participant);
            break;
          case 'yes':
            this._attending(participant);
            break;
          case 'no':
            this._notAttending(participant);
            break;
          default:
            this.messageService.unknownCommand(participant.username);
            break;
        }
      }
    }
  }

  _printStatus(userName) {
    const uptime = Date.now() - this.startTime;
    this.messageService.status(userName, uptime);
  }

  _accept(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.accepted(participant.username);
    participant.attending = AttendanceEnum.ATTENDING;
    this.state = States.FOUND_RESPONSIBLE;
    this._updateList();
  }

  _updateList() {
    this.model.updateList();
    //Persist list
    const list = this.model.getParticipantUserNames().join('\n');
    fs.writeFile(this.settings.listPath, list, (error) => {
      if (error) {
        this._reportError(`Failed to save list due to ${error}. List is currently: ${list}`);
      }
    });
  }

  _reject(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.rejected(participant.username);
    participant.attending = AttendanceEnum.NOT_ATTENDING;
    const nextUser = this.model.getNextParticipant(participant);
    if (!nextUser) {
      this.state = States.NO_RESPONSIBLE;
      this.messageService.nobodyAttending();
      this.model.setResponsible(this.model.participants[0]);
    }
    else {
      this.model.setResponsible(nextUser);
      this.messageService.notifyResponsible();
    }
  }

  _attending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.ATTENDING;
    this.messageService.attending(participant.username);
  }

  _notAttending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.NOT_ATTENDING;
    this.messageService.notAttending(participant.username);
  }

  _canChangeAttendanceStatus() {
    return this.state === States.ASKED_RESPONSBILE || this.state === States.FOUND_RESPONSIBLE;
  }

  _changeResponsible(newResposibleUserName) {
    const responsible = this.model.getParticipantFromUserName(newResposibleUserName);
    if (responsible) {
      if (responsible !== this.model.getResponsible()) {
        this.messageService.noLongerResponsible();
        this.model.setResponsible(responsible);
      }
      this._updateList();
      this.messageService.participationList();
      this.messageService.responsibleChanged(responsible.username);
    }
    else {
      this.messageService.notParticipant(newResposibleUserName);
    }
  }

  _skipNextMeeting() {
    this.state = States.SKIPPED;
    this.messageService.skipping();
  }

  _notifyParticipants() {
    this.messageService.notifyResponsible();
    this.messageService.queryForAttendance();
  }

  _reportError(error) {
    console.error(error);
    this.messageService.error(error);
  }
}

module.exports = RoundpiecesBot;
<|endoftext|>


<commit_before>const express = require('express');
const router = express.Router();

const {  }

router.get('/', (req, res) => {
    if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === 'alexander') {
        console.log("Validating webhook");
        res.status(200).send(req.query['hub.challenge']);
    }
    else {
    console.error("Failed validation. Make sure the validation tokens match.");
    res.sendStatus(403);
  }
});

module.exports = router;<commit_msg>bugfix<commit_after>const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
    if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === 'alexander') {
        console.log("Validating webhook");
        res.status(200).send(req.query['hub.challenge']);
    }
    else {
    console.error("Failed validation. Make sure the validation tokens match.");
    res.sendStatus(403);
  }
});

module.exports = router;<|endoftext|>


<commit_before>const cheerio = require('cheerio');

/**
 * Given this layout:
 *

 *
001 - Araças - Praia da Costa

 *
Via Ibes, Gloria, Vila Velha e Rodoviaria de Itaparica

 *   Visualizar
 *

 *
 * Should return:
 *
 * {
 *   [
 *     {
 *       rua: [Array of addresses splitted by ',' or ' e '],
 *       sentido: 1,
 *       linhaId: '001',
 *     },
 *     ....
 *     {
 *       rua: [Reverse array of addresses splitted by ',' or ' e '],
 *       sentido: 2,
 *       linhaId: '001',
 *     }
 *     ....
 *   ]
 * }
 *
 */
const parseItinerarios = (body) => {
  const $ = cheerio.load(body);

  return $('.feature').map((i, el) => {
    const [linha] = $(el).find('h5').text().split(' - ');
    const itinerarios = $(el).find('p').text().replace('Via ', '').trim().split(/,| e /);

    const ida = itinerarios.map((itinerario) => {
      return {
        sentido: 1,
        linhaId: linha,
        rua: itinerario.trim()
      };
    });

    const volta = itinerarios.reverse().map((itinerario) => {
      return {
        sentido: 2,
        linhaId: linha,
        rua: itinerario.trim()
      };
    });

    return ida.concat(volta);
  }).get();
};

/**
 * Given this layout:
 *

 *
001 - Araças - Praia da Costa

 *
Via Ibes, Gloria, Vila Velha e Rodoviaria de Itaparica

 *   Visualizar
 *

 *
 * Should return:
 *
 * {
 *   linha: 001,
 *   nome: 'Araças x Praia da Costa',
 *   saida: 'Araças',
 *   destino: 'Praia da Costa'
 * }
 */
const parseLinhas = (empresaId, body) => {
  const $ = cheerio.load(body);

  return $('.feature').map((i, el) => {
    const [linha, saida, destino] = $(el).find('h5').text().split(' - ');

    return {
      linha,
      nome: `${saida} x ${destino}`,
      saida,
      destino,
      empresaId
    };
  }).get();
};

const parseHorarios = (linhaId, evtData) => {
  const halfPage = evtData.formImage.Width / 2;
  const firstDayHeight = 26;
  const secondDayHeight = 36;
  const lastDayHeight = 46;

  const horarios = evtData.formImage.Pages.map((pdf) => {
    return pdf.Texts
      .filter((obj) => {
        const txt = decodeURIComponent(obj.R[0].T).trim().split(/:|;/);
        return txt.length == 2 && !isNaN(parseInt(txt[0], 10)) && obj.y < lastDayHeight;
      })
      .map((obj) => {
        const txt = decodeURIComponent(obj.R[0].T).trim().split(/:|;/);
        const hora = parseInt(txt[0], 10);
        const minuto = parseInt(txt[1], 10);
        const x = obj.x;
        const y = obj.y;
        const sentido = x < halfPage ? 1 : 2;
        const dia = y < firstDayHeight ? 1 : (y < secondDayHeight ? 2 : 3);

        return {
          hora,
          minuto,
          sentido,
          dia,
          siglaObs: null,
          linhaId,
        };
      });
  });

  return [].concat.apply([], horarios);
};

exports = module.exports = {
  parseItinerarios,
  parseLinhas,
  parseHorarios
};
<commit_msg>Acerta nomes de linhas circulares<commit_after>const cheerio = require('cheerio');

/**
 * Given this layout:
 *

 *
001 - Araças - Praia da Costa

 *
Via Ibes, Gloria, Vila Velha e Rodoviaria de Itaparica

 *   Visualizar
 *

 *
 * Should return:
 *
 * {
 *   [
 *     {
 *       rua: [Array of addresses splitted by ',' or ' e '],
 *       sentido: 1,
 *       linhaId: '001',
 *     },
 *     ....
 *     {
 *       rua: [Reverse array of addresses splitted by ',' or ' e '],
 *       sentido: 2,
 *       linhaId: '001',
 *     }
 *     ....
 *   ]
 * }
 *
 */
const parseItinerarios = (body) => {
  const $ = cheerio.load(body);

  return $('.feature').map((i, el) => {
    const [linha] = $(el).find('h5').text().split(' - ');
    const itinerarios = $(el).find('p').text().replace('Via ', '').trim().split(/,| e /);

    const ida = itinerarios.map((itinerario) => {
      return {
        sentido: 1,
        linhaId: linha,
        rua: itinerario.trim()
      };
    });

    const volta = itinerarios.reverse().map((itinerario) => {
      return {
        sentido: 2,
        linhaId: linha,
        rua: itinerario.trim()
      };
    });

    return ida.concat(volta);
  }).get();
};

/**
 * Given this layout:
 *

 *
001 - Araças - Praia da Costa

 *
Via Ibes, Gloria, Vila Velha e Rodoviaria de Itaparica

 *   Visualizar
 *

 *
 * Should return:
 *
 * {
 *   linha: 001,
 *   nome: 'Araças x Praia da Costa',
 *   saida: 'Araças',
 *   destino: 'Praia da Costa'
 * }
 */
const parseLinhas = (empresaId, body) => {
  const $ = cheerio.load(body);

  return $('.feature').map((i, el) => {
    const [linha, saida, destino] = $(el).find('h5').text().split(' - ');

    return {
      linha,
      nome: destino
        ? `${saida} x ${destino}`
        : saida,
      saida,
      destino,
      empresaId
    };
  }).get();
};

const parseHorarios = (linhaId, evtData) => {
  const halfPage = evtData.formImage.Width / 2;
  const firstDayHeight = 26;
  const secondDayHeight = 36;
  const lastDayHeight = 46;

  const horarios = evtData.formImage.Pages.map((pdf) => {
    return pdf.Texts
      .filter((obj) => {
        const txt = decodeURIComponent(obj.R[0].T).trim().split(/:|;/);
        return txt.length == 2 && !isNaN(parseInt(txt[0], 10)) && obj.y < lastDayHeight;
      })
      .map((obj) => {
        const txt = decodeURIComponent(obj.R[0].T).trim().split(/:|;/);
        const hora = parseInt(txt[0], 10);
        const minuto = parseInt(txt[1], 10);
        const x = obj.x;
        const y = obj.y;
        const sentido = x < halfPage ? 1 : 2;
        const dia = y < firstDayHeight ? 1 : (y < secondDayHeight ? 2 : 3);

        return {
          hora,
          minuto,
          sentido,
          dia,
          siglaObs: null,
          linhaId,
        };
      });
  });

  return [].concat.apply([], horarios);
};

exports = module.exports = {
  parseItinerarios,
  parseLinhas,
  parseHorarios
};
<|endoftext|>