<commit_before>'use strict';

exports.parseDomains = parseDomains;
exports.parseOptions = parseOptions;
exports.parseHTMLFilter = parseHTMLFilter;
exports.parseFilter = parseFilter;
exports.parse = parse;
exports.matchesFilter = matchesFilter;
exports.matches = matches;
var elementTypes = {
  SCRIPT: 1,
  IMAGE: 2,
  STYLESHEET: 4,
  OBJECT: 8,
  XMLHTTPREQUEST: 16,
  OBJECTSUBREQUEST: 32,
  SUBDOCUMENT: 64,
  DOCUMENT: 128,
  OTHER: 256
};

exports.elementTypes = elementTypes;
var separatorCharacters = ':?/=^';

/**
 * Parses the domain string using the passed in separator and
 * fills in options.
 */

function parseDomains(input, separator, options) {
  options.domains = options.domains || [];
  options.skipDomains = options.skipDomains || [];
  var domains = input.split(separator);
  options.domains = options.domains.concat(domains.filter(function (domain) {
    return domain[0] !== '~';
  }));
  options.skipDomains = options.skipDomains.concat(domains.filter(function (domain) {
    return domain[0] === '~';
  }).map(function (domain) {
    return domain.substring(1);
  }));
}

/**
 * Parses options from the passed in input string
 */

function parseOptions(input) {
  var output = {
    binaryOptions: new Set()
  };
  input.split(',').forEach(function (option) {
    option = option.trim();
    if (option.startsWith('domain=')) {
      var domainString = option.split('=')[1].trim();
      parseDomains(domainString, '|', output);
    } else {
      var optionWithoutPrefix = option[0] === '~' ? option.substring(1) : option;
      if (elementTypeMaskMap.has(optionWithoutPrefix)) {
        if (option[0] === '~') {
          output.skipElementTypeMask |= elementTypeMaskMap.get(optionWithoutPrefix);
        } else {
          output.elementTypeMask |= elementTypeMaskMap.get(optionWithoutPrefix);
        }
      }
      output.binaryOptions.add(option);
    }
  });
  return output;
}

/**
 * Finds the first separator character in the input string
 */
function findFirstSeparatorChar(input, startPos) {
  for (var i = startPos; i < input.length; i++) {
    if (separatorCharacters.indexOf(input[i]) !== -1) {
      return i;
    }
  }
  return -1;
}

/**
 * Parses an HTML filter and modifies the passed in parsedFilterData
 * as necessary.
 *
 * @param input: The entire input string to consider
 * @param index: Index of the first hash
 * @param parsedFilterData: The parsedFilterData object to fill
 */

function parseHTMLFilter(input, index, parsedFilterData) {
  var domainsStr = input.substring(0, index);
  parsedFilterData.options = {};
  if (domainsStr.length > 0) {
    parseDomains(domainsStr, ',', parsedFilterData.options);
  }

  // The XOR parsedFilterData.elementHidingException is in case the rule already
  // was specified as exception handling with a prefixed @@
  parsedFilterData.isException = !!(input[index + 1] === '@' ^ parsedFilterData.isException);
  if (input[index + 1] === '@') {
    // Skip passed the first # since @# is 2 chars same as ##
    index++;
  }
  parsedFilterData.htmlRuleSelector = input.substring(index + 2);
}

function parseFilter(input, parsedFilterData) {
  input = input.trim();

  // Check for comment or nothing
  if (input.length === 0) {
    return false;
  }

  // Check for comments
  var beginIndex = 0;
  if (input[beginIndex] === '[' || input[beginIndex] === '!') {
    parsedFilterData.isComment = true;
    return false;
  }

  // Check for exception instead of filter
  parsedFilterData.isException = input[beginIndex] === '@' && input[beginIndex + 1] === '@';
  if (parsedFilterData.isException) {
    beginIndex = 2;
  }

  // Check for element hiding rules
  var index = input.indexOf('#', beginIndex);
  if (index !== -1) {
    if (input[index + 1] === '#' || input[index + 1] === '@') {
      parseHTMLFilter(input.substring(beginIndex), index - beginIndex, parsedFilterData);
      // HTML rules cannot be combined with other parsing,
      // other than @@ exception marking.
      return true;
    }
  }

  // Check for options, regex can have options too so check this before regex
  index = input.indexOf('$', beginIndex);
  if (index !== -1) {
    parsedFilterData.options = parseOptions(input.substring(index + 1));
    // Get rid of the trailing options for the rest of the parsing
    input = input.substring(0, index);
  } else {
    parsedFilterData.options = {};
  }

  // Check for a regex
  parsedFilterData.isRegex = input[beginIndex] === '/' && input[input.length - 1] === '/' && beginIndex !== input.length - 1;
  if (parsedFilterData.isRegex) {
    parsedFilterData.data = input.slice(beginIndex + 1, -1);
    return true;
  }

  // Check if there's some kind of anchoring
  if (input[beginIndex] === '|') {
    // Check for an anchored domain name
    if (input[beginIndex + 1] === '|') {
      parsedFilterData.hostAnchored = true;
      var indexOfSep = findFirstSeparatorChar(input, beginIndex + 1);
      if (indexOfSep === -1) {
        indexOfSep = input.length;
      }
      beginIndex += 2;
      parsedFilterData.host = input.substring(beginIndex, indexOfSep);
    } else {
      parsedFilterData.leftAnchored = true;
      beginIndex++;
    }
  }
  if (input[input.length - 1] === '|') {
    parsedFilterData.rightAnchored = true;
    input = input.substring(0, input.length - 1);
  }

  parsedFilterData.data = input.substring(beginIndex) || '*';
  return true;
}

/**
 * Parses the set of filter rules and fills in parserData
 * @param input filter rules
 * @param parserData out parameter which will be filled
 *   with the filters, exceptionFilters and htmlRuleFilters.
 */

function parse(input, parserData) {
  parserData.filters = parserData.filters || [];
  parserData.exceptionFilters = parserData.exceptionFilters || [];
  parserData.htmlRuleFilters = parserData.htmlRuleFilters || [];
  var startPos = 0;
  var endPos = input.length;
  var newline = '\n';
  while (startPos <= input.length) {
    endPos = input.indexOf(newline, startPos);
    if (endPos === -1) {
      newline = '\r';
      endPos = input.indexOf(newline, startPos);
    }
    if (endPos === -1) {
      endPos = input.length;
    }
    var filter = input.substring(startPos, endPos);
    var parsedFilterData = {};
    if (parseFilter(filter, parsedFilterData)) {
      if (parsedFilterData.htmlRuleSelector) {
        parserData.htmlRuleFilters.push(parsedFilterData);
      } else if (parsedFilterData.isException) {
        parserData.exceptionFilters.push(parsedFilterData);
      } else {
        parserData.filters.push(parsedFilterData);
      }
    }
    startPos = endPos + 1;
  }
}

/**
 * Obtains the domain index of the input filter line
 */
function getDomainIndex(input) {
  var index = input.indexOf(':');
  ++index;
  while (input[index] === '/') {
    index++;
  }
  return index;
}

/**
 * Similar to str1.indexOf(filter, startingPos) but with
 * extra consideration to some ABP filter rules like ^.
 */
function indexOfFilter(input, filter, startingPos) {
  if (filter.length > input.length) {
    return -1;
  }

  var filterParts = filter.split('^');
  var index = startingPos;
  var beginIndex = -1;
  var prefixedSeparatorChar = false;

  for (var f = 0; f < filterParts.length; f++) {
    if (filterParts[f] === '') {
      prefixedSeparatorChar = true;
      continue;
    }

    index = input.indexOf(filterParts[f], index);
    if (index === -1) {
      return -1;
    }
    if (beginIndex === -1) {
      beginIndex = index;
    }

    if (prefixedSeparatorChar) {
      if (separatorCharacters.indexOf(input[index - 1]) === -1) {
        return -1;
      }
    }
    // If we are in an in between filterPart
    if (f + 1 < filterParts.length &&
    // and we have some chars left in the input past the last filter match
    input.length > index + filterParts[f].length) {
      if (separatorCharacters.indexOf(input[index + filterParts[f].length]) === -1) {
        return -1;
      }
    }

    prefixedSeparatorChar = false;
  }
  return beginIndex;
}

function getUrlHost(input) {
  var domainIndexStart = getDomainIndex(input);
  var domainIndexEnd = findFirstSeparatorChar(input, domainIndexStart);
  if (domainIndexEnd === -1) {
    domainIndexEnd = input.length;
  }
  return input.substring(domainIndexStart, domainIndexEnd);
}

function filterDataContainsOption(parsedFilterData, option) {
  return parsedFilterData.options && parsedFilterData.options.binaryOptions && parsedFilterData.options.binaryOptions.has(option);
}

function isThirdPartyHost(baseContextHost, testHost) {
  if (!testHost.endsWith(baseContextHost)) {
    return true;
  }

  var c = testHost[testHost.length - baseContextHost.length - 1];
  return c !== '.' && c !== undefined;
}

// Determines if there's a match based on the options, this doesn't
// mean that the filter rule shoudl be accepted, just that the filter rule
// should be considered given the current context.
// By specifying context params, you can filter out the number of rules which are
// considered.
function matchOptions(parsedFilterData, input) {
  var contextParams = arguments[2] === undefined ? {} : arguments[2];

  if (contextParams.elementTypeMask !== undefined && parsedFilterData.options) {
    if (parsedFilterData.options.elementTypeMask !== undefined && !(parsedFilterData.options.elementTypeMask & contextParams.elementTypeMask)) {
      return false;
    }if (parsedFilterData.options.skipElementTypeMask !== undefined && parsedFilterData.options.skipElementTypeMask & contextParams.elementTypeMask) {
      return false;
    }
  }

  // Domain option check
  if (contextParams.domain !== undefined && parsedFilterData.options) {
    if (parsedFilterData.options.domains || parsedFilterData.options.skipDomains) {
      var _ret = (function () {
        // Get the domains that should be considered
        var shouldBlockDomains = parsedFilterData.options.domains.filter(function (domain) {
          return !isThirdPartyHost(domain, contextParams.domain);
        });

        var shouldSkipDomains = parsedFilterData.options.skipDomains.filter(function (domain) {
          return !isThirdPartyHost(domain, contextParams.domain);
        });
        // Handle cases like: example.com|~foo.example.com should llow for foo.example.com
        // But ~example.com|foo.example.com should block for foo.example.com
        var leftOverBlocking = shouldBlockDomains.filter(function (shouldBlockDomain) {
          return shouldSkipDomains.every(function (shouldSkipDomain) {
            return isThirdPartyHost(shouldBlockDomain, shouldSkipDomain);
          });
        });
        var leftOverSkipping = shouldSkipDomains.filter(function (shouldSkipDomain) {
          return shouldBlockDomains.every(function (shouldBlockDomain) {
            return isThirdPartyHost(shouldSkipDomain, shouldBlockDomain);
          });
        });

        // If we have none left over, then we shouldn't consider this a match
        if (shouldBlockDomains.length === 0 && parsedFilterData.options.domains.length !== 0 || shouldBlockDomains.length > 0 && leftOverBlocking.length === 0 || shouldSkipDomains.length > 0 && leftOverSkipping.length > 0) {
          return {
            v: false
          };
        }
      })();

      if (typeof _ret === 'object') return _ret.v;
    }
  }

  // If we're in the context of third-party site, then consider third-party option checks
  if (contextParams['third-party'] !== undefined) {
    // Is the current rule check for third party only?
    if (filterDataContainsOption(parsedFilterData, 'third-party')) {
      var inputHost = getUrlHost(input);
      var inputHostIsThirdParty = isThirdPartyHost(parsedFilterData.host, inputHost);
      if (inputHostIsThirdParty || !contextParams['third-party']) {
        return false;
      }
    }
  }

  return true;
}

/**
 * Given an individual parsed filter data determines if the input url should block.
 */

function matchesFilter(parsedFilterData, input) {
  var contextParams = arguments[2] === undefined ? {} : arguments[2];
  var cachedInputData = arguments[3] === undefined ? {} : arguments[3];

  if (!matchOptions(parsedFilterData, input, contextParams)) {
    return false;
  }

  // Check for a regex match
  if (parsedFilterData.isRegex) {
    if (!parsedFilterData.regex) {
      parsedFilterData.regex = new RegExp(parsedFilterData.data);
    }
    return parsedFilterData.regex.test(input);
  }

  // Check for both left and right anchored
  if (parsedFilterData.leftAnchored && parsedFilterData.rightAnchored) {
    return parsedFilterData.data === input;
  }

  // Check for right anchored
  if (parsedFilterData.rightAnchored) {
    return input.slice(-parsedFilterData.data.length) === parsedFilterData.data;
  }

  // Check for left anchored
  if (parsedFilterData.leftAnchored) {
    return input.substring(0, parsedFilterData.data.length) === parsedFilterData.data;
  }

  // Check for domain name anchored
  if (parsedFilterData.hostAnchored) {
    if (!cachedInputData.currentHost) {
      cachedInputData.currentHost = getUrlHost(input);
    }

    return !isThirdPartyHost(parsedFilterData.host, cachedInputData.currentHost) && indexOfFilter(input, parsedFilterData.data) !== -1;
  }

  // Wildcard match comparison
  var parts = parsedFilterData.data.split('*');
  var index = 0;
  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var part = _step.value;

      var newIndex = indexOfFilter(input, part, index);
      if (newIndex === -1) {
        return false;
      }
      index = newIndex + part.length;
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator['return']) {
        _iterator['return']();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }

  return true;
}

var maxCached = 100;

/**
 * Using the parserData rules will try to see if the input URL should be blocked or not
 * @param parserData The filter data obtained from a call to parse
 * @param input The input URL
 * @return true if the URL should be blocked
 */

function matches(parserData, input) {
  var contextParams = arguments[2] === undefined ? {} : arguments[2];
  var cachedInputData = arguments[3] === undefined ? {} : arguments[3];

  delete cachedInputData.currentHost;
  cachedInputData.misses = cachedInputData.misses || new Set();
  cachedInputData.missList = cachedInputData.missList || [];
  if (cachedInputData.missList.length > maxCached) {
    cachedInputData.misses['delete'](cachedInputData.missList[0]);
    cachedInputData.missList = cachedInputData.missList.splice(1);
  }
  if (cachedInputData.misses.has(input)) {
    return false;
  }

  if (parserData.filters.some(function (parsedFilterData) {
    return matchesFilter(parsedFilterData, input, contextParams, cachedInputData);
  })) {
    // Check for exceptions only when there's a match because matches are
    // rare compared to the volume of checks
    return !parserData.exceptionFilters.some(function (parsedFilterData) {
      return matchesFilter(parsedFilterData, input, contextParams, cachedInputData);
    });
  }

  cachedInputData.missList.push(input);
  cachedInputData.misses.add(input);
  return false;
}

//# sourceMappingURL=abp-filter-parser-commonjs.js.map
<commit_msg>Remove unused custom packaged abp-filter-parser<commit_after><|endoftext|>


<commit_before>"use strict";

const slackFetch = require('./lib/slack-fetch');
const store = require('./lib/store');

const client_id = process.env.SLACK_APP_CLIENT_ID;
const client_secret = process.env.SLACK_APP_CLIENT_SECRET;
const KEY = process.env.JWT_KEY;

if (!client_id || !client_secret || !KEY) {
  throw "SLACK_APP_CLIENT_ID and SLACK_APP_CLIENT_SECRET and JWT_KEY must be set to operate";
}

module.exports = async function (req, res, next) {
  try {
    if (req.params.code) {
      const body = await slackFetch('https://slack.com/api/oauth.access', Object.assign({
        client_id,
        client_secret,
        code: req.params.code
      }, req.params.redirect_uri ? { redirect_uri: req.params.redirect_uri } : {}));

      if (!body.access_token) {
        throw new Error("No access token granted");
      }

      const sid = await store.new(body);

      res.send({ token: body, sid });
      next();
    } else {
      throw new Error("No code supplied");
    }
  } catch(e) {
    next(e);
  }
};
<commit_msg>Excise jwt key<commit_after>"use strict";

const slackFetch = require('./lib/slack-fetch');
const store = require('./lib/store');

const client_id = process.env.SLACK_APP_CLIENT_ID;
const client_secret = process.env.SLACK_APP_CLIENT_SECRET;

if (!client_id || !client_secret) {
  throw "SLACK_APP_CLIENT_ID and SLACK_APP_CLIENT_SECRET must be set to operate";
}

module.exports = async function (req, res, next) {
  try {
    if (req.params.code) {
      const body = await slackFetch('https://slack.com/api/oauth.access', Object.assign({
        client_id,
        client_secret,
        code: req.params.code
      }, req.params.redirect_uri ? { redirect_uri: req.params.redirect_uri } : {}));

      if (!body.access_token) {
        throw new Error("No access token granted");
      }

      const sid = await store.new(body);

      res.send({ token: body, sid });
      next();
    } else {
      throw new Error("No code supplied");
    }
  } catch(e) {
    next(e);
  }
};
<|endoftext|>


<commit_before>
AFRAME.registerComponent('spawner', {
  schema: {
    on: { default: 'click' },
    mixin: { default: '' }
  },

  /**
   * Add event listener.
   */
  update: function (oldData) {
    this.el.addEventListener(this.data.on, this.spawn.bind(this));
  },

  /**
   * Spawn new entity at entity's current position.
   */
  spawn: function () {
    var el = this.el;
    var entity = document.createElement('a-entity');
    var matrixWorld = el.object3D.matrixWorld;
    var position = new THREE.Vector3();
    var rotation = el.getAttribute('rotation');
    var cameraEl = el.parentElement;
    var tankEl = cameraEl.parentElement.parentElement;
    var cameraRotation = cameraEl.getAttribute('rotation');
    var tankRotation = tankEl.getAttribute('rotation');
    var tankVel = tankEl.getAttribute('velocity');
    var entityRotation;

    position.setFromMatrixPosition(matrixWorld);
    entity.setAttribute('position', position);
    entity.setAttribute('mixin', this.data.mixin);

    // Rotate to heading based on turret rotation and tank rotation
    var velocity = new THREE.Vector3(0, 0, -30);
    if (cameraRotation) {
      const cameraHeading = new THREE.Euler(0, 0, 0, 'YXZ');
      cameraHeading.set(
        THREE.Math.degToRad(cameraRotation.x),
        THREE.Math.degToRad(cameraRotation.y),
        THREE.Math.degToRad(cameraRotation.z)
      );
      velocity.applyEuler(cameraHeading);
    }
    if(tankRotation) {
      const tankHeading = new THREE.Euler(0, 0, 0, 'YXZ');
      tankHeading.set(
        THREE.Math.degToRad(tankRotation.x),
        THREE.Math.degToRad(tankRotation.y),
        THREE.Math.degToRad(tankRotation.z)
      );
      velocity.applyEuler(tankHeading);
    }

    // Add momentum from tank
    velocity.add(tankVel);

    // Set velocity on projectile
    entity.setAttribute('velocity', velocity);

    // Have the spawned entity face the same direction as the entity.
    // Allow the entity to further modify the inherited rotation.
    // entity.addEventListener('loaded', function () {
    //   entityRotation = entity.getComputedAttribute('rotation');
    //   entity.setAttribute('rotation', {
    //     x: entityRotation.x + rotation.x,
    //     y: entityRotation.y + rotation.y,
    //     z: entityRotation.z + rotation.z
    //   });
    // });
    el.sceneEl.appendChild(entity);
  }
});
<commit_msg>Increase flexibility of spawner component<commit_after>
AFRAME.registerComponent('spawner', {
  schema: {
    on: { default: 'click' },
    mixin: { default: '' },
    enabled: {default: true}
  },

  /**
   * Add event listener.
   */
  update: function (oldData) {
    if(this.data.enabled) {
      this.el.addEventListener(this.data.on, this.spawn.bind(this));
    }
  },

  /**
   * Spawn new entity at entity's current position.
   */
  spawn: function () {
    var el = this.el;
    var entity = document.createElement('a-entity');
    var matrixWorld = el.object3D.matrixWorld;
    var position = new THREE.Vector3();
    var rotation = el.getAttribute('rotation');
    var cameraEl = document.querySelector('#camera').object3D.el;
    // var cameraEl = el.parentElement.parentElement;
    var tankEl = document.querySelector('#tankBody').object3D.el;
    // var tankEl = cameraEl.parentElement.parentElement;
    var cameraRotation = cameraEl.getAttribute('rotation');
    var tankRotation = tankEl.getAttribute('rotation');
    var tankVel = tankEl.getAttribute('velocity');
    var entityRotation;

    // console.log('El', el);
    // console.log('cameraEl', cameraEl);
    // console.log('tankEl', tankEl);

    position.setFromMatrixPosition(matrixWorld);
    entity.setAttribute('position', position);
    entity.setAttribute('mixin', this.data.mixin);

    // Rotate to heading based on turret rotation and tank rotation
    var velocity = new THREE.Vector3(0, 0, -30);
    if (cameraRotation) {
      const cameraHeading = new THREE.Euler(0, 0, 0, 'YXZ');
      cameraHeading.set(
        THREE.Math.degToRad(cameraRotation.x),
        THREE.Math.degToRad(cameraRotation.y),
        THREE.Math.degToRad(cameraRotation.z)
      );
      velocity.applyEuler(cameraHeading);
    }
    if(tankRotation) {
      const tankHeading = new THREE.Euler(0, 0, 0, 'YXZ');
      tankHeading.set(
        THREE.Math.degToRad(tankRotation.x),
        THREE.Math.degToRad(tankRotation.y),
        THREE.Math.degToRad(tankRotation.z)
      );
      velocity.applyEuler(tankHeading);
    }

    // Add momentum from tank
    velocity.add(tankVel);

    // Set velocity on projectile
    entity.setAttribute('velocity', velocity);

    // Have the spawned entity face the same direction as the entity.
    // Allow the entity to further modify the inherited rotation.
    // entity.addEventListener('loaded', function () {
    //   entityRotation = entity.getComputedAttribute('rotation');
    //   entity.setAttribute('rotation', {
    //     x: entityRotation.x + rotation.x,
    //     y: entityRotation.y + rotation.y,
    //     z: entityRotation.z + rotation.z
    //   });
    // });
    el.sceneEl.appendChild(entity);
  }
});
<|endoftext|>


<commit_before>import * as I from 'immutable';

import { rationalMatrices } from '../arithmetic/types';
const ops = rationalMatrices;

const Edge = I.Record({
  head : undefined,
  tail : undefined,
  shift: undefined
});

Edge.prototype.toString = function toString() {
  return 'Edge('+this.head+', '+this.tail+', '+this.shift+')';
};

Edge.prototype.reverse = function reverse() {
  return new Edge({
    head : this.tail,
    tail : this.head,
    shift: this.shift.map(x => -x)
  });
};

const _isNegative = vec => vec.find(x => ops.sgn(x) != 0) < 0;

Edge.prototype.canonical = function canonical() {
  if (this.tail < this.head || (this.tail == this.head
                                && _isNegative(this.shift)))
    return this.reverse();
  else
    return this;
};

const _makeEdge = function _makeEdge(e) {
  return new Edge({ head: e[0], tail: e[1], shift: e[2] }).canonical();
};

const Graph = I.Record({
  dim  : undefined,
  edges: undefined
});

Graph.prototype.toString = function toString() {
  return 'PGraph('+this.edges+')';
};

export function make(data) {
  const edges = I.Set(data).map(_makeEdge);
  if (edges.size == 0)
    throw new Error('cannot be empty');

  const dim = edges.first().shift.length;
  if (edges.some(e => e.shift.length != dim))
    throw new Error('must have consistent shift dimensions');

  return new Graph({ dim: dim, edges: edges });
};

const CoverVertex = I.Record({
  v: undefined,
  s: undefined
});

const _target = e => CoverVertex({ v: e.tail, s: e.shift });

export function adjacencies(graph) {
  let res = I.Map();

  graph.edges.forEach(function(e) {
    res = res
      .update(e.head, a => (a || I.List()).push(_target(e)))
      .update(e.tail, a => (a || I.List()).push(_target(e.reverse())));
  });

  return res;
};

export function coordinationSeq(graph, start, dist) {
  const adj  = adjacencies(graph);
  const zero = I.List(I.Repeat(0, graph.dim));
  const plus = (s, t) => I.Range(0, graph.dim).map(i => s.get(i) + t[i]);

  let oldShell = I.Set();
  let thisShell = I.Set([CoverVertex({ v: start, s: zero })]);
  let res = [1];

  I.Range(1, dist+1).forEach(function(i) {
    let nextShell = I.Set();
    thisShell.forEach(function(v) {
      adj.get(v.v).forEach(function(t) {
        const w = CoverVertex({ v: t.v, s: plus(v.s, t.s) });
        if (!oldShell.contains(w) && !thisShell.contains(w))
          nextShell = nextShell.add(w);
      });
    });

    res.push(nextShell.size);
    oldShell = thisShell;
    thisShell = nextShell;
  });

  return res;
};

const _componentInOrbitGraph = (graph, start) => {
  const adj = adjacencies(graph);
  const queue = [start]
  const bridges = [];
  const nodeShifts = I.Map([[start, ops.vector(graph.dim)]]).asMutable();

  while (queue.length) {
    const v = queue.shift();
    const av = nodeShifts.get(v);

    for (const {v: w, s} of adj.get(v)) {
      const shift = I.List(s).toArray();
      if (nodeShifts.get(w) == undefined) {
        queue.push(w);
        nodeShifts.set(w, ops.minus(av, shift));
      }
      else {
        const aw = nodeShifts.get(w);
        const newShift = ops.plus(shift, ops.minus(aw, av));
        const pivot = newShift.find(x => ops.ne(x, 0));

        if (pivot != undefined && ops.gt(pivot, 0))
          bridges.push({ v, w, s: newShift });
      }
    }
  }

  return {
    nodes: I.Set(nodeShifts.keys()),
    nodeShifts,
    bridges
  };
};

const _isConnectedOrbitGraph = (graph) => {
  const verts = I.List(adjacencies(graph).keySeq());
  const comp = _componentInOrbitGraph(graph, verts.first());

  return comp.nodes.size >= verts.size;
};

const _basis = M => {
  const T = ops.triangulation(M).R;
  return T.slice(0, ops.rank(T));
};

const _componentInCoverGraph = (graph, start) => {
  const { nodes, nodeShifts, bridges } = _componentInOrbitGraph(graph, start);
  const basis = _basis(bridges.map(b => b.s));
  const transform = ops.inverse(basis);
  const old2new = I.Map(I.List(nodes).zip(I.Range(1, nodes.size+1)));

  const newEdges = graph.edges
    .filter(({ head, tail }) =>
            old2new.get(head) != null && old2new.get(tail) != null)
    .map(({ head, tail, shift }) => {
      const [v, w] = [old2new.get(head), old2new.get(tail)];
      const [av, aw] = [nodeShifts.get(head), nodeShifts.get(tail)];
      const t = ops.times(ops.plus(shift, ops.minus(aw, av)), transform);
      return [v, w, t];
    });

  return {
    basis,
    nodes: nodes.toArray(),
    multiplicity: ops.determinant(basis),
    graph: make(newEdges)
  };
};

export function isConnected(graph) {
  const verts = I.List(adjacencies(graph).keySeq());
  const comp = _componentInCoverGraph(graph, verts.first());

  return comp.nodes.length >= verts.size && comp.multiplicity == 1;
};

export function connectedComponents(graph) {
  const verts = I.List(adjacencies(graph).keySeq());
  const seen = I.Set().asMutable();
  const result = [];

  for (const start of verts) {
    if (!seen.contains(start)) {
      const comp = _componentInCoverGraph(graph, start);
      result.push(comp);
      comp.nodes.forEach(v => seen.add(v));
    }
  }

  return result;
};

export function barycentricPlacement(graph) {
  if (!isConnected(graph))
    throw new Error('must have a connected orbit graph');

  const adj   = adjacencies(graph);
  const verts = I.List(adj.keySeq());
  const vIdcs = I.Map(I.Range(0, verts.size).map(i => [verts.get(i), i]));

  const n = verts.size;
  const d = graph.dim;
  let A = ops.matrix(n+1, n);
  let t = ops.matrix(n+1, d);

  verts.forEach((v, i) => {
    adj.get(v).forEach(c => {
      if (c.v != v) {
        const j = vIdcs.get(c.v);
        A[i][j] -= 1;
        A[i][i] += 1;
        t[i] = ops.plus(t[i], c.s);
      }
    });
  });
  A[n][0] = 1;

  const p = ops.solve(A, t);

  return I.Map(I.Range(0, n).map(i => [verts.get(i), p[i]]));
};

export function barycentricPlacementAsFloat(graph) {
  return barycentricPlacement(graph).map(p => p.map(x => ops.toJS(x)));
};

export function isStable(graph, pos=barycentricPlacement(graph)) {
  const verts = I.List(adjacencies(graph).keySeq());
  const seen = I.Set().asMutable();

  for (const v of verts) {
    const p = pos.get(v);
    const key = I.fromJS(ops.repr(p.map(x => ops.mod(x, 1))));
    if (seen.contains(key))
      return false;
    else
      seen.add(key);
  }

  return true;
};

export function isLocallyStable(graph, pos=barycentricPlacement(graph)) {
  const adj = adjacencies(graph);
  const verts = I.List(adj.keySeq());

  for (const v of verts) {
    const seen = I.Set().asMutable();

    for (const w of adj.get(v)) {
      const p = ops.plus(pos.get(w.v), w.s);
      const key = I.fromJS(ops.repr(p));
      if (seen.contains(key))
        return false;
      else
        seen.add(key);
    }
  }

  return true;
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const test = function test(g) {
    console.log('g = '+g);
    console.log('  cs  = '+coordinationSeq(g, 1, 10));
    if (isConnected(g)) {
      console.log('  pos = '+barycentricPlacement(g));
      console.log('      = '+barycentricPlacementAsFloat(g));
      console.log('  stable: '+isStable(g));
      console.log('  locally stable: '+isLocallyStable(g));
    }

    for (const comp of connectedComponents(g)) {
      console.log('  component:');
      console.log('    nodes = '+comp.nodes);
      console.log('    graph = '+comp.graph);
      console.log('    basis = '+comp.basis);
      console.log('    multiplicity = '+comp.multiplicity);
    }
    console.log();
  };

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ],
              [ 1, 4, [ 0, 0 ] ],
              [ 1, 4, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ -1,  1,  1 ] ],
              [ 1, 1, [  0, -1,  1 ] ],
              [ 1, 1, [  0,  0, -1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 0 ] ],
              [ 1, 2, [ 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 1, 0, 0 ] ],
              [ 1, 2, [ 0, 1, 0 ] ],
              [ 1, 2, [ 0, 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 2, 0, 0 ] ],
              [ 1, 2, [ 0, 2, 0 ] ],
              [ 1, 2, [ 0, 0, 2 ] ] ]));

  test(make([ [ 1, 3, [ 0, 0, 0 ] ],
              [ 1, 3, [ 2, 0, 0 ] ],
              [ 1, 3, [ 0, 2, 0 ] ],
              [ 1, 3, [ 0, 0, 2 ] ],
              [ 2, 4, [ 0, 0, 0 ] ],
              [ 2, 4, [ 2, 0, 0 ] ],
              [ 2, 4, [ 0, 2, 0 ] ],
              [ 2, 4, [ 0, 0, 2 ] ] ]));
}
<commit_msg>remove an unnecessary conversion<commit_after>import * as I from 'immutable';

import { rationalMatrices } from '../arithmetic/types';
const ops = rationalMatrices;

const Edge = I.Record({
  head : undefined,
  tail : undefined,
  shift: undefined
});

Edge.prototype.toString = function toString() {
  return 'Edge('+this.head+', '+this.tail+', '+this.shift+')';
};

Edge.prototype.reverse = function reverse() {
  return new Edge({
    head : this.tail,
    tail : this.head,
    shift: this.shift.map(x => -x)
  });
};

const _isNegative = vec => vec.find(x => ops.sgn(x) != 0) < 0;

Edge.prototype.canonical = function canonical() {
  if (this.tail < this.head || (this.tail == this.head
                                && _isNegative(this.shift)))
    return this.reverse();
  else
    return this;
};

const _makeEdge = function _makeEdge(e) {
  return new Edge({ head: e[0], tail: e[1], shift: e[2] }).canonical();
};

const Graph = I.Record({
  dim  : undefined,
  edges: undefined
});

Graph.prototype.toString = function toString() {
  return 'PGraph('+this.edges+')';
};

export function make(data) {
  const edges = I.Set(data).map(_makeEdge);
  if (edges.size == 0)
    throw new Error('cannot be empty');

  const dim = edges.first().shift.length;
  if (edges.some(e => e.shift.length != dim))
    throw new Error('must have consistent shift dimensions');

  return new Graph({ dim: dim, edges: edges });
};

const CoverVertex = I.Record({
  v: undefined,
  s: undefined
});

const _target = e => CoverVertex({ v: e.tail, s: e.shift });

export function adjacencies(graph) {
  let res = I.Map();

  graph.edges.forEach(function(e) {
    res = res
      .update(e.head, a => (a || I.List()).push(_target(e)))
      .update(e.tail, a => (a || I.List()).push(_target(e.reverse())));
  });

  return res;
};

export function coordinationSeq(graph, start, dist) {
  const adj  = adjacencies(graph);
  const zero = I.List(I.Repeat(0, graph.dim));
  const plus = (s, t) => I.Range(0, graph.dim).map(i => s.get(i) + t[i]);

  let oldShell = I.Set();
  let thisShell = I.Set([CoverVertex({ v: start, s: zero })]);
  let res = [1];

  I.Range(1, dist+1).forEach(function(i) {
    let nextShell = I.Set();
    thisShell.forEach(function(v) {
      adj.get(v.v).forEach(function(t) {
        const w = CoverVertex({ v: t.v, s: plus(v.s, t.s) });
        if (!oldShell.contains(w) && !thisShell.contains(w))
          nextShell = nextShell.add(w);
      });
    });

    res.push(nextShell.size);
    oldShell = thisShell;
    thisShell = nextShell;
  });

  return res;
};

const _componentInOrbitGraph = (graph, start) => {
  const adj = adjacencies(graph);
  const queue = [start]
  const bridges = [];
  const nodeShifts = I.Map([[start, ops.vector(graph.dim)]]).asMutable();

  while (queue.length) {
    const v = queue.shift();
    const av = nodeShifts.get(v);

    for (const {v: w, s: shift} of adj.get(v)) {
      if (nodeShifts.get(w) == undefined) {
        queue.push(w);
        nodeShifts.set(w, ops.minus(av, shift));
      }
      else {
        const aw = nodeShifts.get(w);
        const newShift = ops.plus(shift, ops.minus(aw, av));
        const pivot = newShift.find(x => ops.ne(x, 0));

        if (pivot != undefined && ops.gt(pivot, 0))
          bridges.push({ v, w, s: newShift });
      }
    }
  }

  return {
    nodes: I.Set(nodeShifts.keys()),
    nodeShifts,
    bridges
  };
};

const _isConnectedOrbitGraph = (graph) => {
  const verts = I.List(adjacencies(graph).keySeq());
  const comp = _componentInOrbitGraph(graph, verts.first());

  return comp.nodes.size >= verts.size;
};

const _basis = M => {
  const T = ops.triangulation(M).R;
  return T.slice(0, ops.rank(T));
};

const _componentInCoverGraph = (graph, start) => {
  const { nodes, nodeShifts, bridges } = _componentInOrbitGraph(graph, start);
  const basis = _basis(bridges.map(b => b.s));
  const transform = ops.inverse(basis);
  const old2new = I.Map(I.List(nodes).zip(I.Range(1, nodes.size+1)));

  const newEdges = graph.edges
    .filter(({ head, tail }) =>
            old2new.get(head) != null && old2new.get(tail) != null)
    .map(({ head, tail, shift }) => {
      const [v, w] = [old2new.get(head), old2new.get(tail)];
      const [av, aw] = [nodeShifts.get(head), nodeShifts.get(tail)];
      const t = ops.times(ops.plus(shift, ops.minus(aw, av)), transform);
      return [v, w, t];
    });

  return {
    basis,
    nodes: nodes.toArray(),
    multiplicity: ops.determinant(basis),
    graph: make(newEdges)
  };
};

export function isConnected(graph) {
  const verts = I.List(adjacencies(graph).keySeq());
  const comp = _componentInCoverGraph(graph, verts.first());

  return comp.nodes.length >= verts.size && comp.multiplicity == 1;
};

export function connectedComponents(graph) {
  const verts = I.List(adjacencies(graph).keySeq());
  const seen = I.Set().asMutable();
  const result = [];

  for (const start of verts) {
    if (!seen.contains(start)) {
      const comp = _componentInCoverGraph(graph, start);
      result.push(comp);
      comp.nodes.forEach(v => seen.add(v));
    }
  }

  return result;
};

export function barycentricPlacement(graph) {
  if (!isConnected(graph))
    throw new Error('must have a connected orbit graph');

  const adj   = adjacencies(graph);
  const verts = I.List(adj.keySeq());
  const vIdcs = I.Map(I.Range(0, verts.size).map(i => [verts.get(i), i]));

  const n = verts.size;
  const d = graph.dim;
  let A = ops.matrix(n+1, n);
  let t = ops.matrix(n+1, d);

  verts.forEach((v, i) => {
    adj.get(v).forEach(c => {
      if (c.v != v) {
        const j = vIdcs.get(c.v);
        A[i][j] -= 1;
        A[i][i] += 1;
        t[i] = ops.plus(t[i], c.s);
      }
    });
  });
  A[n][0] = 1;

  const p = ops.solve(A, t);

  return I.Map(I.Range(0, n).map(i => [verts.get(i), p[i]]));
};

export function barycentricPlacementAsFloat(graph) {
  return barycentricPlacement(graph).map(p => p.map(x => ops.toJS(x)));
};

export function isStable(graph, pos=barycentricPlacement(graph)) {
  const verts = I.List(adjacencies(graph).keySeq());
  const seen = I.Set().asMutable();

  for (const v of verts) {
    const p = pos.get(v);
    const key = I.fromJS(ops.repr(p.map(x => ops.mod(x, 1))));
    if (seen.contains(key))
      return false;
    else
      seen.add(key);
  }

  return true;
};

export function isLocallyStable(graph, pos=barycentricPlacement(graph)) {
  const adj = adjacencies(graph);
  const verts = I.List(adj.keySeq());

  for (const v of verts) {
    const seen = I.Set().asMutable();

    for (const w of adj.get(v)) {
      const p = ops.plus(pos.get(w.v), w.s);
      const key = I.fromJS(ops.repr(p));
      if (seen.contains(key))
        return false;
      else
        seen.add(key);
    }
  }

  return true;
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const test = function test(g) {
    console.log('g = '+g);
    console.log('  cs  = '+coordinationSeq(g, 1, 10));
    if (isConnected(g)) {
      console.log('  pos = '+barycentricPlacement(g));
      console.log('      = '+barycentricPlacementAsFloat(g));
      console.log('  stable: '+isStable(g));
      console.log('  locally stable: '+isLocallyStable(g));
    }

    for (const comp of connectedComponents(g)) {
      console.log('  component:');
      console.log('    nodes = '+comp.nodes);
      console.log('    graph = '+comp.graph);
      console.log('    basis = '+comp.basis);
      console.log('    multiplicity = '+comp.multiplicity);
    }
    console.log();
  };

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ 1, 0 ] ],
              [ 1, 1, [ 0, 1 ] ],
              [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 1 ] ],
              [ 1, 3, [ 0, 0 ] ],
              [ 1, 3, [ 1, -1 ] ],
              [ 1, 4, [ 0, 0 ] ],
              [ 1, 4, [ 1, -1 ] ] ]));

  test(make([ [ 1, 1, [ -1,  1,  1 ] ],
              [ 1, 1, [  0, -1,  1 ] ],
              [ 1, 1, [  0,  0, -1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0 ] ],
              [ 1, 2, [ 1, 0 ] ],
              [ 1, 2, [ 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 1, 0, 0 ] ],
              [ 1, 2, [ 0, 1, 0 ] ],
              [ 1, 2, [ 0, 0, 1 ] ] ]));

  test(make([ [ 1, 2, [ 0, 0, 0 ] ],
              [ 1, 2, [ 2, 0, 0 ] ],
              [ 1, 2, [ 0, 2, 0 ] ],
              [ 1, 2, [ 0, 0, 2 ] ] ]));

  test(make([ [ 1, 3, [ 0, 0, 0 ] ],
              [ 1, 3, [ 2, 0, 0 ] ],
              [ 1, 3, [ 0, 2, 0 ] ],
              [ 1, 3, [ 0, 0, 2 ] ],
              [ 2, 4, [ 0, 0, 0 ] ],
              [ 2, 4, [ 2, 0, 0 ] ],
              [ 2, 4, [ 0, 2, 0 ] ],
              [ 2, 4, [ 0, 0, 2 ] ] ]));
}
<|endoftext|>


<commit_before>/* global AFRAME */
AFRAME.registerComponent('physics-collider', {
  schema: {
    ignoreSleep: {default: true}
  },
  init: function () {
    this.collisions = new Set()
    this.currentCollisions = new Set()
    this.newCollisions = []
    this.clearedCollisions = []
    this.collisionEventDetails = {
      els: this.newCollisions,
      clearedEls: this.clearedCollisions
    }
  },
  update: function () {
    if (this.el.body) {
      this.updateBody()
    } else {
      this.el.addEventListener(
        'body-loaded',
        this.updateBody.bind(this),
        { once: true }
      )
    }
  },
  tick: (function () {
    const uppperMask = 0xFFFF0000
    const lowerMask = 0x0000FFFF
    return function () {
      if (!this.el.body) return
      const currentCollisions = this.currentCollisions
      const thisBodyId = this.el.body.id
      const worldCollisions = this.el.body.world.bodyOverlapKeeper.current
      const worldBodyMap = this.el.body.world.idToBodyMap
      const collisions = this.collisions
      const newCollisions = this.newCollisions
      const clearedCollisions = this.clearedCollisions
      let i = 0
      let upperId = (worldCollisions[i] & uppperMask) >> 16
      let target
      newCollisions.length = clearedCollisions.length = 0
      currentCollisions.clear()
      while (i < worldCollisions.length && upperId < thisBodyId) {
        target = worldBodyMap[upperId].el
        if ((worldCollisions[i] & lowerMask) === thisBodyId) {
          currentCollisions.add(target)
          if (!collisions.has(target)) { newCollisions.push(target) }
        }
        upperId = (worldCollisions[++i] & uppperMask) >> 16
      }
      while (i < worldCollisions.length && upperId === thisBodyId) {
        target = worldBodyMap[worldCollisions[i] & lowerMask].el
        currentCollisions.add(target)
        if (!collisions.has(target)) { newCollisions.push(target) }
        upperId = (worldCollisions[++i] & uppperMask) >> 16
      }

      for (let col of collisions) {
        if (!currentCollisions.has(col)) {
          clearedCollisions.push(col)
          collisions.delete(col)
        }
      }
      for (let col of newCollisions) {
        collisions.add(col)
      }
      if (newCollisions.length || clearedCollisions.length) {
        this.el.emit('collisions', this.collisionEventDetails)
      }
    }
  })(),
  remove: function () {
    if (this.originalSleepConfig) {
      AFRAME.utils.extend(this.el.body, this.originalSleepConfig)
    }
  },
  updateBody: function (evt) {
    // ignore bubbled 'body-loaded' events
    if (evt !== undefined && evt.target !== this.el) { return }
    if (this.data.ignoreSleep) {
      // ensure sleep doesn't disable collision detection
      this.el.body.allowSleep = false
      /* naiveBroadphase ignores collisions between sleeping & static bodies */
      this.el.body.type = window.CANNON.Body.KINEMATIC
      // Kinematics must have velocity >= their sleep limit to wake others
      this.el.body.sleepSpeedLimit = 0
    } else if (this.originalSleepConfig === undefined) {
      this.originalSleepConfig = {
        allowSleep: this.el.body.allowSleep,
        sleepSpeedLimit: this.el.body.sleepSpeedLimit,
        type: this.el.body.type
      }
    } else {
      // restore original settings
      AFRAME.utils.extend(this.el.body, this.originalSleepConfig)
    }
  }
})
<commit_msg>fix a crash that can occur if a physics-collider is colliding with an entity (e.g. dynamic-body) that is removed from the scene<commit_after>/* global AFRAME */
AFRAME.registerComponent('physics-collider', {
  schema: {
    ignoreSleep: {default: true}
  },
  init: function () {
    this.collisions = new Set()
    this.currentCollisions = new Set()
    this.newCollisions = []
    this.clearedCollisions = []
    this.collisionEventDetails = {
      els: this.newCollisions,
      clearedEls: this.clearedCollisions
    }
  },
  update: function () {
    if (this.el.body) {
      this.updateBody()
    } else {
      this.el.addEventListener(
        'body-loaded',
        this.updateBody.bind(this),
        { once: true }
      )
    }
  },
  tick: (function () {
    const uppperMask = 0xFFFF0000
    const lowerMask = 0x0000FFFF
    return function () {
      if (!this.el.body) return
      const currentCollisions = this.currentCollisions
      const thisBodyId = this.el.body.id
      const worldCollisions = this.el.body.world.bodyOverlapKeeper.current
      const worldBodyMap = this.el.body.world.idToBodyMap
      const collisions = this.collisions
      const newCollisions = this.newCollisions
      const clearedCollisions = this.clearedCollisions
      let i = 0
      let upperId = (worldCollisions[i] & uppperMask) >> 16
      let target
      newCollisions.length = clearedCollisions.length = 0
      currentCollisions.clear()
      while (i < worldCollisions.length && upperId < thisBodyId) {
        target = worldBodyMap[upperId].el
        if ((worldCollisions[i] & lowerMask) === thisBodyId) {
          currentCollisions.add(target)
          if (!collisions.has(target)) { newCollisions.push(target) }
        }
        upperId = (worldCollisions[++i] & uppperMask) >> 16
      }
      while (i < worldCollisions.length && upperId === thisBodyId
        && worldBodyMap[worldCollisions[i] & lowerMask]) {
        target = worldBodyMap[worldCollisions[i] & lowerMask].el
        currentCollisions.add(target)
        if (!collisions.has(target)) { newCollisions.push(target) }
        upperId = (worldCollisions[++i] & uppperMask) >> 16
      }

      for (let col of collisions) {
        if (!currentCollisions.has(col)) {
          clearedCollisions.push(col)
          collisions.delete(col)
        }
      }
      for (let col of newCollisions) {
        collisions.add(col)
      }
      if (newCollisions.length || clearedCollisions.length) {
        this.el.emit('collisions', this.collisionEventDetails)
      }
    }
  })(),
  remove: function () {
    if (this.originalSleepConfig) {
      AFRAME.utils.extend(this.el.body, this.originalSleepConfig)
    }
  },
  updateBody: function (evt) {
    // ignore bubbled 'body-loaded' events
    if (evt !== undefined && evt.target !== this.el) { return }
    if (this.data.ignoreSleep) {
      // ensure sleep doesn't disable collision detection
      this.el.body.allowSleep = false
      /* naiveBroadphase ignores collisions between sleeping & static bodies */
      this.el.body.type = window.CANNON.Body.KINEMATIC
      // Kinematics must have velocity >= their sleep limit to wake others
      this.el.body.sleepSpeedLimit = 0
    } else if (this.originalSleepConfig === undefined) {
      this.originalSleepConfig = {
        allowSleep: this.el.body.allowSleep,
        sleepSpeedLimit: this.el.body.sleepSpeedLimit,
        type: this.el.body.type
      }
    } else {
      // restore original settings
      AFRAME.utils.extend(this.el.body, this.originalSleepConfig)
    }
  }
})
<|endoftext|>


<commit_before>Crafty.c('NavGauge', {

  init: function() {
    this.requires('2D, DOM')
      .attr({
        h: 5,
        alpha: 0.80
      })
      .css('border-radius','2px');

  },

  /**
   * Specify width in pixels for every 1 unit value
   * @param  {Number} width - width per value
   * @return {NavGauge}
   */
  unitWidth: function(width) {
    this._unitWidth = width;
    return this;
  },

  /**
   * Specify the gauge value and apply this to the width
   * @param  {Number} val - the value
   * @return {NavGauge}
   */
  value: function(val) {
    this._value = val;
    this.w = val * this._unitWidth;
    return this;
  },

  /**
   * Set the color of the gauge
   * @param  {String} color - the css color value
   * @return {NavGauge}
   */
  color: function(color) {
    this.css('background-color',color);
    return this;
  }

});<commit_msg>added name method to navgauge<commit_after>Crafty.c('NavGauge', {

  init: function() {
    this.requires('2D, DOM')
      .attr({
        h: 5,
        alpha: 0.80
      })
      .css('border-radius','2px');

  },

  /**
   * Specify the name of the gauge
   * @param  {String} gaugeName
   * @return {NavGauge}
   */
  name: function(gaugeName) {
    this._name = gaugeName;
    return this;
  },

  /**
   * Specify width in pixels for every 1 unit value
   * @param  {Number} width - width per value
   * @return {NavGauge}
   */
  unitWidth: function(width) {
    this._unitWidth = width;
    return this;
  },

  /**
   * Specify the gauge value and apply this to the width
   * @param  {Number} val - the value
   * @return {NavGauge}
   */
  value: function(val) {
    this._value = val;
    this.w = val * this._unitWidth;
    return this;
  },

  /**
   * Set the color of the gauge
   * @param  {String} color - the css color value
   * @return {NavGauge}
   */
  color: function(color) {
    this.css('background-color',color);
    return this;
  }

});<|endoftext|>


<commit_before>import test from 'tape'
import MiniContext from '../../src/contexts/MiniContext'
import JsContext from '../../src/contexts/JsContext'
import FunctionManager from '../../src/function/FunctionManager'
import { libtestXML, libtest } from './libtest'

function setupContextWithFunctions() {
  // A JsContext with the test function library
  let jsContext = new JsContext()
  jsContext.importLibrary('test', libtest)
  // Function manager for getting function specs
  let functionManager = new FunctionManager()
  functionManager.importLibrary('test', libtestXML)
  // A mock Host that provides the JsContext when requested
  let host = {
    createContext: function(language) {
      if (language !== 'js') throw new Error('This stub only creates JsContexts')
      return Promise.resolve(jsContext)
    },
    functionManager
  }
  return new MiniContext(host)
}

test('MiniContext: analyseCode(x=5)', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.analyseCode('x=5').then((res) => {
    t.equal(res.output, 'x', 'there should be output variable x')
  })
})

test('MiniContext: analyseCode(foo(x,y,z))', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.analyseCode('foo(x,y,z)').then((res) => {
    t.deepEqual(res.inputs.map(s => s.name), ['x','y','z'], 'there should be input variables x,y,z')
  })
})

test('MiniContext: x=5', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.executeCode('x=5').then((res) => {
    t.equal(res.value.data, 5, 'value should be correct')
  })
})

test('MiniContext: 1+2+3', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.executeCode('1+2+3').then((res) => {
    let val = res.value
    t.equal(val.data, 6, 'value should be correct')
  })
})

test('MiniContext: no_params()', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params()').then((res) => {
    let val = res.value
    t.equal(val.type, 'integer', 'type should be correct')
    t.equal(val.data, 5, 'result should be correct')
    t.end()
  })
})

test('MiniContext: no_params() + 1', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params() + 1').then((res) => {
    let val = res.value
    t.equal(val.type, 'integer', 'type should be correct')
    t.equal(val.data, 6, 'result should be correct')
    t.end()
  })
})

test('MiniContext: no_params(4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params(4)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Too many parameters supplied (1), expected 0 at most', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: no_params(param_foo=4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params(param_foo=4)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), '"param_foo" is not a valid parameter names for function "no_params"', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param(2)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(2)').then((res) => {
    let val = res.value
    t.equal(val.type, 'number', 'type should be correct')
    t.equal(val.data, 2.2, 'result should be correct')
    t.end()
  })
})

test('MiniContext: one_param()', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param()').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Required parameter "param1" was not supplied', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param(1, 2, 3)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(1, 2, 3)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Too many parameters supplied (3), expected 1 at most', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param(param1=4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(param1=4)').then((res) => {
    t.equal(res.value.data, 4.4, 'result should be correct')
    t.end()
  })
})

test('MiniContext: one_param(param_foo=4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(param_foo=4)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), '"param_foo" is not a valid parameter names for function "one_param"', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param("wrong type")', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param("wrong type")').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Parameter "param1" must be of type "number"', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param_with_default("Howdy!")', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param_with_default("Howdy!")').then((res) => {
    t.equal(res.value.data, "Howdy!")
    t.end()
  })
})

test('MiniContext: one_param_with_default()', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param_with_default()').then((res) => {
    t.equal(res.value.data, "Hello!")
    t.end()
  })
})

function _hasError(res) {
  return res.messages && res.messages.length > 0
}

function _getMessage(res) {
  let error = res.messages[0]
  return error ? error.message : undefined
}
<commit_msg>Fix test.<commit_after>import test from 'tape'
import MiniContext from '../../src/contexts/MiniContext'
import JsContext from '../../src/contexts/JsContext'
import FunctionManager from '../../src/function/FunctionManager'
import { libtestXML, libtest } from './libtest'

function setupContextWithFunctions() {
  // A JsContext with the test function library
  let jsContext = new JsContext()
  jsContext.importLibrary('test', libtest)
  // Function manager for getting function specs
  let functionManager = new FunctionManager()
  functionManager.importLibrary('test', libtestXML)
  // A mock Host that provides the JsContext when requested
  let host = {
    createContext: function(language) {
      if (language !== 'js') throw new Error('This stub only creates JsContexts')
      return Promise.resolve(jsContext)
    },
    functionManager
  }
  return new MiniContext(host)
}

test('MiniContext: analyseCode(x=5)', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.analyseCode('x=5').then((res) => {
    t.equal(res.output, 'x', 'there should be output variable x')
  })
})

test('MiniContext: analyseCode(foo(x,y,z))', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.analyseCode('foo(x,y,z)').then((res) => {
    t.deepEqual(res.inputs.map(s => s.name), ['x','y','z'], 'there should be input variables x,y,z')
  })
})

test('MiniContext: x=5', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.executeCode('x=5').then((res) => {
    t.equal(res.value.data, 5, 'value should be correct')
  })
})

test('MiniContext: 1+2+3', t => {
  let c = setupContextWithFunctions()
  t.plan(1)
  c.executeCode('1+2+3').then((res) => {
    let val = res.value
    t.equal(val.data, 6, 'value should be correct')
  })
})

test('MiniContext: no_params()', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params()').then((res) => {
    let val = res.value
    t.equal(val.type, 'integer', 'type should be correct')
    t.equal(val.data, 5, 'result should be correct')
    t.end()
  })
})

test('MiniContext: no_params() + 1', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params() + 1').then((res) => {
    let val = res.value
    t.equal(val.type, 'integer', 'type should be correct')
    t.equal(val.data, 6, 'result should be correct')
    t.end()
  })
})

test('MiniContext: no_params(4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params(4)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Too many parameters supplied (1), expected 0 at most', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: no_params(param_foo=4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('no_params(param_foo=4)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), '"param_foo" is not a valid parameter names for function "no_params"', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param(2)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(2)').then((res) => {
    let val = res.value
    t.equal(val.type, 'number', 'type should be correct')
    t.equal(val.data, 2.2, 'result should be correct')
    t.end()
  })
})

test('MiniContext: one_param()', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param()').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Required parameter "param1" was not supplied', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param(1, 2, 3)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(1, 2, 3)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Too many parameters supplied (3), expected 1 at most', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param(param1=4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(param1=4)').then((res) => {
    t.equal(res.value.data, 4.4, 'result should be correct')
    t.end()
  })
})

test('MiniContext: one_param(param_foo=4)', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param(param_foo=4)').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), '"param_foo" is not a valid parameter names for function "one_param"', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param("wrong type")', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param("wrong type")').then((res) => {
    t.ok(_hasError(res), 'should error')
    t.equal(_getMessage(res), 'Parameter "param1" must be of type "number" but was of type "string"', 'error message should be correct')
    t.end()
  })
})

test('MiniContext: one_param_with_default("Howdy!")', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param_with_default("Howdy!")').then((res) => {
    t.equal(res.value.data, "Howdy!")
    t.end()
  })
})

test('MiniContext: one_param_with_default()', t => {
  let c = setupContextWithFunctions()
  c.executeCode('one_param_with_default()').then((res) => {
    t.equal(res.value.data, "Hello!")
    t.end()
  })
})

function _hasError(res) {
  return res.messages && res.messages.length > 0
}

function _getMessage(res) {
  let error = res.messages[0]
  return error ? error.message : undefined
}
<|endoftext|>


<commit_before>const path = require('path');
const flow = require('rollup-plugin-flow-no-whitespace');
const buble = require('rollup-plugin-buble');
const resolve = require('rollup-plugin-node-resolve');
const replace = require('rollup-plugin-replace');
const filesize = require('rollup-plugin-filesize');
const progress = require('rollup-plugin-progress');
const uglify = require('rollup-plugin-uglify');
const commonjs = require('rollup-plugin-commonjs');
const vue = require('rollup-plugin-vue');
const package = require('../package.json');
const version = process.env.VERSION || package.version;
const author = package.author;
const license = package.license;
const moduleName = package.name;
const dependencies = package.dependencies || [];

const banner =
`/*
 * ${moduleName} v${version}
 * (c) ${new Date().getFullYear()} ${author.name}(${author.email})
 * Released under the ${license} License.
 */
`;
const external = Object.keys(dependencies);
const builds = {
  "full-umd": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.js`),
    format: 'umd',
    moduleName,
    banner
  },
  "prod-umd": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.min.js`),
    format: 'umd',
    sourceMap: true,
    plugins: [
      uglify()
    ],
    moduleName,
    banner
  },
  "esm": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.esm.js`),
    format: 'es',
    moduleName,
    banner
  },
  "cjs": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.common.js`),
    format: 'cjs',
    moduleName,
    banner
  }
};

function genConfig(opts) {
  const config = {
    entry: opts.entry,
    dest: opts.dest,
    external: external.concat(opts.external),
    globals: {
      'lodash': '_'
    },
    format: opts.format,
    banner: opts.banner,
    moduleName: opts.moduleName,
    plugins: [
      replace({
        __VERSION__: version
      }),
      flow(),
      resolve(),
      commonjs(),
      vue({
        css: true
      }),
      buble(),
      progress(),
      filesize()
    ].concat(opts.plugins || [])
  };
  return config;
}

exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name]));
<commit_msg>export component css as a simple file<commit_after>const path = require('path');
const flow = require('rollup-plugin-flow-no-whitespace');
const buble = require('rollup-plugin-buble');
const resolve = require('rollup-plugin-node-resolve');
const replace = require('rollup-plugin-replace');
const filesize = require('rollup-plugin-filesize');
const progress = require('rollup-plugin-progress');
const uglify = require('rollup-plugin-uglify');
const commonjs = require('rollup-plugin-commonjs');
const vue = require('rollup-plugin-vue');
const package = require('../package.json');
const version = process.env.VERSION || package.version;
const author = package.author;
const license = package.license;
const moduleName = package.name;
const dependencies = package.dependencies || [];

const banner =
`/*
 * ${moduleName} v${version}
 * (c) ${new Date().getFullYear()} ${author.name}(${author.email})
 * Released under the ${license} License.
 */
`;
const external = Object.keys(dependencies);
const builds = {
  "full-umd": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.js`),
    format: 'umd',
    moduleName,
    banner
  },
  "prod-umd": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.min.js`),
    format: 'umd',
    sourceMap: true,
    plugins: [
      uglify()
    ],
    moduleName,
    banner
  },
  "esm": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.esm.js`),
    format: 'es',
    moduleName,
    banner
  },
  "cjs": {
    entry: path.resolve(__dirname, '../src/index.js'),
    dest: path.resolve(__dirname, `../dist/${moduleName}.common.js`),
    format: 'cjs',
    moduleName,
    banner
  }
};

function genConfig(opts) {
  const config = {
    entry: opts.entry,
    dest: opts.dest,
    external: external.concat(opts.external),
    globals: {
      'lodash': '_'
    },
    format: opts.format,
    banner: opts.banner,
    moduleName: opts.moduleName,
    plugins: [
      replace({
        __VERSION__: version
      }),
      flow(),
      resolve(),
      commonjs(),
      vue({
        css: `dist/${moduleName}.css`
      }),
      buble(),
      progress(),
      filesize()
    ].concat(opts.plugins || [])
  };
  return config;
}

exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name]));
<|endoftext|>


<commit_before>/**
 * Handsontable UndoRedo plugin class
 *
 * @plugin
 * @class Handsontable.UndoRedo
 */
(function(Handsontable){
  Handsontable.UndoRedo = function (instance) {
    var plugin = this;
    this.instance = instance;
    this.doneActions = [];
    this.undoneActions = [];
    this.ignoreNewActions = false;
    instance.addHook("afterChange", function (changes, origin) {
      if(changes){
        var action = new Handsontable.UndoRedo.ChangeAction(changes);
        plugin.done(action);
      }
    });

    instance.addHook("afterCreateRow", function (index, amount, createdAutomatically) {

      if (createdAutomatically) {
        return;
      }

      var action = new Handsontable.UndoRedo.CreateRowAction(index, amount);
      plugin.done(action);
    });

    instance.addHook("beforeRemoveRow", function (index, amount) {
      var originalData = plugin.instance.getData();
      index = ( originalData.length + index ) % originalData.length;
      var removedData = originalData.slice(index, index + amount);
      var action = new Handsontable.UndoRedo.RemoveRowAction(index, removedData);
      plugin.done(action);
    });

    instance.addHook("afterCreateCol", function (index, amount, createdAutomatically) {

      if (createdAutomatically) {
        return;
      }

      var action = new Handsontable.UndoRedo.CreateColumnAction(index, amount);
      plugin.done(action);
    });

    instance.addHook("beforeRemoveCol", function (index, amount) {
      var originalData = plugin.instance.getData();
      index = ( plugin.instance.countCols() + index ) % plugin.instance.countCols();
      var removedData = [];

      for (var i = 0, len = originalData.length; i < len; i++) {
        removedData[i] = originalData[i].slice(index, index + amount);
      }

      var headers;
      if(Array.isArray(instance.getSettings().colHeaders)){
        headers = instance.getSettings().colHeaders.slice(index, index + removedData.length);
      }

      var action = new Handsontable.UndoRedo.RemoveColumnAction(index, removedData, headers);
      plugin.done(action);
    });

    instance.addHook("beforeCellAlignment", function (stateBefore, range, type, alignment) {
      var action = new Handsontable.UndoRedo.CellAlignmentAction(stateBefore, range, type, alignment);
      plugin.done(action);
    });
  };

  Handsontable.UndoRedo.prototype.done = function (action) {
    if (!this.ignoreNewActions) {
      this.doneActions.push(action);
      this.undoneActions.length = 0;
    }
  };

  /**
   * Undo last edit.
   */
  Handsontable.UndoRedo.prototype.undo = function () {
    if (this.isUndoAvailable()) {
      var action = this.doneActions.pop(),
          result = Handsontable.hooks.run(this.instance, "beforeUndo", action.dup());

      if (result === false) {
        return;
      }

      this.ignoreNewActions = true;
      var that = this;
      action.undo(this.instance, function () {
        that.ignoreNewActions = false;
        that.undoneActions.push(action);
      });

      Handsontable.hooks.run(this.instance, "afterChange", 'undo', action.dup());
    }
  };

  /**
   * Redo edit (used to reverse an undo).
   */
  Handsontable.UndoRedo.prototype.redo = function () {
    if (this.isRedoAvailable()) {
      var action = this.undoneActions.pop(),
          result = Handsontable.hooks.run(this.instance, "beforeRedo", action.dup());

      if (result === false) {
        return;
      }

      this.ignoreNewActions = true;
      var that = this;
      action.redo(this.instance, function () {
        that.ignoreNewActions = false;
        that.doneActions.push(action);
      });

      Handsontable.hooks.run(this.instance, "afterChange", 'redo', action.dup());
    }
  };

  /**
   * Check if undo action is available.
   *
   * @return {Boolean} Return `true` if undo can be performed, `false` otherwise
   */
  Handsontable.UndoRedo.prototype.isUndoAvailable = function () {
    return this.doneActions.length > 0;
  };

  /**
   * Check if redo action is available.
   *
   * @return {Boolean} Return `true` if redo can be performed, `false` otherwise.
   */
  Handsontable.UndoRedo.prototype.isRedoAvailable = function () {
    return this.undoneActions.length > 0;
  };

  /**
   * Clears undo history.
   */
  Handsontable.UndoRedo.prototype.clear = function () {
    this.doneActions.length = 0;
    this.undoneActions.length = 0;
  };

  Handsontable.UndoRedo.Action = function () {
  };
  Handsontable.UndoRedo.Action.prototype.undo = function () {
  };
  Handsontable.UndoRedo.Action.prototype.redo = function () {
  };
  Handsontable.UndoRedo.Action.prototype.dup = function () {
    return JSON.parse(JSON.stringify(this));
  };

  Handsontable.UndoRedo.ChangeAction = function (changes) {
    this.changes = changes;
    this.type = "change";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.ChangeAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.ChangeAction.prototype.undo = function (instance, undoneCallback) {
    var data = Handsontable.helper.deepClone(this.changes),
        emptyRowsAtTheEnd = instance.countEmptyRows(true),
        emptyColsAtTheEnd = instance.countEmptyCols(true);

    for (var i = 0, len = data.length; i < len; i++) {
      data[i].splice(3, 1);
    }

    instance.addHookOnce('afterChange', undoneCallback);

    instance.setDataAtRowProp(data, null, null, 'undo');

    for (var i = 0, len = data.length; i < len; i++) {
     if (instance.getSettings().minSpareRows &&
        data[i][0] + 1 + instance.getSettings().minSpareRows === instance.countRows() &&
        emptyRowsAtTheEnd == instance.getSettings().minSpareRows) {

       instance.alter('remove_row', parseInt(data[i][0]+1,10), instance.getSettings().minSpareRows);
       instance.undoRedo.doneActions.pop();

     }

     if (instance.getSettings().minSpareCols &&
        data[i][1] + 1 + instance.getSettings().minSpareCols === instance.countCols() &&
        emptyColsAtTheEnd == instance.getSettings().minSpareCols) {

        instance.alter('remove_col', parseInt(data[i][1]+1,10), instance.getSettings().minSpareCols);
        instance.undoRedo.doneActions.pop();
      }
    }

  };
  Handsontable.UndoRedo.ChangeAction.prototype.redo = function (instance, onFinishCallback) {
    var data = Handsontable.helper.deepClone(this.changes);

    for (var i = 0, len = data.length; i < len; i++) {
      data[i].splice(2, 1);
    }

    instance.addHookOnce('afterChange', onFinishCallback);

    instance.setDataAtRowProp(data, null, null, 'redo');

  };

  Handsontable.UndoRedo.CreateRowAction = function (index, amount) {
    this.index = index;
    this.amount = amount;
    this.type = "create_row";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.CreateRowAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.CreateRowAction.prototype.undo = function (instance, undoneCallback) {
    var rowCount = instance.countRows(),
      minSpareRows = instance.getSettings().minSpareRows;
    if(this.index >= rowCount && this.index - minSpareRows < rowCount) {
      this.index -= minSpareRows; // work around the situation where the needed row was removed due to an 'undo' of a made change
    }

    instance.addHookOnce('afterRemoveRow', undoneCallback);
    instance.alter('remove_row', this.index, this.amount);
  };
  Handsontable.UndoRedo.CreateRowAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterCreateRow', redoneCallback);
    instance.alter('insert_row', this.index + 1, this.amount);
  };

  Handsontable.UndoRedo.RemoveRowAction = function (index, data) {
    this.index = index;
    this.data = data;
    this.type = "remove_row";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.RemoveRowAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.RemoveRowAction.prototype.undo = function (instance, undoneCallback) {
    var spliceArgs = [this.index, 0];
    Array.prototype.push.apply(spliceArgs, this.data);

    Array.prototype.splice.apply(instance.getData(), spliceArgs);

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };
  Handsontable.UndoRedo.RemoveRowAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterRemoveRow', redoneCallback);
    instance.alter('remove_row', this.index, this.data.length);
  };

  Handsontable.UndoRedo.CreateColumnAction = function (index, amount) {
    this.index = index;
    this.amount = amount;
    this.type = "create_col";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.CreateColumnAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.CreateColumnAction.prototype.undo = function (instance, undoneCallback) {
    instance.addHookOnce('afterRemoveCol', undoneCallback);
    instance.alter('remove_col', this.index, this.amount);
  };
  Handsontable.UndoRedo.CreateColumnAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterCreateCol', redoneCallback);
    instance.alter('insert_col', this.index + 1, this.amount);
  };

  Handsontable.UndoRedo.CellAlignmentAction = function (stateBefore, range, type, alignment) {
    this.stateBefore = stateBefore;
    this.range = range;
    this.type = type;
    this.alignment = alignment;
  };
  Handsontable.UndoRedo.CellAlignmentAction.prototype.undo = function(instance, undoneCallback) {
    if (!instance.contextMenu) {
      return;
    }

    for (var row = this.range.from.row; row <= this.range.to.row; row++) {
      for (var col = this.range.from.col; col <= this.range.to.col; col++) {
        instance.setCellMeta(row, col, 'className', this.stateBefore[row][col] || ' htLeft');
      }
    }

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };
  Handsontable.UndoRedo.CellAlignmentAction.prototype.redo = function(instance, undoneCallback) {
    if (!instance.contextMenu) {
      return;
    }

    for (var row = this.range.from.row; row <= this.range.to.row; row++) {
      for (var col = this.range.from.col; col <= this.range.to.col; col++) {
        instance.contextMenu.align.call(instance, this.range, this.type, this.alignment);
      }
    }

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };

  Handsontable.UndoRedo.RemoveColumnAction = function (index, data, headers) {
    this.index = index;
    this.data = data;
    this.amount = this.data[0].length;
    this.headers = headers;
    this.type = "remove_col";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.RemoveColumnAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.RemoveColumnAction.prototype.undo = function (instance, undoneCallback) {
    var row, spliceArgs;
    for (var i = 0, len = instance.getData().length; i < len; i++) {
      row = instance.getSourceDataAtRow(i);

      spliceArgs = [this.index, 0];
      Array.prototype.push.apply(spliceArgs, this.data[i]);

      Array.prototype.splice.apply(row, spliceArgs);

    }

    if(typeof this.headers != 'undefined'){
      spliceArgs = [this.index, 0];
      Array.prototype.push.apply(spliceArgs, this.headers);
      Array.prototype.splice.apply(instance.getSettings().colHeaders, spliceArgs);
    }

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };
  Handsontable.UndoRedo.RemoveColumnAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterRemoveCol', redoneCallback);
    instance.alter('remove_col', this.index, this.amount);
  };

  // register hooks
  Handsontable.hooks.register("beforeUndo");
  Handsontable.hooks.register("beforeRedo");
  Handsontable.hooks.register("afterUndo");
  Handsontable.hooks.register("afterRedo");
})(Handsontable);

(function(Handsontable){

  function init(){
    var instance = this;
    var pluginEnabled = typeof instance.getSettings().undo == 'undefined' || instance.getSettings().undo;

    if(pluginEnabled){
      if(!instance.undoRedo){
        /**
         * Instance of Handsontable.UndoRedo Plugin {@link Handsontable.UndoRedo}
         *
         * @alias undoRedo
         * @memberof! Handsontable.Core#
         * @type {UndoRedo}
         */
        instance.undoRedo = new Handsontable.UndoRedo(instance);

        exposeUndoRedoMethods(instance);

        instance.addHook('beforeKeyDown', onBeforeKeyDown);
        instance.addHook('afterChange', onAfterChange);
      }
    } else {
      if(instance.undoRedo){
        delete instance.undoRedo;

        removeExposedUndoRedoMethods(instance);

        instance.removeHook('beforeKeyDown', onBeforeKeyDown);
        instance.removeHook('afterChange', onAfterChange);
      }
    }
  }

  function onBeforeKeyDown(event){
    var instance = this;

    var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey;

    if(ctrlDown){
      if (event.keyCode === 89 || (event.shiftKey && event.keyCode === 90)) { //CTRL + Y or CTRL + SHIFT + Z
        instance.undoRedo.redo();
        event.stopImmediatePropagation();
      }
      else if (event.keyCode === 90) { //CTRL + Z
        instance.undoRedo.undo();
        event.stopImmediatePropagation();
      }
    }
  }

  function onAfterChange(changes, source){
    var instance = this;
    if (source == 'loadData'){
      return instance.undoRedo.clear();
    }
  }

  function exposeUndoRedoMethods(instance) {
    /**
     * {@link Handsontable.UndoRedo#undo}
     * @alias undo
     * @memberof! Handsontable.Core#
     */
    instance.undo = function(){
      return instance.undoRedo.undo();
    };

    /**
     * {@link Handsontable.UndoRedo#redo}
     * @alias redo
     * @memberof! Handsontable.Core#
     */
    instance.redo = function(){
      return instance.undoRedo.redo();
    };

    /**
     * {@link Handsontable.UndoRedo#isUndoAvailable}
     * @alias isUndoAvailable
     * @memberof! Handsontable.Core#
     */
    instance.isUndoAvailable = function(){
      return instance.undoRedo.isUndoAvailable();
    };

    /**
     * {@link Handsontable.UndoRedo#isRedoAvailable}
     * @alias isRedoAvailable
     * @memberof! Handsontable.Core#
     */
    instance.isRedoAvailable = function(){
      return instance.undoRedo.isRedoAvailable();
    };

    /**
     * {@link Handsontable.UndoRedo#clear}
     * @alias clearUndo
     * @memberof! Handsontable.Core#
     */
    instance.clearUndo = function(){
      return instance.undoRedo.clear();
    };
  }

  function removeExposedUndoRedoMethods(instance){
    delete instance.undo;
    delete instance.redo;
    delete instance.isUndoAvailable;
    delete instance.isRedoAvailable;
    delete instance.clearUndo;
  }

  Handsontable.hooks.add('afterInit', init);
  Handsontable.hooks.add('afterUpdateSettings', init);

})(Handsontable);
<commit_msg>change order of stuff<commit_after>/**
 * Handsontable UndoRedo plugin class
 *
 * @plugin
 * @class Handsontable.UndoRedo
 */
(function(Handsontable){
  Handsontable.UndoRedo = function (instance) {
    var plugin = this;
    this.instance = instance;
    this.doneActions = [];
    this.undoneActions = [];
    this.ignoreNewActions = false;
    instance.addHook("afterChange", function (changes, origin) {
      if(changes){
        var action = new Handsontable.UndoRedo.ChangeAction(changes);
        plugin.done(action);
      }
    });

    instance.addHook("afterCreateRow", function (index, amount, createdAutomatically) {

      if (createdAutomatically) {
        return;
      }

      var action = new Handsontable.UndoRedo.CreateRowAction(index, amount);
      plugin.done(action);
    });

    instance.addHook("beforeRemoveRow", function (index, amount) {
      var originalData = plugin.instance.getData();
      index = ( originalData.length + index ) % originalData.length;
      var removedData = originalData.slice(index, index + amount);
      var action = new Handsontable.UndoRedo.RemoveRowAction(index, removedData);
      plugin.done(action);
    });

    instance.addHook("afterCreateCol", function (index, amount, createdAutomatically) {

      if (createdAutomatically) {
        return;
      }

      var action = new Handsontable.UndoRedo.CreateColumnAction(index, amount);
      plugin.done(action);
    });

    instance.addHook("beforeRemoveCol", function (index, amount) {
      var originalData = plugin.instance.getData();
      index = ( plugin.instance.countCols() + index ) % plugin.instance.countCols();
      var removedData = [];

      for (var i = 0, len = originalData.length; i < len; i++) {
        removedData[i] = originalData[i].slice(index, index + amount);
      }

      var headers;
      if(Array.isArray(instance.getSettings().colHeaders)){
        headers = instance.getSettings().colHeaders.slice(index, index + removedData.length);
      }

      var action = new Handsontable.UndoRedo.RemoveColumnAction(index, removedData, headers);
      plugin.done(action);
    });

    instance.addHook("beforeCellAlignment", function (stateBefore, range, type, alignment) {
      var action = new Handsontable.UndoRedo.CellAlignmentAction(stateBefore, range, type, alignment);
      plugin.done(action);
    });
  };

  Handsontable.UndoRedo.prototype.done = function (action) {
    if (!this.ignoreNewActions) {
      this.doneActions.push(action);
      this.undoneActions.length = 0;
    }
  };

  /**
   * Undo last edit.
   */
  Handsontable.UndoRedo.prototype.undo = function () {
    if (this.isUndoAvailable()) {
      var action = this.doneActions.pop(),
          result = Handsontable.hooks.run(this.instance, "beforeUndo", action.dup());

      if (result === false) {
        return;
      }

      this.ignoreNewActions = true;
      var that = this;
      action.undo(this.instance, function () {
        that.ignoreNewActions = false;
        that.undoneActions.push(action);
      });

      Handsontable.hooks.run(this.instance, "afterChange", 'undo', action.dup());
    }
  };

  /**
   * Redo edit (used to reverse an undo).
   */
  Handsontable.UndoRedo.prototype.redo = function () {
    if (this.isRedoAvailable()) {
      var action = this.undoneActions.pop(),
          result = Handsontable.hooks.run(this.instance, action.dup(), "beforeRedo");

      if (result === false) {
        return;
      }

      this.ignoreNewActions = true;
      var that = this;
      action.redo(this.instance, function () {
        that.ignoreNewActions = false;
        that.doneActions.push(action);
      });

      Handsontable.hooks.run(this.instance, "afterChange", action.dup().data, 'redo');
    }
  };

  /**
   * Check if undo action is available.
   *
   * @return {Boolean} Return `true` if undo can be performed, `false` otherwise
   */
  Handsontable.UndoRedo.prototype.isUndoAvailable = function () {
    return this.doneActions.length > 0;
  };

  /**
   * Check if redo action is available.
   *
   * @return {Boolean} Return `true` if redo can be performed, `false` otherwise.
   */
  Handsontable.UndoRedo.prototype.isRedoAvailable = function () {
    return this.undoneActions.length > 0;
  };

  /**
   * Clears undo history.
   */
  Handsontable.UndoRedo.prototype.clear = function () {
    this.doneActions.length = 0;
    this.undoneActions.length = 0;
  };

  Handsontable.UndoRedo.Action = function () {
  };
  Handsontable.UndoRedo.Action.prototype.undo = function () {
  };
  Handsontable.UndoRedo.Action.prototype.redo = function () {
  };
  Handsontable.UndoRedo.Action.prototype.dup = function () {
    return JSON.parse(JSON.stringify(this));
  };

  Handsontable.UndoRedo.ChangeAction = function (changes) {
    this.changes = changes;
    this.type = "change";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.ChangeAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.ChangeAction.prototype.undo = function (instance, undoneCallback) {
    var data = Handsontable.helper.deepClone(this.changes),
        emptyRowsAtTheEnd = instance.countEmptyRows(true),
        emptyColsAtTheEnd = instance.countEmptyCols(true);

    for (var i = 0, len = data.length; i < len; i++) {
      data[i].splice(3, 1);
    }

    instance.addHookOnce('afterChange', undoneCallback);

    instance.setDataAtRowProp(data, null, null, 'undo');

    for (var i = 0, len = data.length; i < len; i++) {
     if (instance.getSettings().minSpareRows &&
        data[i][0] + 1 + instance.getSettings().minSpareRows === instance.countRows() &&
        emptyRowsAtTheEnd == instance.getSettings().minSpareRows) {

       instance.alter('remove_row', parseInt(data[i][0]+1,10), instance.getSettings().minSpareRows);
       instance.undoRedo.doneActions.pop();

     }

     if (instance.getSettings().minSpareCols &&
        data[i][1] + 1 + instance.getSettings().minSpareCols === instance.countCols() &&
        emptyColsAtTheEnd == instance.getSettings().minSpareCols) {

        instance.alter('remove_col', parseInt(data[i][1]+1,10), instance.getSettings().minSpareCols);
        instance.undoRedo.doneActions.pop();
      }
    }

  };
  Handsontable.UndoRedo.ChangeAction.prototype.redo = function (instance, onFinishCallback) {
    var data = Handsontable.helper.deepClone(this.changes);

    for (var i = 0, len = data.length; i < len; i++) {
      data[i].splice(2, 1);
    }

    instance.addHookOnce('afterChange', onFinishCallback);

    instance.setDataAtRowProp(data, null, null, 'redo');

  };

  Handsontable.UndoRedo.CreateRowAction = function (index, amount) {
    this.index = index;
    this.amount = amount;
    this.type = "create_row";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.CreateRowAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.CreateRowAction.prototype.undo = function (instance, undoneCallback) {
    var rowCount = instance.countRows(),
      minSpareRows = instance.getSettings().minSpareRows;
    if(this.index >= rowCount && this.index - minSpareRows < rowCount) {
      this.index -= minSpareRows; // work around the situation where the needed row was removed due to an 'undo' of a made change
    }

    instance.addHookOnce('afterRemoveRow', undoneCallback);
    instance.alter('remove_row', this.index, this.amount);
  };
  Handsontable.UndoRedo.CreateRowAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterCreateRow', redoneCallback);
    instance.alter('insert_row', this.index + 1, this.amount);
  };

  Handsontable.UndoRedo.RemoveRowAction = function (index, data) {
    this.index = index;
    this.data = data;
    this.type = "remove_row";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.RemoveRowAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.RemoveRowAction.prototype.undo = function (instance, undoneCallback) {
    var spliceArgs = [this.index, 0];
    Array.prototype.push.apply(spliceArgs, this.data);

    Array.prototype.splice.apply(instance.getData(), spliceArgs);

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };
  Handsontable.UndoRedo.RemoveRowAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterRemoveRow', redoneCallback);
    instance.alter('remove_row', this.index, this.data.length);
  };

  Handsontable.UndoRedo.CreateColumnAction = function (index, amount) {
    this.index = index;
    this.amount = amount;
    this.type = "create_col";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.CreateColumnAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.CreateColumnAction.prototype.undo = function (instance, undoneCallback) {
    instance.addHookOnce('afterRemoveCol', undoneCallback);
    instance.alter('remove_col', this.index, this.amount);
  };
  Handsontable.UndoRedo.CreateColumnAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterCreateCol', redoneCallback);
    instance.alter('insert_col', this.index + 1, this.amount);
  };

  Handsontable.UndoRedo.CellAlignmentAction = function (stateBefore, range, type, alignment) {
    this.stateBefore = stateBefore;
    this.range = range;
    this.type = type;
    this.alignment = alignment;
  };
  Handsontable.UndoRedo.CellAlignmentAction.prototype.undo = function(instance, undoneCallback) {
    if (!instance.contextMenu) {
      return;
    }

    for (var row = this.range.from.row; row <= this.range.to.row; row++) {
      for (var col = this.range.from.col; col <= this.range.to.col; col++) {
        instance.setCellMeta(row, col, 'className', this.stateBefore[row][col] || ' htLeft');
      }
    }

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };
  Handsontable.UndoRedo.CellAlignmentAction.prototype.redo = function(instance, undoneCallback) {
    if (!instance.contextMenu) {
      return;
    }

    for (var row = this.range.from.row; row <= this.range.to.row; row++) {
      for (var col = this.range.from.col; col <= this.range.to.col; col++) {
        instance.contextMenu.align.call(instance, this.range, this.type, this.alignment);
      }
    }

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };

  Handsontable.UndoRedo.RemoveColumnAction = function (index, data, headers) {
    this.index = index;
    this.data = data;
    this.amount = this.data[0].length;
    this.headers = headers;
    this.type = "remove_col";
  };
  Handsontable.helper.inherit(Handsontable.UndoRedo.RemoveColumnAction, Handsontable.UndoRedo.Action);
  Handsontable.UndoRedo.RemoveColumnAction.prototype.undo = function (instance, undoneCallback) {
    var row, spliceArgs;
    for (var i = 0, len = instance.getData().length; i < len; i++) {
      row = instance.getSourceDataAtRow(i);

      spliceArgs = [this.index, 0];
      Array.prototype.push.apply(spliceArgs, this.data[i]);

      Array.prototype.splice.apply(row, spliceArgs);

    }

    if(typeof this.headers != 'undefined'){
      spliceArgs = [this.index, 0];
      Array.prototype.push.apply(spliceArgs, this.headers);
      Array.prototype.splice.apply(instance.getSettings().colHeaders, spliceArgs);
    }

    instance.addHookOnce('afterRender', undoneCallback);
    instance.render();
  };
  Handsontable.UndoRedo.RemoveColumnAction.prototype.redo = function (instance, redoneCallback) {
    instance.addHookOnce('afterRemoveCol', redoneCallback);
    instance.alter('remove_col', this.index, this.amount);
  };

  // register hooks
  Handsontable.hooks.register("beforeUndo");
  Handsontable.hooks.register("beforeRedo");
  Handsontable.hooks.register("afterUndo");
  Handsontable.hooks.register("afterRedo");
})(Handsontable);

(function(Handsontable){

  function init(){
    var instance = this;
    var pluginEnabled = typeof instance.getSettings().undo == 'undefined' || instance.getSettings().undo;

    if(pluginEnabled){
      if(!instance.undoRedo){
        /**
         * Instance of Handsontable.UndoRedo Plugin {@link Handsontable.UndoRedo}
         *
         * @alias undoRedo
         * @memberof! Handsontable.Core#
         * @type {UndoRedo}
         */
        instance.undoRedo = new Handsontable.UndoRedo(instance);

        exposeUndoRedoMethods(instance);

        instance.addHook('beforeKeyDown', onBeforeKeyDown);
        instance.addHook('afterChange', onAfterChange);
      }
    } else {
      if(instance.undoRedo){
        delete instance.undoRedo;

        removeExposedUndoRedoMethods(instance);

        instance.removeHook('beforeKeyDown', onBeforeKeyDown);
        instance.removeHook('afterChange', onAfterChange);
      }
    }
  }

  function onBeforeKeyDown(event){
    var instance = this;

    var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey;

    if(ctrlDown){
      if (event.keyCode === 89 || (event.shiftKey && event.keyCode === 90)) { //CTRL + Y or CTRL + SHIFT + Z
        instance.undoRedo.redo();
        event.stopImmediatePropagation();
      }
      else if (event.keyCode === 90) { //CTRL + Z
        instance.undoRedo.undo();
        event.stopImmediatePropagation();
      }
    }
  }

  function onAfterChange(changes, source){
    var instance = this;
    if (source == 'loadData'){
      return instance.undoRedo.clear();
    }
  }

  function exposeUndoRedoMethods(instance) {
    /**
     * {@link Handsontable.UndoRedo#undo}
     * @alias undo
     * @memberof! Handsontable.Core#
     */
    instance.undo = function(){
      return instance.undoRedo.undo();
    };

    /**
     * {@link Handsontable.UndoRedo#redo}
     * @alias redo
     * @memberof! Handsontable.Core#
     */
    instance.redo = function(){
      return instance.undoRedo.redo();
    };

    /**
     * {@link Handsontable.UndoRedo#isUndoAvailable}
     * @alias isUndoAvailable
     * @memberof! Handsontable.Core#
     */
    instance.isUndoAvailable = function(){
      return instance.undoRedo.isUndoAvailable();
    };

    /**
     * {@link Handsontable.UndoRedo#isRedoAvailable}
     * @alias isRedoAvailable
     * @memberof! Handsontable.Core#
     */
    instance.isRedoAvailable = function(){
      return instance.undoRedo.isRedoAvailable();
    };

    /**
     * {@link Handsontable.UndoRedo#clear}
     * @alias clearUndo
     * @memberof! Handsontable.Core#
     */
    instance.clearUndo = function(){
      return instance.undoRedo.clear();
    };
  }

  function removeExposedUndoRedoMethods(instance){
    delete instance.undo;
    delete instance.redo;
    delete instance.isUndoAvailable;
    delete instance.isRedoAvailable;
    delete instance.clearUndo;
  }

  Handsontable.hooks.add('afterInit', init);
  Handsontable.hooks.add('afterUpdateSettings', init);

})(Handsontable);
<|endoftext|>