<commit_before>
function Route(sourceAddress, destinationAddress, deployRate = 1) {
  this.sourceAddress = sourceAddress;
  this.destinationAddress = destinationAddress;
  this.deployRate = deployRate;

  this.sendCount = 0;
  this.receivedCount = 0;
  this.receivedStepCount = 0;
  this.efficiency = NaN;

  this.reset = function reset() {
    this.sendCount = 0;
    this.receivedCount = 0;
    this.receivedStepCount = 0;
    this.efficiency = NaN;
  }
}

function createSim(graph) {
  var self = {};

  self.simStep = 0;
  self.simDuration = 0;
  self.routes = {};

  function shuffleArray(a) {
    for (let i = a.length; i; i--) {
      let j = Math.floor(Math.random() * i);
      [a[i - 1], a[j]] = [a[j], a[i - 1]];
    }
  }

  self.reset = function reset() {
    var intNodes = graph.getIntNodes();
    for (var i = 0; i < intNodes.length; i++) {
      intNodes[i].o.reset();
    }

    for (var id in self.routes) {
      self.routes[id].reset();
    }

    self.simStep = 0;
    self.simDuration = 0;

    updateSimStatistics();

    graph.redraw();
  }

  function num(v, suffix = '') {
    return isNaN(v) ? '-' : (v.toFixed(2) + suffix);
  }

  function updateSimStatistics() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    var packetsBroadcastCount = 0;
    var packetsUnicastCount = 0;
    var packetsSendCount = 0;
    var packetsReceivedCount = 0;
    var packetsTransitCount = 0;
    var routesCount = 0;

    var routingEfficiencySum = 0;
    var routingEfficiencyCount = 0;

    function countPackets(packet) {
      if (packet.destinationAddress === BROADCAST_MAC) {
        packetsBroadcastCount += 1;
      } else {
        packetsUnicastCount += 1;

        var id = packet.sourceAddress + '=>' + packet.destinationAddress;
        packetsTransitCount += (id in self.routes);
      }
    }

    for (var i in intNodes) {
      var node = intNodes[i].o;
      node.incoming.forEach(countPackets);
      node.outgoing.forEach(countPackets);
    }

    for (var id in self.routes) {
      var route = self.routes[id];
      routesCount += 1;
      packetsSendCount += route.sendCount;
      packetsReceivedCount += route.receivedCount;

      if (!isNaN(route.efficiency)) {
        routingEfficiencySum += route.efficiency;
        routingEfficiencyCount += 1;
      }
    }

    // Convert to medium percent
    var routingEfficiencyPercent = 100 * routingEfficiencySum / routingEfficiencyCount;

    $$('sim_steps_total').nodeValue = self.simStep;
    $$('sim_duration').nodeValue = self.simDuration + ' ms';

    $$('packets_unicast_count').nodeValue = packetsUnicastCount
    $$('packets_broadcast_count').nodeValue = packetsBroadcastCount;

    function percent(value) {
      var p = (100 * value / packetsSendCount);
      return isNaN(p) ? '' : (' (' + p.toFixed(2) + '%)');
    };

    $$('routes_count').nodeValue = routesCount;
    $$('routes_packets_send').nodeValue = packetsSendCount;
    $$('routes_packets_received').nodeValue = packetsReceivedCount + percent(packetsReceivedCount);
    $$('routes_packets_transit').nodeValue = packetsTransitCount + percent(packetsTransitCount);
    var packetsLostCount = (packetsSendCount - packetsReceivedCount - packetsTransitCount);
    $$('routes_packets_lost').nodeValue = packetsLostCount + percent(packetsLostCount);

    $$('routing_efficiency').nodeValue = num(routingEfficiencyPercent, '%');
  }

  function updateRoutesTable() {
    var tbody = $('sim_routes');

    // Remove all elements
    clearChildren(tbody);

    for (key in self.routes) {
      var route = self.routes[key];
      var tr = append(tbody, 'tr');
      var source_td = append(tr, 'td', route.sourceAddress.slice(-5));
      var target_td = append(tr, 'td', route.destinationAddress.slice(-5));
      append(tr, 'td', route.deployRate);
      append(tr, 'td', route.sendCount);
      append(tr, 'td', route.receivedCount);
      append(tr, 'td', num(route.efficiency * 100, '%'));

      source_td.title = route.sourceAddress
      target_td.title = route.destinationAddress;
    }

    var display = (tbody.children.length === 0);
    displayElement($('sim_no_routes'), display);
  }

  self.delRoutes = function delRoutes() {
    function delRoute(sourceNode, targetNode) {
      var id = sourceNode.mac + '=>' + targetNode.mac;
      delete routes[id];
      updateSimStatistics();
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceNode = intNodes[0].o;
    for (var i = 1; i < intNodes.length; i += 1) {
      var targetNode = intNodes[i].o;
      delRoute(sourceNode, targetNode);
    }
    updateRoutesTable();
  }

  self.addRoutes = function addRoutes() {
    function addRoute(sourceAddress, destinationAddress) {
      var id = sourceAddress + '=>' + destinationAddress;
      if (!(id in self.routes)) {
        self.routes[id] = new Route(sourceAddress, destinationAddress, 1);
        updateSimStatistics();
      }
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceAddress = intNodes[0].o.mac;
    for (var i = 1; i < intNodes.length; i += 1) {
      var destinationAddress = intNodes[i].o.mac;
      addRoute(sourceAddress, destinationAddress);
    }
    updateRoutesTable();
  }

  function deployPackets_() {
    var nodes = graph.getIntNodes();
    var nodeMap = {};

    nodes.forEach(function(e) {
      nodeMap[e.o.mac] = e.o;
    });

    for (var id in self.routes) {
      var route = self.routes[id];
      if (randomBoolean(route.deployRate)) {
        var src = route.sourceAddress;
        var dst = route.destinationAddress;
        nodeMap[src].incoming.push(
          new Packet(src, src, src, dst, self.simStep)
        );
        route.sendCount += 1
      }
    }
  }

  self.deployPackets = function deployPackets() {
    if (isEmpty(self.routes)) {
      alert('No routes set on which to deploy packets.');
      return;
    }

    deployPackets_();
    updateRoutesTable();
    updateSimStatistics();

    graph.redraw();
  }

  function updateRouteEfficiency() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();
    var dijkstra = createDijkstra(intNodes, intLinks);

    for (var id in self.routes) {
      var route = self.routes[id];
      var sourceIntNode = intNodes.find(function(e) { return e.o.mac === route.sourceAddress; });
      var targetIntNode = intNodes.find(function(e) { return e.o.mac === route.destinationAddress; });
      var shortestDistance = dijkstra.getShortestDistance(sourceIntNode, targetIntNode);
      /*
      * Efficiency as rate of optimal step count weighted by rate of received packets.
      * This means packets in transit are counted as lost packets.
      */
      route.efficiency = (shortestDistance * route.receivedCount / route.receivedStepCount) * (route.receivedCount / route.sendCount);
    }
  }

  // Get other part of the link
  function getNeighbor(intLink, intNode) {
    return (intLink.source.index !== intNode.index) ? intLink.source : intLink.target;
  }

  function propagateBroadcastPacket(packet, intNode, intLinks) {
    function clonePacket(packet) {
      return JSON.parse(JSON.stringify(packet));
    }

    // Send cloned packet to all neighbors
    for (var k = 0; k < intLinks.length; k++) {
      var intLink = intLinks[k];
      var intNeigh = getNeighbor(intLink, intNode);
      packet = clonePacket(packet);

      if (randomBoolean(intLink.quality / 100)) {
        intNeigh.o.incoming.push(packet);
      } else {
        // Packet is lost
      }
    }
  }

  function propagateUnicastPacket(packet, intNode, intLinks) {
    function updateRouteStats(packet) {
      var id = packet.sourceAddress + '=>' + packet.destinationAddress;
      if (id in self.routes) {
        var route = self.routes[id];
        route.receivedCount += 1;
        route.receivedStepCount += (self.simStep - packet.deployedAtStep);
      } else {
        console.log('Packet route not known: ' + id);
      }
    }

    // Send to one neighbor
    for (var k = 0; k < intLinks.length; k++) {
      var intLink = intLinks[k];
      var intNeigh = getNeighbor(intLink, intNode);

      if (packet.receiverAddress === intNeigh.o.mac) {
        if (randomBoolean(intLink.quality / 100)) {
          intNeigh.o.incoming.push(packet);
          // Final destination reached
          if (packet.destinationAddress === intNeigh.o.mac) {
            updateRouteStats(packet);
          }
        }
        break;
      }
    }
  }

  self.step = function step(steps_id, deploy_id) {
    var steps = getInteger(steps_id);
    var deployPacketsEnabled = getBoolean(deploy_id);

    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    // Map internal node index to array of link objects
    var connections = {};

    intNodes.forEach(function(n) {
      connections[n.index] = [];
    });

    intLinks.forEach(function(l) {
      connections[l.source.index].push(l);
      connections[l.target.index].push(l);
    });

    var date = new Date();
    var simStartTime = date.getTime();

    // Simulation steps
    var len = intNodes.length;
    for (var step = 0; step < steps; step += 1) {
      self.simStep += 1;

      if (deployPacketsEnabled) {
        deployPackets_();
      }

      // Guarantee random handling
      shuffleArray(intNodes);

      // Step nodes
      for (var i = 0; i < len; i++) {
        var intNode = intNodes[i];
        intNode.o.step();
      }

      // Propagate packets
      for (var i = 0; i < len; i++) {
        var intNode = intNodes[i];
        var intLinks = connections[intNode.index];
        for (var p = 0; p < intNode.o.outgoing.length; p++) {
          var packet = intNode.o.outgoing[p];
          if (packet.receiverAddress === BROADCAST_MAC) {
            propagateBroadcastPacket(packet, intNode, intLinks);
          } else {
            propagateUnicastPacket(packet, intNode, intLinks);
          }
        }

        // All packets should have been handled
        intNode.o.outgoing = [];
      }
    }

    self.simDuration = date.getTime() - simStartTime;

    updateRouteEfficiency();
    updateRoutesTable();
    updateSimStatistics();

    graph.redraw();
  }

  return self;
}<commit_msg>let the traffic over a link influence transmission probability<commit_after>
function Route(sourceAddress, destinationAddress, deployRate = 1) {
  this.sourceAddress = sourceAddress;
  this.destinationAddress = destinationAddress;
  this.deployRate = deployRate;

  this.sendCount = 0;
  this.receivedCount = 0;
  this.receivedStepCount = 0;
  this.efficiency = NaN;

  this.reset = function reset() {
    this.sendCount = 0;
    this.receivedCount = 0;
    this.receivedStepCount = 0;
    this.efficiency = NaN;
  }
}

function createSim(graph) {
  var self = {};

  self.simStep = 0;
  self.simDuration = 0;
  self.routes = {};

  function shuffleArray(a) {
    for (let i = a.length; i; i--) {
      let j = Math.floor(Math.random() * i);
      [a[i - 1], a[j]] = [a[j], a[i - 1]];
    }
  }

  self.reset = function reset() {
    var intNodes = graph.getIntNodes();
    for (var i = 0; i < intNodes.length; i++) {
      intNodes[i].o.reset();
    }

    for (var id in self.routes) {
      self.routes[id].reset();
    }

    self.simStep = 0;
    self.simDuration = 0;

    updateSimStatistics();

    graph.redraw();
  }

  function num(v, suffix = '') {
    return isNaN(v) ? '-' : (v.toFixed(2) + suffix);
  }

  function updateSimStatistics() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    var packetsBroadcastCount = 0;
    var packetsUnicastCount = 0;
    var packetsSendCount = 0;
    var packetsReceivedCount = 0;
    var packetsTransitCount = 0;
    var routesCount = 0;

    var routingEfficiencySum = 0;
    var routingEfficiencyCount = 0;

    function countPackets(packet) {
      if (packet.destinationAddress === BROADCAST_MAC) {
        packetsBroadcastCount += 1;
      } else {
        packetsUnicastCount += 1;

        var id = packet.sourceAddress + '=>' + packet.destinationAddress;
        packetsTransitCount += (id in self.routes);
      }
    }

    for (var i in intNodes) {
      var node = intNodes[i].o;
      node.incoming.forEach(countPackets);
      node.outgoing.forEach(countPackets);
    }

    for (var id in self.routes) {
      var route = self.routes[id];
      routesCount += 1;
      packetsSendCount += route.sendCount;
      packetsReceivedCount += route.receivedCount;

      if (!isNaN(route.efficiency)) {
        routingEfficiencySum += route.efficiency;
        routingEfficiencyCount += 1;
      }
    }

    // Convert to medium percent
    var routingEfficiencyPercent = 100 * routingEfficiencySum / routingEfficiencyCount;

    $$('sim_steps_total').nodeValue = self.simStep;
    $$('sim_duration').nodeValue = self.simDuration + ' ms';

    $$('packets_unicast_count').nodeValue = packetsUnicastCount
    $$('packets_broadcast_count').nodeValue = packetsBroadcastCount;

    function percent(value) {
      var p = (100 * value / packetsSendCount);
      return isNaN(p) ? '' : (' (' + p.toFixed(2) + '%)');
    };

    $$('routes_count').nodeValue = routesCount;
    $$('routes_packets_send').nodeValue = packetsSendCount;
    $$('routes_packets_received').nodeValue = packetsReceivedCount + percent(packetsReceivedCount);
    $$('routes_packets_transit').nodeValue = packetsTransitCount + percent(packetsTransitCount);
    var packetsLostCount = (packetsSendCount - packetsReceivedCount - packetsTransitCount);
    $$('routes_packets_lost').nodeValue = packetsLostCount + percent(packetsLostCount);

    $$('routing_efficiency').nodeValue = num(routingEfficiencyPercent, '%');
  }

  function updateRoutesTable() {
    var tbody = $('sim_routes');

    // Remove all elements
    clearChildren(tbody);

    for (key in self.routes) {
      var route = self.routes[key];
      var tr = append(tbody, 'tr');
      var source_td = append(tr, 'td', route.sourceAddress.slice(-5));
      var target_td = append(tr, 'td', route.destinationAddress.slice(-5));
      append(tr, 'td', route.deployRate);
      append(tr, 'td', route.sendCount);
      append(tr, 'td', route.receivedCount);
      append(tr, 'td', num(route.efficiency * 100, '%'));

      source_td.title = route.sourceAddress
      target_td.title = route.destinationAddress;
    }

    var display = (tbody.children.length === 0);
    displayElement($('sim_no_routes'), display);
  }

  self.delRoutes = function delRoutes() {
    function delRoute(sourceNode, targetNode) {
      var id = sourceNode.mac + '=>' + targetNode.mac;
      delete routes[id];
      updateSimStatistics();
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceNode = intNodes[0].o;
    for (var i = 1; i < intNodes.length; i += 1) {
      var targetNode = intNodes[i].o;
      delRoute(sourceNode, targetNode);
    }
    updateRoutesTable();
  }

  self.addRoutes = function addRoutes() {
    function addRoute(sourceAddress, destinationAddress) {
      var id = sourceAddress + '=>' + destinationAddress;
      if (!(id in self.routes)) {
        self.routes[id] = new Route(sourceAddress, destinationAddress, 1);
        updateSimStatistics();
      }
    }

    var intNodes = graph.getSelectedIntNodes();
    if (intNodes.length == 0) {
      alert('Select one source and at least one target node.');
      return;
    }

    var sourceAddress = intNodes[0].o.mac;
    for (var i = 1; i < intNodes.length; i += 1) {
      var destinationAddress = intNodes[i].o.mac;
      addRoute(sourceAddress, destinationAddress);
    }
    updateRoutesTable();
  }

  function deployPackets_() {
    var nodes = graph.getIntNodes();
    var nodeMap = {};

    nodes.forEach(function(e) {
      nodeMap[e.o.mac] = e.o;
    });

    for (var id in self.routes) {
      var route = self.routes[id];
      if (randomBoolean(route.deployRate)) {
        var src = route.sourceAddress;
        var dst = route.destinationAddress;
        nodeMap[src].incoming.push(
          new Packet(src, src, src, dst, self.simStep)
        );
        route.sendCount += 1
      }
    }
  }

  self.deployPackets = function deployPackets() {
    if (isEmpty(self.routes)) {
      alert('No routes set on which to deploy packets.');
      return;
    }

    deployPackets_();
    updateRoutesTable();
    updateSimStatistics();

    graph.redraw();
  }

  function updateRouteEfficiency() {
    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();
    var dijkstra = createDijkstra(intNodes, intLinks);

    for (var id in self.routes) {
      var route = self.routes[id];
      var sourceIntNode = intNodes.find(function(e) { return e.o.mac === route.sourceAddress; });
      var targetIntNode = intNodes.find(function(e) { return e.o.mac === route.destinationAddress; });
      var shortestDistance = dijkstra.getShortestDistance(sourceIntNode, targetIntNode);
      /*
      * Efficiency as rate of optimal step count weighted by rate of received packets.
      * This means packets in transit are counted as lost packets.
      */
      route.efficiency = (shortestDistance * route.receivedCount / route.receivedStepCount) * (route.receivedCount / route.sendCount);
    }
  }

  // Get other part of the link
  function getNeighbor(intLink, intNode) {
    return (intLink.source.index !== intNode.index) ? intLink.source : intLink.target;
  }

  // Decide if a transmission is successful based on link properties
  function isTransmissionSuccessful(intLink) {
    var n = 100 * (Math.min(intLink.packetCount, intLink.bandwidth) / intLink.bandwidth);
    return ((intLink.quality / 100) * Math.pow(0.999, n)) < Math.random();
  }

  function propagateBroadcastPacket(packet, intNode, intLinks) {
    function clonePacket(packet) {
      return JSON.parse(JSON.stringify(packet));
    }

    // Send cloned packet to all neighbors
    for (var k = 0; k < intLinks.length; k++) {
      var intLink = intLinks[k];
      var intNeigh = getNeighbor(intLink, intNode);

      intLink.packetCount += 1;
      if (isTransmissionSuccessful(intLink)) {
        packet = clonePacket(packet);
        intNeigh.o.incoming.push(packet);
      }
    }
  }

  function propagateUnicastPacket(packet, intNode, intLinks) {
    function updateRouteStats(packet) {
      var id = packet.sourceAddress + '=>' + packet.destinationAddress;
      if (id in self.routes) {
        var route = self.routes[id];
        route.receivedCount += 1;
        route.receivedStepCount += (self.simStep - packet.deployedAtStep);
      } else {
        console.log('Packet route not known: ' + id);
      }
    }

    // Send to one neighbor
    for (var k = 0; k < intLinks.length; k++) {
      var intLink = intLinks[k];
      var intNeigh = getNeighbor(intLink, intNode);

      if (packet.receiverAddress === intNeigh.o.mac) {
        intLink.packetCount += 1;
        if (isTransmissionSuccessful(intLink)) {
          intNeigh.o.incoming.push(packet);
          // Final destination reached
          if (packet.destinationAddress === intNeigh.o.mac) {
            updateRouteStats(packet);
          }
        }
        break;
      }
    }
  }

  self.step = function step(steps_id, deploy_id) {
    var steps = getInteger(steps_id);
    var deployPacketsEnabled = getBoolean(deploy_id);

    var intNodes = graph.getIntNodes();
    var intLinks = graph.getIntLinks();

    // Map internal node index to array of link objects
    var connections = {};

    intNodes.forEach(function(n) {
      connections[n.index] = [];
    });

    intLinks.forEach(function(l) {
      connections[l.source.index].push(l);
      connections[l.target.index].push(l);
      // Count packets over the link for packet loss calculation
      l.packetCount = 0;
    });

    var date = new Date();
    var simStartTime = date.getTime();

    // Simulation steps
    var len = intNodes.length;
    for (var step = 0; step < steps; step += 1) {
      self.simStep += 1;

      if (deployPacketsEnabled) {
        deployPackets_();
      }

      // Guarantee random handling
      shuffleArray(intNodes);

      // Step nodes
      for (var i = 0; i < len; i++) {
        var intNode = intNodes[i];
        intNode.o.step();
      }

      // Propagate packets
      for (var i = 0; i < len; i++) {
        var intNode = intNodes[i];
        var intLinks = connections[intNode.index];
        for (var p = 0; p < intNode.o.outgoing.length; p++) {
          var packet = intNode.o.outgoing[p];
          if (packet.receiverAddress === BROADCAST_MAC) {
            propagateBroadcastPacket(packet, intNode, intLinks);
          } else {
            propagateUnicastPacket(packet, intNode, intLinks);
          }
        }

        // All packets should have been handled
        intNode.o.outgoing = [];
      }
    }

    self.simDuration = date.getTime() - simStartTime;

    updateRouteEfficiency();
    updateRoutesTable();
    updateSimStatistics();

    graph.redraw();
  }

  return self;
}<|endoftext|>


<commit_before>//META {"name": "BetterReplyer", "source": "https://github.com/Zerthox/BetterDiscord-Plugins/blob/master/plugins/BetterReplyer.plugin.js"} *//

/**
 * BetterReplyer plugin class
 * @author Zerthox
 * @version 3.0.3
 */
class BetterReplyer {

	/**
	 * @return {string} plugin name
	 */
	getName() {
		return "BetterReplyer";
    }

    /**
     * @return {*} plugin description
     */
	getDescription() {
		return BdApi.React.createElement("span", {style: {"white-space": "pre-line"}},
			"Reply to people using their ID with a button.\n Inspired by ",
			BdApi.React.createElement("a", {href: "https://github.com/cosmicsalad/Discord-Themes-and-Plugins/blob/master/plugins/replyer.plugin.js", target: "_blank"}, "Replyer"),
			" by ",
			BdApi.React.createElement("a", {href: "https://github.com/cosmicsalad", target: "_blank"}, "@Hammock#3110"),
			", ",
			BdApi.React.createElement("a", {href: "https://github.com/Delivator", target: "_blank"}, "@Natsulus#0001"),
			" & ",
			BdApi.React.createElement("a", {href: "https://github.com/rauenzi", target: "_blank"}, "@Zerebos#7790"),
			"."
		);
    }

    /**
     * @return {string} plugin version
     */
	getVersion() {
		return "3.0.3";
	}
    /**
     * @return {*} plugin author
     */
	getAuthor() {
		return BdApi.React.createElement("a", {href: "https://github.com/Zerthox", target: "_blank"}, "Zerthox");
	}

	/**
	 * plugin class constructor
	 */
	constructor() {

		/**
		 * object with selectors
		 */
		this.selector = {
			messageHeader: ".messages-3amgkR .headerCozyMeta-rdohGq",
			channelTextarea: ".textArea-2Spzkt",
			accountDetails: ".accountDetails-3k9g4n"
		};

		/**
		 * plugin styles
		 */
		this.css = `/* BetterReplyer CSS */
			.replyer {
				position: relative;
				top: -1px;
				margin-left: 5px;
				padding: 3px 5px;
				background: rgba(0, 0, 0, 0.4);
				border-radius: 3px;
				color: #fff !important;
				font-size: 10px;
				text-transform: uppercase;
				cursor: pointer;
			}
			.container-1YxwTf:not(:hover) .replyer {
				visibility: hidden;
			}`;

		/**
		 * object with plugin state relevant stuff
		 */
		this.state = {
			/**
			 * clicked element save
			 */
			clicked: document.body,

			/**
			 * focused textarea save
			 */
			focused: null,

			/**
			 * reply mode
			 */
			mode: false
		}

				/**
		 * object with handlers
		 */
		this.handler = {

			/**
			 * document click handler
			 */
			click: (e) => {

				// save event target
				this.state.clicked = e.target;
			},

			/**
			 * textarea blur handler
			 */
			blur: (e) => {

				// save event target
				this.state.focused = e.target;

				// update reply mode
				this.state.mode = this.state.clicked.matches(`${this.selector.messageHeader} .replyer`) ? true : false;
			}
		};
	}

	 /**
     * plugin start function
     */
	start() {

		// inject styles
		BdApi.injectCSS(this.getName(), this.css);

		// add click handler
		document.addEventListener("mousedown", this.handler.click);

        // iterate over all message headers
        for (var e of document.querySelectorAll(this.selector.messageHeader)) {

            // insert reply button
            this.insert(e);
		}

		// iterate over all textareas
		for (var e of document.querySelectorAll(this.selector.channelTextarea)) {

			// add blur handler
			e.addEventListener("blur", this.handler.blur);
		}

        // console output
		console.log(`[${this.getName()}] Enabled`);
    }

    /**
     * plugin stop function
     */
	stop() {

		// remove styles
		BdApi.clearCSS(this.getName());

		// remove click handler
		document.removeEventListener("mousedown", this.handler.click);

        // iterate over all message headers
        for (var e of document.querySelectorAll(`${this.selector.messageHeader} .replyer`)) {

            // remove reply button
            e.remove();
		}

		// iterate over all textareas
		for (var e of document.querySelectorAll(this.selector.channelTextarea)) {

			// remove blur handler
			e.removeEventListener("blur", this.handler.blur);
		}

		// reset state
		this.state = {
			clicked: document.body,
			focused: null,
			mode: false
		}

        // console output
		console.log(`[${this.getName()}] Disabled`);
	}

	/**
	 * plugin DOM observer
	 * @param {*} e event
	 */
	observer(e) {

		// iterate over added nodes
        for (var n of e.addedNodes) {

            // check if node is html element
            if (n instanceof HTMLElement) {

				// check if added nodes contain message header or textarea
                if (n.matches(this.selector.messageHeader)) {

					// insert directly into message header
                    this.insert(n);
                }
				else if (n.matches(this.selector.channelTextarea)) {

					// add blur handler directly to textarea
					n.addEventListener("blur", this.handler.blur);
				}
                else {

					// search for descendant message headers
					var l = n.findAll(this.selector.messageHeader);
					if (l.length > 0) {

						// insert into descendant message headers
						for (var m of l) {
							this.insert(m);
						}
					}

					// search for descendant textareas
					l = n.findAll(this.selector.channelTextarea);
					if (l.length > 0) {

						// add blur handler to descendant textareas
						for (var t of l) {
							t.addEventListener("blur", this.handler.blur);
						}
					}
				}
            }
        }
	}

	/**
	 * insert reply button into passed message header
	 * @param {HTMLElement} e message header
	 */
	insert(e) {

		// get message author id
		var id = BdApi.getInternalInstance(e).child.memoizedProps.message.author.id;

		// check if message is sent by user
		if (id != BdApi.getInternalInstance(document.querySelector(this.selector.accountDetails)).return.memoizedProps.user.id) {

			// create reply button element
			var b = document.createElement("span");

			// set reply button class
			b.className = "replyer";

			// set reply button text
			b.innerHTML = "Reply";

			// add event listener
			b.addEventListener("click", () => {

				// get last focused textarea or default to first textarea found
				var t = this.state.focused instanceof HTMLElement ? this.state.focused : document.querySelector(this.selector.channelTextarea);

				// get mention string
				var m = `<@!${id}>`;

				// focus textarea
				t.focus();

				// check which mode to use
				if (this.state.mode) {

					// insert directly
					document.execCommand("insertText", false, m);
				}
				else {

					// add space to mention string
					m += " ";

					// save selection
					var ts = t.selectionStart + m.length,
						te = t.selectionEnd + m.length;

					// go to start of textarea
					t.selectionEnd = 0;

					// insert mention
					document.execCommand("insertText", false, m);

					// select saved selection
					t.setSelectionRange(ts, te);
				}
			});

			// append reply button
			e.appendChild(b);
		}
	}

}<commit_msg>Remove reply buttons in channels with disabled textarea<commit_after>//META {"name": "BetterReplyer", "source": "https://github.com/Zerthox/BetterDiscord-Plugins/blob/master/plugins/BetterReplyer.plugin.js"} *//

/**
 * BetterReplyer plugin class
 * @author Zerthox
 * @version 3.0.4
 */
class BetterReplyer {

	/**
	 * @return {string} plugin name
	 */
	getName() {
		return "BetterReplyer";
    }

    /**
     * @return {*} plugin description
     */
	getDescription() {
		return BdApi.React.createElement("span", {style: {"white-space": "pre-line"}},
			"Reply to people using their ID with a button.\n Inspired by ",
			BdApi.React.createElement("a", {href: "https://github.com/cosmicsalad/Discord-Themes-and-Plugins/blob/master/plugins/replyer.plugin.js", target: "_blank"}, "Replyer"),
			" by ",
			BdApi.React.createElement("a", {href: "https://github.com/cosmicsalad", target: "_blank"}, "@Hammock#3110"),
			", ",
			BdApi.React.createElement("a", {href: "https://github.com/Delivator", target: "_blank"}, "@Natsulus#0001"),
			" & ",
			BdApi.React.createElement("a", {href: "https://github.com/rauenzi", target: "_blank"}, "@Zerebos#7790"),
			"."
		);
    }

    /**
     * @return {string} plugin version
     */
	getVersion() {
		return "3.0.4";
	}
    /**
     * @return {*} plugin author
     */
	getAuthor() {
		return BdApi.React.createElement("a", {href: "https://github.com/Zerthox", target: "_blank"}, "Zerthox");
	}

	/**
	 * plugin class constructor
	 */
	constructor() {

		/**
		 * object with selectors
		 */
		this.selector = {
			messageHeader: ".messages-3amgkR .headerCozyMeta-rdohGq",
			channelTextarea: ".textArea-2Spzkt",
			channelTextareaDisabled: ".channelTextAreaDisabled-rZtG8r",
			accountDetails: ".accountDetails-3k9g4n"
		};

		/**
		 * plugin styles
		 */
		this.css = `/* BetterReplyer CSS */
			.replyer {
				position: relative;
				top: -1px;
				margin-left: 5px;
				padding: 3px 5px;
				background: rgba(0, 0, 0, 0.4);
				border-radius: 3px;
				color: #fff !important;
				font-size: 10px;
				text-transform: uppercase;
				cursor: pointer;
			}
			.container-1YxwTf:not(:hover) .replyer {
				visibility: hidden;
			}`;

		/**
		 * object with plugin state relevant stuff
		 */
		this.state = {
			/**
			 * clicked element save
			 */
			clicked: document.body,

			/**
			 * focused textarea save
			 */
			focused: null,

			/**
			 * reply mode
			 */
			mode: false
		}

				/**
		 * object with handlers
		 */
		this.handler = {

			/**
			 * document click handler
			 */
			click: (e) => {

				// save event target
				this.state.clicked = e.target;
			},

			/**
			 * textarea blur handler
			 */
			blur: (e) => {

				// save event target
				this.state.focused = e.target;

				// update reply mode
				this.state.mode = this.state.clicked.matches(`${this.selector.messageHeader} .replyer`) ? true : false;
			}
		};
	}

	 /**
     * plugin start function
     */
	start() {

		// inject styles
		BdApi.injectCSS(this.getName(), this.css);

		// add click handler
		document.addEventListener("mousedown", this.handler.click);

        // iterate over all message headers
        for (var e of document.querySelectorAll(this.selector.messageHeader)) {

            // insert reply button
            this.insert(e);
		}

		// iterate over all textareas
		for (var e of document.querySelectorAll(this.selector.channelTextarea)) {

			// add blur handler
			e.addEventListener("blur", this.handler.blur);
		}

        // console output
		console.log(`[${this.getName()}] Enabled`);
    }

    /**
     * plugin stop function
     */
	stop() {

		// remove styles
		BdApi.clearCSS(this.getName());

		// remove click handler
		document.removeEventListener("mousedown", this.handler.click);

        // iterate over all message headers
        for (var e of document.querySelectorAll(`${this.selector.messageHeader} .replyer`)) {

            // remove reply button
            e.remove();
		}

		// iterate over all textareas
		for (var e of document.querySelectorAll(this.selector.channelTextarea)) {

			// remove blur handler
			e.removeEventListener("blur", this.handler.blur);
		}

		// reset state
		this.state = {
			clicked: document.body,
			focused: null,
			mode: false
		}

        // console output
		console.log(`[${this.getName()}] Disabled`);
	}

	/**
	 * plugin DOM observer
	 * @param {*} e event
	 */
	observer(e) {

		// check if channel textarea is disabled
		if (document.querySelector(this.selector.channelTextareaDisabled) === null) {

			// iterate over added nodes
			for (var n of e.addedNodes) {

				// check if node is html element
				if (n instanceof HTMLElement) {

					// check if added nodes contain message header or textarea
					if (n.matches(this.selector.messageHeader)) {

						// insert directly into message header
						this.insert(n);
					}
					else if (n.matches(this.selector.channelTextarea)) {

						// add blur handler directly to textarea
						n.addEventListener("blur", this.handler.blur);
					}
					else {

						// search for descendant message headers
						var l = n.findAll(this.selector.messageHeader);
						if (l.length > 0) {

							// insert into descendant message headers
							for (var m of l) {
								this.insert(m);
							}
						}

						// search for descendant textareas
						l = n.findAll(this.selector.channelTextarea);
						if (l.length > 0) {

							// add blur handler to descendant textareas
							for (var t of l) {
								t.addEventListener("blur", this.handler.blur);
							}
						}
					}
				}
			}
		}
	}

	/**
	 * insert reply button into passed message header
	 * @param {HTMLElement} e message header
	 */
	insert(e) {

		// get message author id
		var id = BdApi.getInternalInstance(e).child.memoizedProps.message.author.id;

		// check if message is sent by user
		if (id != BdApi.getInternalInstance(document.querySelector(this.selector.accountDetails)).return.memoizedProps.user.id) {

			// create reply button element
			var b = document.createElement("span");

			// set reply button class
			b.className = "replyer";

			// set reply button text
			b.innerHTML = "Reply";

			// add event listener
			b.addEventListener("click", () => {

				// get last focused textarea or default to first textarea found
				var t = this.state.focused instanceof HTMLElement ? this.state.focused : document.querySelector(this.selector.channelTextarea);

				// get mention string
				var m = `<@!${id}>`;

				// focus textarea
				t.focus();

				// check which mode to use
				if (this.state.mode) {

					// insert directly
					document.execCommand("insertText", false, m);
				}
				else {

					// add space to mention string
					m += " ";

					// save selection
					var ts = t.selectionStart + m.length,
						te = t.selectionEnd + m.length;

					// go to start of textarea
					t.selectionEnd = 0;

					// insert mention
					document.execCommand("insertText", false, m);

					// select saved selection
					t.setSelectionRange(ts, te);
				}
			});

			// append reply button
			e.appendChild(b);
		}
	}

}<|endoftext|>


<commit_before>import * as ajax from './support/ajax';
import * as layout from './support/layout';
import * as system from './support/system';
import * as task from './support/task';

var ranks;
var myRank;
var theRows;
var rankCount;
var characterRow;

var privLookup = {
  'Bank Withdraw': 5,
  'Build/Upgrade/Demolish Structures': 5,
  'Can Un-Tag Items': 5,
  'Build/Upgrade Structures': 4,
  'Can Kick Members': 4,
  'Can Mass Messages': 0.5,
  'Take Items': 0.2,
  'Can Recall Tagged Items': 0.2,
  'Store Items': 0.1,
  'Can View Advisor': 0.1
};

function parseRankData(linkElement, responseText) { // Native
  // Makes a weighted calculation of available permissions and gets tax rate
  var doc = system.createDocument(responseText);
  var checkBoxes = doc.querySelectorAll(
    '#pCC input[type="checkbox"]:checked');
  var count = 0;
  Array.prototype.forEach.call(checkBoxes, function(checkbox) {
    var privName = checkbox.nextElementSibling.textContent.trim();
    if (privName in privLookup) {
      count += privLookup[privName];
    } else {count += 1;}
  });
  var taxRate = doc.querySelector('#pCC input[name="rank_tax"]').value;
  linkElement.insertAdjacentHTML('afterbegin', '(' +
    Math.round(10 * count) / 10 + ') Tax:(' + taxRate + '%) ');
}

function fetchRankData() { // jQuery
  var calcButton = document.getElementById('getrankweightings');
  calcButton.classList.add('fshHide');
  var allItems = document.querySelectorAll('#pCC input[value="Edit"]');
  Array.prototype.forEach.call(allItems, function(anItem) {
    var targetNode = anItem.parentNode.parentNode.previousElementSibling;
    var href = /window\.location='(.*)';/.exec(anItem
      .getAttribute('onclick'))[1];
    $.get(href).done(parseRankData.bind(null, targetNode));
  });
}

function notValidRow(thisRankRowNum, targetRowNum, parentTable) { // Native
  return characterRow >= Math.min(thisRankRowNum, targetRowNum) ||
    targetRowNum < 1 ||
    targetRowNum > parentTable.rows.length;
}

function getTargetRowNumber(val) { // Native
  if (val === 'Up') {return -1;}
  return 2;
}

function getPxScroll(val) { // Native
  if (val === 'Up') {return -22;}
  return 22;
}

function ajaxifyRankControls(evt) { // jQuery
  var val = evt.target.getAttribute('value');
  if (val !== 'Up' && val !== 'Down') {return;}
  evt.stopPropagation();
  var onclickHREF = /window.location='(.*)';/
    .exec(evt.target.getAttribute('onclick'))[1];
  var thisRankRow = evt.target.parentNode.parentNode.parentNode;
  var thisRankRowNum = thisRankRow.rowIndex;
  var targetRowNum = getTargetRowNumber(val);
  var parentTable = thisRankRow.parentNode;
  if (notValidRow(thisRankRowNum, targetRowNum, parentTable)) {return;}
  $.get(onclickHREF);
  var injectRow = parentTable.rows[targetRowNum];
  parentTable.insertBefore(thisRankRow, injectRow);
  var pxScroll = getPxScroll(val);
  window.scrollBy(0, pxScroll);
}

function doButtons() { // Native
  // gather rank info button
  var weightButton = document.createElement('input');
  weightButton.id = 'getrankweightings';
  weightButton.className = 'custombutton';
  weightButton.setAttribute('type', 'button');
  weightButton.setAttribute('value', 'Get Rank Weightings');
  weightButton.addEventListener('click', fetchRankData);
  var theTd = document.getElementById('show-guild-founder-rank-name')
    .parentNode;
  theTd.insertAdjacentHTML('beforeend', ' ');
  theTd.insertAdjacentElement('beforeend', weightButton);

  if (system.getValue('ajaxifyRankControls')) {
    layout.pCC.addEventListener('click',
      ajaxifyRankControls, true);
  }
}

function writeMembers(el) { // Native
  var rankCell = el.firstElementChild;
  var rankName = rankCell.textContent;
  if (ranks[rankName]) { // has members
    if (rankName === myRank) {
      characterRow = rankCount; // limit for ajaxify later
    }
    rankCell.insertAdjacentHTML('beforeend', ' - ' +
      ranks[rankName].join(', ') + '');
  }
}

function paintRanks() { // Native
  var limit = performance.now() + 10;
  while (performance.now() < limit &&
      rankCount < theRows.length) {
    var el = theRows[rankCount];

    writeMembers(el);

    rankCount += 1;
  }
  if (rankCount < theRows.length) {
    task.add(3, paintRanks);
  }
}

function getRanks(membrList) { // Native
  ranks = Object.keys(membrList).reduce(function(prev, curr) {
    if (curr !== 'lastUpdate') {
      var rankName = membrList[curr].rank_name;
      prev[rankName] = prev[rankName] || [];
      prev[rankName].push(curr);
    }
    return prev;
  }, {});
  myRank = membrList[layout.playerName()].rank_name;
  theRows = layout.pCC.firstElementChild
    .nextElementSibling.rows[13].firstElementChild.firstElementChild.rows;
  rankCount = 1;
  task.add(3, paintRanks);
}

export function injectGuildRanks() { // jQuery
  ajax.getMembrList(true).done(function(membrList) {
    task.add(3, getRanks, [membrList]);
  });
  task.add(3, doButtons);
}
<commit_msg>rank bug<commit_after>import * as ajax from './support/ajax';
import * as layout from './support/layout';
import * as system from './support/system';
import * as task from './support/task';

var ranks;
var myRank;
var theRows;
var rankCount;
var characterRow;

var privLookup = {
  'Bank Withdraw': 5,
  'Build/Upgrade/Demolish Structures': 5,
  'Can Un-Tag Items': 5,
  'Build/Upgrade Structures': 4,
  'Can Kick Members': 4,
  'Can Mass Messages': 0.5,
  'Take Items': 0.2,
  'Can Recall Tagged Items': 0.2,
  'Store Items': 0.1,
  'Can View Advisor': 0.1
};

function parseRankData(linkElement, responseText) { // Native
  // Makes a weighted calculation of available permissions and gets tax rate
  var doc = system.createDocument(responseText);
  var checkBoxes = doc.querySelectorAll(
    '#pCC input[type="checkbox"]:checked');
  var count = 0;
  Array.prototype.forEach.call(checkBoxes, function(checkbox) {
    var privName = checkbox.nextElementSibling.textContent.trim();
    if (privName in privLookup) {
      count += privLookup[privName];
    } else {count += 1;}
  });
  var taxRate = doc.querySelector('#pCC input[name="rank_tax"]').value;
  linkElement.insertAdjacentHTML('afterbegin', '(' +
    Math.round(10 * count) / 10 + ') Tax:(' + taxRate + '%) ');
}

function fetchRankData() { // jQuery
  var calcButton = document.getElementById('getrankweightings');
  calcButton.classList.add('fshHide');
  var allItems = document.querySelectorAll('#pCC input[value="Edit"]');
  Array.prototype.forEach.call(allItems, function(anItem) {
    var targetNode = anItem.parentNode.parentNode.previousElementSibling;
    var href = /window\.location='(.*)';/.exec(anItem
      .getAttribute('onclick'))[1];
    $.get(href).done(parseRankData.bind(null, targetNode));
  });
}

function notValidRow(thisRankRowNum, targetRowNum, parentTable) { // Native
  return characterRow >= Math.min(thisRankRowNum, targetRowNum) ||
    targetRowNum < 1 ||
    targetRowNum > parentTable.rows.length;
}

function getTargetRowNumber(val) { // Native
  if (val === 'Up') {return -1;}
  return 2;
}

function getPxScroll(val) { // Native
  if (val === 'Up') {return -22;}
  return 22;
}

function ajaxifyRankControls(evt) { // jQuery
  var val = evt.target.getAttribute('value');
  if (val !== 'Up' && val !== 'Down') {return;}
  evt.stopPropagation();
  var onclickHREF = /window.location='(.*)';/
    .exec(evt.target.getAttribute('onclick'))[1];
  var thisRankRow = evt.target.parentNode.parentNode.parentNode;
  var thisRankRowNum = thisRankRow.rowIndex;
  var targetRowNum = thisRankRowNum + getTargetRowNumber(val);
  var parentTable = thisRankRow.parentNode;
  if (notValidRow(thisRankRowNum, targetRowNum, parentTable)) {return;}
  $.get(onclickHREF);
  var injectRow = parentTable.rows[targetRowNum];
  parentTable.insertBefore(thisRankRow, injectRow);
  var pxScroll = getPxScroll(val);
  window.scrollBy(0, pxScroll);
}

function doButtons() { // Native
  // gather rank info button
  var weightButton = document.createElement('input');
  weightButton.id = 'getrankweightings';
  weightButton.className = 'custombutton';
  weightButton.setAttribute('type', 'button');
  weightButton.setAttribute('value', 'Get Rank Weightings');
  weightButton.addEventListener('click', fetchRankData);
  var theTd = document.getElementById('show-guild-founder-rank-name')
    .parentNode;
  theTd.insertAdjacentHTML('beforeend', ' ');
  theTd.insertAdjacentElement('beforeend', weightButton);

  if (system.getValue('ajaxifyRankControls')) {
    layout.pCC.addEventListener('click',
      ajaxifyRankControls, true);
  }
}

function writeMembers(el) { // Native
  var rankCell = el.firstElementChild;
  var rankName = rankCell.textContent;
  if (ranks[rankName]) { // has members
    if (rankName === myRank) {
      characterRow = rankCount; // limit for ajaxify later
    }
    rankCell.insertAdjacentHTML('beforeend', ' - ' +
      ranks[rankName].join(', ') + '');
  }
}

function paintRanks() { // Native
  var limit = performance.now() + 10;
  while (performance.now() < limit &&
      rankCount < theRows.length) {
    var el = theRows[rankCount];

    writeMembers(el);

    rankCount += 1;
  }
  if (rankCount < theRows.length) {
    task.add(3, paintRanks);
  }
}

function getRanks(membrList) { // Native
  ranks = Object.keys(membrList).reduce(function(prev, curr) {
    if (curr !== 'lastUpdate') {
      var rankName = membrList[curr].rank_name;
      prev[rankName] = prev[rankName] || [];
      prev[rankName].push(curr);
    }
    return prev;
  }, {});
  myRank = membrList[layout.playerName()].rank_name;
  theRows = layout.pCC.firstElementChild
    .nextElementSibling.rows[13].firstElementChild.firstElementChild.rows;
  rankCount = 1;
  task.add(3, paintRanks);
}

export function injectGuildRanks() { // jQuery
  ajax.getMembrList(true).done(function(membrList) {
    task.add(3, getRanks, [membrList]);
  });
  task.add(3, doButtons);
}
<|endoftext|>


<commit_before>const easingCache = 3
const easingFraction = 0.95
const easingAccelerator = 1.5
const quadraticEasing = 0.25

const bounds = (target, val) => {
  if (val < -target._height) {
    // target._easing = false
    return -target._height
  } else if (val > 0) {
    // target._easing = false
    return 0
  } else {
    return val
  }
}

const easeOut = (target, distance, original, event, stamp, easingFraction, setVal) => {
  if (target._easing) {
    // maybe stop when 2 ticks are same value
    target._ly = setVal(target, target._ly + distance * (1 - easingFraction), original, event, stamp)
    if (distance > 0.5 || distance < -0.5) {
      target._isEasing = global.requestAnimationFrame(() => easeOut(target, distance * easingFraction, original, event, stamp, easingFraction, setVal))
    } else {
      target._easing = false
    }
  }
}

const setValX = (target, val, original, event, stamp) => {
  val = bounds(target, val)
  target.style.transform = `translate3d(${val}rem, 0, 0)`
  if (!original) {
    original = target
  }
  if (original._ && original._._scrollListener) {
    original._._scrollListener({ x: val, target: original, scroller: target }, stamp)
  }
  return val
}

const setValY = (target, val, original, event, stamp) => {
  val = bounds(target, val)
  target.style.transform = `translate3d(0, ${val}rem, 0)`
  if (!original) {
    original = target
  }

  if (original._ && original._._scrollListener) {
    original._._scrollListener({ y: val, target: original, scroller: target }, stamp)
  }
  return val
}

const touchstart = ({ target, y, event }, ch, sh) => {
  global.cancelAnimationFrame(target._isEasing)

  if (ch >= sh) {
    target._block = true
    target.__init = false
    return true
  }

  target.__init = true
  target._start = y
  target._y = target._ly || 0
  target._easing = false
  target._prev = [ 1, 0, 0, 0 ]
  target._height = sh - ch
  target._block = ch >= sh
  target._sh = sh
}

const touchmove = (event, stamp, setVal) => {
  const target = event.target
  if (target._block) return true
  const original = event.original
  const y = event.y
  const index = target._prev[0]
  target._prev[0]++
  if (target._prev[0] > easingCache) {
    target._prev[0] = 1
  }
  const val = y - target._start + target._y
  target._prev[index] = val - (target._ly || 0)
  target._ly = setVal(target, val, original, event, stamp)
}

const touchend = (event, stamp, setVal) => {
  const target = event.target
  target.__init = false
  if (!target._prev) return
  const original = event.original
  var delta = 0
  var i = easingCache
  while (i--) {
    delta += target._prev[i] || 0
  }
  delta = (delta / easingCache) * easingAccelerator
  const sing = delta < 0 ? -1 : 1
  var distance = quadraticEasing * ((delta * delta) * sing)
  if (distance > 750) {
    distance = 750
  }
  target._easing = true
  easeOut(target, distance, original, event, stamp, easingFraction, setVal)
}

const lookupMode = [1.0, 28.0, 500.0]

const wheelX = (event, stamp, sh) => {
  const evt = event.event

  if ('deltaX' in evt) {
    if (Math.abs(evt.deltaX) <= Math.abs(evt.deltaY)) {
      return
    } else {
      event.prevent = true
      evt.preventDefault()
    }
    const mode = lookupMode[evt.deltaMode] || lookupMode[0]
    event.x = evt.deltaX * mode * -1
  } else {
    // is this ok on x?
    event.x = evt.wheelDelta / -3
  }

  const target = event.target
  if (!target.__init) {
    if (!target._ly) {
      target._ly = 0
    }
    if (touchstart(event, event.target.parentNode.clientWidth, sh)) {
      return
    }
  }
  clearTimeout(target._timeout)
  target._timeout = setTimeout(() => {
    if (target.offsetParent !== null) {
      target.__init = false
    }
  }, 20)
  target._ly = setValX(target, target._ly + event.x, event.original, event, stamp)
}

const wheelY = (event, stamp, sh) => {
  event.prevent = true
  const evt = event.event
  evt.preventDefault()
  if ('deltaY' in evt) {
    if (Math.abs(evt.deltaX) >= Math.abs(evt.deltaY)) {
      return
    } else {
      event.prevent = true
      evt.preventDefault()
    }
    const mode = lookupMode[evt.deltaMode] || lookupMode[0]
    event.y = evt.deltaY * mode * -1
  } else {
    event.y = evt.wheelDelta / -3
  }
  const target = event.target
  if (!target.__init) {
    if (!target._ly) {
      target._ly = 0
    }
    if (touchstart(event, event.target.parentNode.clientHeight, sh)) {
      return
    }
  }
  clearTimeout(target._timeout)
  target._timeout = setTimeout(() => {
    if (target.offsetParent !== null) {
      target.__init = false
    }
  }, 20)
  target._ly = setValY(target, target._ly + event.y, event.original, event, stamp)
}

export default {
  props: {
    scroll: (t, val) => {
      var fn, target, size, direction

      if (!val) return
      if (typeof val === 'function') {
        fn = val
      } else if (typeof val === 'object') {
        if (val.target) target = val.target
        fn = val.onScroll
      }

      direction = val.direction

      t.set({
        define: {
          hasScrollY: true
        }
      }, false)

      if (fn) {
        t.set({
          define: {
            _scrollListener: fn
          }
        }, false)
      }

      if (!direction) {
        direction = 'y'
      }

      if (!size) {
        size = direction === 'y'
          ? (val) => val.target.scrollHeight
          : (val) => val.target.scrollWidth
      }

      if (!target) {
        target = t => t
      }

      t.set({
        define: {
          setScrollY (y, t, stamp) {
            const rt = target(t)
            global.cancelAnimationFrame(rt._isEasing)
            if (!rt._ly) rt._ly = 0
            rt._ly = setValY(rt, bounds(rt, -y) - rt._ly, t, void 0, stamp)
          },
          easeScrollY (y, t, stamp) {
            const rt = target(t)
            const event = {
              original: t,
              target: rt
            }
            global.cancelAnimationFrame(rt._isEasing)
            rt._easing = true
            if (!rt._ly) rt._ly = 0
            easeOut(rt, bounds(rt, -y) - rt._ly, t, event, stamp, 0.9, setValY)
          }
        },
        on: {
          touchstart: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.target = target(val.target)
              touchstart(val, val.target.parentNode.clientHeight, size(val))
            } : (val, stamp) => {
              val.target = target(val.target)
              touchstart(val, val.target.parentNode.clientWidth, size(val))
            }
          },
          touchmove: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.prevent = true
              val.original = val.target
              val.target = target(val.target)
              touchmove(val, stamp, setValY)
            } : (val, stamp) => {
              val.prevent = true
              val.original = val.target
              val.target = target(val.target)
              touchmove(val, stamp, setValX)
            }
          },
          touchend: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              touchend(val, stamp, setValY)
            } : (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              touchend(val, stamp, setValX)
            }
          },
          wheel: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              wheelY(val, stamp, size(val))
            } : (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              wheelX(val, stamp, size(val))
            }
          }
        }
      }, false)
    }
  }
}
<commit_msg>scroll<commit_after>const easingCache = 3
const easingFraction = 0.95
const easingAccelerator = 1.5
const quadraticEasing = 0.5
const abs = Math.abs

const bounds = (target, val) => {
  if (val < -target._height) {
    // target._easing = false
    return -target._height
  } else if (val > 0) {
    // target._easing = false
    return 0
  } else {
    return val
  }
}

const easeOut = (target, distance, original, event, stamp, easingFraction, setVal) => {
  if (target._easing) {
    // maybe stop when 2 ticks are same value
    target._ly = setVal(target, target._ly + distance * (1 - easingFraction), original, event, stamp)
    if (distance > 0.5 || distance < -0.5) {
      target._isEasing = global.requestAnimationFrame(() => easeOut(target, distance * easingFraction, original, event, stamp, easingFraction, setVal))
    } else {
      target._easing = false
    }
  }
}

const setValX = (target, val, original, event, stamp) => {
  val = bounds(target, val)
  target.style.transform = `translate3d(${val}rem, 0, 0)`
  if (!original) {
    original = target
  }
  if (original._ && original._._scrollListener) {
    original._._scrollListener({ x: val, target: original, scroller: target, state: event.state }, stamp)
  }
  return val
}

const setValY = (target, val, original, event, stamp) => {
  val = bounds(target, val)
  target.style.transform = `translate3d(0, ${val}rem, 0)`
  if (!original) {
    original = target
  }

  if (original._ && original._._scrollListener) {
    original._._scrollListener({ y: val, target: original, scroller: target, state: event.state }, stamp)
  }
  return val
}

const touchstart = ({ target, event }, x, y, ch, sh) => {
  global.cancelAnimationFrame(target._isEasing)

  if (ch >= sh) {
    target._block = true
    target.__init = false
    return true
  }

  target.__init = true
  target._start = y
  target._y = target._ly || 0
  target._easing = false
  target._prev = [ 1, 0, 0, 0 ]
  target._height = sh - ch
  target._block = ch >= sh
  target._sh = sh
  target._ey = y
  target._ex = x
}

const touchmove = (event, x, y, stamp, setVal) => {
  const target = event.target
  if (
    target._block ||
    abs(target._ey - y) <= abs(target._ex - x)
  ) {
    return true
  }
  event.prevent = true
  const original = event.original
  const index = target._prev[0]
  target._prev[0]++
  if (target._prev[0] > easingCache) {
    target._prev[0] = 1
  }
  const val = y - target._start + target._y
  target._prev[index] = val - (target._ly || 0)
  target._ly = setVal(target, val, original, event, stamp)
  target._ey = y
  target._ex = x
}

const touchend = (event, stamp, setVal) => {
  const target = event.target
  target.__init = false
  if (!target._prev) return
  const original = event.original
  var delta = 0
  var i = easingCache
  while (i--) {
    delta += target._prev[i] || 0
  }
  delta = (delta / easingCache) * easingAccelerator
  const sing = delta < 0 ? -1 : 1
  var distance = quadraticEasing * ((delta * delta) * sing)
  if (distance > 750) {
    distance = 750
  }
  target._easing = true
  easeOut(target, distance, original, event, stamp, easingFraction, setVal)
}

const lookupMode = [1.0, 28.0, 500.0]

const wheelX = (event, stamp, sh) => {
  const evt = event.event

  if ('deltaX' in evt) {
    if (abs(evt.deltaX) <= abs(evt.deltaY)) {
      return
    } else {
      event.prevent = true
      evt.preventDefault()
    }
    const mode = lookupMode[evt.deltaMode] || lookupMode[0]
    event.x = evt.deltaX * mode * -1
  } else {
    // is this ok on x?
    event.x = evt.wheelDelta / -3
  }

  const target = event.target
  if (!target.__init) {
    if (!target._ly) {
      target._ly = 0
    }
    if (touchstart(event, event.y, event.x, event.target.parentNode.clientWidth, sh)) {
      return
    }
  }
  clearTimeout(target._timeout)
  target._timeout = setTimeout(() => {
    if (target.offsetParent !== null) {
      target.__init = false
    }
  }, 20)
  target._ly = setValX(target, target._ly + event.x, event.original, event, stamp)
}

const wheelY = (event, stamp, sh) => {
  event.prevent = true
  const evt = event.event
  evt.preventDefault()
  if ('deltaY' in evt) {
    if (abs(evt.deltaX) >= abs(evt.deltaY)) {
      return
    } else {
      event.prevent = true
      evt.preventDefault()
    }
    const mode = lookupMode[evt.deltaMode] || lookupMode[0]
    event.y = evt.deltaY * mode * -1
  } else {
    event.y = evt.wheelDelta / -3
  }
  const target = event.target
  if (!target.__init) {
    if (!target._ly) {
      target._ly = 0
    }
    if (touchstart(event, event.x, event.y, event.target.parentNode.clientHeight, sh)) {
      return
    }
  }
  clearTimeout(target._timeout)
  target._timeout = setTimeout(() => {
    if (target.offsetParent !== null) {
      target.__init = false
    }
  }, 20)
  target._ly = setValY(target, target._ly + event.y, event.original, event, stamp)
}

export default {
  props: {
    scroll: (t, val) => {
      var fn, target, size, direction

      if (!val) return
      if (typeof val === 'function') {
        fn = val
      } else if (typeof val === 'object') {
        if (val.target) target = val.target
        fn = val.onScroll
        direction = val.direction
        size = val.size
      }

      t.set({
        define: {
          hasScrollY: true
        }
      }, false)

      if (fn) {
        t.set({
          define: {
            _scrollListener: fn
          }
        }, false)
      }

      if (!direction) {
        direction = 'y'
      }

      if (!size) {
        size = direction === 'y'
          ? (val) => val.target.scrollHeight
          : (val) => val.target.scrollWidth
      }

      if (!target) {
        target = t => t
      }

      t.set({
        define: {
          setScroll (y, t, stamp) {
            const rt = target(t)
            global.cancelAnimationFrame(rt._isEasing)
            if (!rt._ly) rt._ly = 0
            rt._ly = setValY(rt, bounds(rt, -y) - rt._ly, t, void 0, stamp)
          },
          easeScroll (y, t, stamp) {
            const rt = target(t)
            const event = {
              original: t,
              target: rt,
              state: t._s
            }
            global.cancelAnimationFrame(rt._isEasing)
            rt._easing = true
            if (!rt._ly) rt._ly = 0

            // console.log(bounds)
            easeOut(rt, bounds(rt, -y) - rt._ly, t, event, stamp, 0.9, direction === 'y' ? setValY : setValX)
          }
        },
        on: {
          touchstart: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.target = target(val.target)
              touchstart(val, val.x, val.y, val.target.parentNode.clientHeight, size(val))
            } : (val, stamp) => {
              val.target = target(val.target)
              touchstart(val, val.y, val.x, val.target.parentNode.clientWidth, size(val))
            }
          },
          touchmove: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              touchmove(val, val.x, val.y, stamp, setValY)
            } : (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              touchmove(val, val.y, val.x, stamp, setValX)
            }
          },
          touchend: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              touchend(val, stamp, setValY)
            } : (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              touchend(val, stamp, setValX)
            }
          },
          wheel: {
            scroll: direction === 'y' ? (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              wheelY(val, stamp, size(val))
            } : (val, stamp) => {
              val.original = val.target
              val.target = target(val.target)
              wheelX(val, stamp, size(val))
            }
          }
        }
      }, false)
    }
  }
}
<|endoftext|>


<commit_before>/* eslint-disable no-unused-vars */
import React from 'react';
import styled from 'styled-components';
import { Row } from '../';
import { divvy, media, passOn } from '../utils';

function ColumnContainer(props) {
  const { children, debug, divisions, fluid, xs, sm, md, lg,
    xsShift, smShift, mdShift, lgShift,
    ...rest } = props;
  // Pass the debug prop on to the children.
  const newChildren = passOn(children, [Row, Column], { debug: debug });
  return
{newChildren}
;
}

ColumnContainer.propTypes = {
  className: React.PropTypes.string,
  debug: React.PropTypes.bool,
  divisions: React.PropTypes.number,
  fluid: React.PropTypes.bool,
  xs: React.PropTypes.number,
  sm: React.PropTypes.number,
  md: React.PropTypes.number,
  lg: React.PropTypes.number,
  xsShift: React.PropTypes.number,
  smShift: React.PropTypes.number,
  mdShift: React.PropTypes.number,
  lgShift: React.PropTypes.number
};

ColumnContainer.defaultProps = {
  debug: false,
  divisions: 12
};

const Column = styled(ColumnContainer)`
  display: block;
  ${props => props.debug ? `background-color: rgba(50, 50, 255, .1);
  border: 1px solid #fff;` : ''}
  box-sizing: border-box;
  ${props =>
    props.fluid ? 'padding: 0;' : 'padding: 20px;'
  }
  width: 100%;
  ${props =>
    props.xs
      ? `width: ${divvy(props.divisions) * props.xs}%;`
      : null
  }
  ${props =>
    props.xsShift
      ? `margin-left: ${divvy(props.divisions) * props.xsShift}%;`
      : null
  }
  ${media.sm`
    ${props =>
      props.sm
        ? `width: ${divvy(props.divisions) * props.sm}%;`
        : null
    }
    ${props => props.smShift
        ? `margin-left: ${divvy(props.divisions) * props.smShift}%;`
        : null
    }
  `}
  ${media.md`
    ${props =>
      props.md
        ? `width: ${divvy(props.divisions) * props.md}%;`
        : null
    }
    ${props =>
      props.mdShift
        ? `margin-left: ${divvy(props.divisions) * props.mdShift}%;`
        : null
    }
  `}
  ${media.lg`
    ${props =>
      props.lg
        ? `width: ${divvy(props.divisions) * props.lg}%;`
        : null
    }
    ${props =>
      props.lgShift
        ? `margin-left: ${divvy(props.divisions) * props.lgShift}%;`
        : null
    }
  `}
`;

export default Column;

<commit_msg>Fixed eslint errors<commit_after>/* eslint-disable no-unused-vars */
import React from 'react';
import styled from 'styled-components';
import { Row } from '../';
import { divvy, media, passOn } from '../utils';

function ColumnContainer(props) {
  const { children, debug, divisions, fluid, xs, sm, md, lg,
    xsShift, smShift, mdShift, lgShift,
    ...rest } = props;
  // Pass the debug prop on to the children.
  const newChildren = passOn(children, [Row], { debug });
  return
{newChildren}
;
}

ColumnContainer.propTypes = {
  children: React.PropTypes.node,
  className: React.PropTypes.string,
  debug: React.PropTypes.bool,
  divisions: React.PropTypes.number,
  fluid: React.PropTypes.bool,
  xs: React.PropTypes.number,
  sm: React.PropTypes.number,
  md: React.PropTypes.number,
  lg: React.PropTypes.number,
  xsShift: React.PropTypes.number,
  smShift: React.PropTypes.number,
  mdShift: React.PropTypes.number,
  lgShift: React.PropTypes.number
};

ColumnContainer.defaultProps = {
  debug: false,
  divisions: 12
};

const Column = styled(ColumnContainer)`
  display: block;
  ${props => props.debug ? `background-color: rgba(50, 50, 255, .1);
  border: 1px solid #fff;` : ''}
  box-sizing: border-box;
  ${props =>
    props.fluid ? 'padding: 0;' : 'padding: 20px;'
  }
  width: 100%;
  ${props =>
    props.xs
      ? `width: ${divvy(props.divisions) * props.xs}%;`
      : null
  }
  ${props =>
    props.xsShift
      ? `margin-left: ${divvy(props.divisions) * props.xsShift}%;`
      : null
  }
  ${media.sm`
    ${props =>
      props.sm
        ? `width: ${divvy(props.divisions) * props.sm}%;`
        : null
    }
    ${props => props.smShift
        ? `margin-left: ${divvy(props.divisions) * props.smShift}%;`
        : null
    }
  `}
  ${media.md`
    ${props =>
      props.md
        ? `width: ${divvy(props.divisions) * props.md}%;`
        : null
    }
    ${props =>
      props.mdShift
        ? `margin-left: ${divvy(props.divisions) * props.mdShift}%;`
        : null
    }
  `}
  ${media.lg`
    ${props =>
      props.lg
        ? `width: ${divvy(props.divisions) * props.lg}%;`
        : null
    }
    ${props =>
      props.lgShift
        ? `margin-left: ${divvy(props.divisions) * props.lgShift}%;`
        : null
    }
  `}
`;

export default Column;

<|endoftext|>


<commit_before>$(document).ready(function(){
  var $this = $(this);
  var matches = [];
  var $cards = $(".card.col6");

    $("div.card").on("click", function(event) {
      if (!$(this).hasClass("disabled") ){
        $(this).addClass("chosen");
        $(this).find(".value").fadeOut();
        $(this).find(".question").fadeIn();
        matches.push($(this).find(".answer").text().trim());
        $(this).addClass("disabled");
        if (matches[0]===matches[1]){
          $cards.filter(".disabled").addClass("played")
          $cards.filter(".disabled").removeClass("disabled")
          matches.pop()
          matches.pop()
          alert(matches)
        }
        else if(matches.length===2 && matches[0] !== matches[1]){
          $cards.filter(".disabled").find(".value")
          //.fadeIn().find(".question").fadeOut()
          alert("hi")
          $cards.filter(".disabled").removeClass("disabled").removeClass("chosen")
        }

       //.find(".question").fadeOut()

        //$cards.filter(".disabled and chosen").remove disabled remove chosen fade in
        //fade out value
        // else{
        //   //$(this).removeClass("disabled");
        //   //$cards.filter(".disabled").addClass(".value")
        //   matches.pop()
        //   matches.pop()

        // }

    }});
});
// $(document).ready(function(){

//     var $this = $(this);

//   //toggle text
//   $("div.card").on("click", function(event) {
//       if (!$(this).hasClass("disabled")){
//         alert("hi")}
//         // ;
//         // ;
//         // $(this).addClass("disabled");
//         //save card
//         //if no match, removed disabled
//         //if yes match, then do something else, ie win case
//     }
//   });

//   //make + and - buttons work
// });

//logic
//if <commit_msg>add working matches<commit_after>$(document).ready(function(){
  var $this = $(this);
  var matches = [];
  var $cards = $(".card.col6");

    $("div.card").on("click", function(event) {
      if (!$(this).hasClass("disabled") ){
        $(this).addClass("chosen");
        $(this).find(".value").fadeOut();
        $(this).find(".question").fadeIn();
        matches.push($(this).find(".answer").text().trim());
        $(this).addClass("disabled");
        if (matches[0]===matches[1]){
          $cards.filter(".disabled").addClass("played")
          $cards.filter(".disabled").removeClass("disabled")
          matches.pop()
          matches.pop()
          alert("you got a match!")
        }
        else if(matches.length===2 && matches[0] !== matches[1]){
          $cards.filter(".disabled").find(".value")
          //.fadeIn().find(".question").fadeOut()
          $cards.filter(".disabled").removeClass("chosen")
          matches.pop()
          matches.pop()
        }

       //.find(".question").fadeOut()

        //$cards.filter(".disabled and chosen").remove disabled remove chosen fade in
        //fade out value
        // else{
        //   //$(this).removeClass("disabled");
        //   //$cards.filter(".disabled").addClass(".value")
        //   matches.pop()
        //   matches.pop()

        // }

    }});
});
// $(document).ready(function(){

//     var $this = $(this);

//   //toggle text
//   $("div.card").on("click", function(event) {
//       if (!$(this).hasClass("disabled")){
//         alert("hi")}
//         // ;
//         // ;
//         // $(this).addClass("disabled");
//         //save card
//         //if no match, removed disabled
//         //if yes match, then do something else, ie win case
//     }
//   });

//   //make + and - buttons work
// });

//logic
//if <|endoftext|>


<commit_before>/*jslint node:true, nomen:true */
"use strict";

module.exports = function (grunt) {
  var http = require('http');
  var path = require('path');
  var fs = require('fs-extra');
  var glob = require('glob');
  var pkg = require('../package.json');
  var activeReporters = {};

  grunt.registerMultiTask('build-test-addon', pkg.description, function() {
    var name = this.target;

    var ctx = this.options({
      template: __dirname + '/../tasks/jasmine-firefoxaddon/',
      resources: undefined,
      helpers: undefined,
      timeout : 10000,
      port: 9989,
      stayOpen: false
    });

    if (!this.data.files) {
      grunt.log.error('No app target provided.');
      return false;
    }

    for (var target in this.data.files) {
      if (this.data.files.hasOwnProperty(target)) {
        ctx.target = target;
        ctx.files = this.data.files[target];
        buildSpec(ctx);
      }
    }

    if (!activeReporters[ctx.port]) {
      var Web = function() {
        var my = this;
        this.messages = [];
        this.ip = "";
        this.server = http.createServer(function (req, res) {
          if (req.url === '/') {
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.end('' +
                    'Hi, App should also load.' +
                    '');
          } else if (req.url === '/put') {
            req.setEncoding('utf8');
            req.on('data', function(chunk) {
              my.ip += chunk;
            });
            req.on('end', function() {
              my.messages.push(JSON.parse(new Buffer(my.ip, 'base64').toString('ascii')));
              grunt.log.write('- ' + my.messages[my.messages.length - 1].fullName + '\n');
              my.ip = '';
              res.end('{}');
            });
          } else if (req.url === '/ready') {
            grunt.log.writeln('Done.');
            res.end('{}');
          }
        }).listen(ctx.port);
      };
      activeReporters[ctx.port] = new Web();
    }

    // Configure downstream jpm run to use the generated addon.
    if (!grunt.config.get('jpm')) {
      grunt.config.set('jpm', {
        options: {
          src: './.build/',
          xpi: './.build/'
        }
      });
    }
    var divider = new RegExp('^.*HERE$', 'm');
    var indexfile = grunt.file.read('.build/index.js');
    var indexout = indexfile.split(divider)[0];
    var specfile = grunt.file.read('.build/spec.js');
    var specout = specfile.split(divider)[0];
    // Add the helpers to the spec and index files and copy them into addon
    grunt.file.expand(grunt.config.get('jasmine_firefoxaddon').helpers)
      .forEach(function (helper) {
        grunt.file.copy(helper, '.build/data/' + helper);
        // NOTE - hardcoding the path because self.data.url not visible in spec
        // This path is set in tasks/jasmine-firefoxaddon/package.json
        indexout += 'Cu.import(self.data.url(' + helper + '))';
        specout += '\nComponents.utils.import("' +
          'resource://jid1-mkagayemb0e5nq-at-jetpack/data/' + helper + '");';
      });
    // Add actual tests to spec file
    grunt.file.expand(grunt.config.get('jasmine_firefoxaddon').tests)
      .forEach(function (test) {
        specout += '\nrequire("../' + test + '");';
      });
    specout += specfile.split(divider)[1];
    grunt.file.write('.build/spec.js', specout);
    // Copy the source files
    grunt.file.expand(grunt.config.get('jasmine_firefoxaddon').resources)
      .forEach(function (resource) {
        grunt.file.copy(resource, '.build/data/' + resource);
      });
    return true;
  });

  grunt.config.set('build-test-addon', {
    main: {
      files: {
        '.build': [ 'spec.jsm' ]
      },
      options: {
        helper: [
          //{path: 'freedom-for-firefox.jsm', include: false},
          //{path: freedomPrefix + '/providers', name: 'providers', include: false},
          //{path: freedomPrefix + '/spec', name: 'spec', include: false}
        ]
      }
    }
  });

  grunt.loadNpmTasks('grunt-jpm');
  if (!grunt.config.get('jpm')) {
    grunt.config.set('jpm', {
      options: {
        src: './.build/',
        xpi: './.build/'
      }
    });
  }
  grunt.loadNpmTasks('grunt-browserify');
  grunt.config.merge({
    browserify: {
      jasmine: {
        files: {
          '.build/data/spec.jsm': ['.build/spec.js']
        }
      }
    }
  });

  grunt.registerTask('report-tests', pkg.description, function() {
    var failures = 0;
    for (var port in activeReporters) {
      if (activeReporters[port].messages.length) {
        activeReporters[port].messages.forEach(function(msg) {
          if (!msg.fullName) {
            return;
          }
          if (msg.status === 'passed') {
            grunt.log.ok(msg.fullName, msg.status);
            if (msg.failedExpectations) {
              msg.failedExpectations.forEach(function(exp) {
                grunt.log.warn(exp.message);
              });
            }
          } else if (msg.status === 'failed') {
            grunt.log.error(msg.fullName, msg.status);
            if (msg.failedExpectations) {
              msg.failedExpectations.forEach(function(exp) {
                grunt.log.warn(exp.message);
              });
            }
            failures += 1;
          } else if (msg.status === 'pending') {
            grunt.log.write('>> ' + msg.fullName).subhead(msg.status);
          } else {
            grunt.log.warn(msg.fullName);
            console.error(msg);
            failures += 1;
          }
        });
      }
    }
    grunt.file.setBase('../');  // to delete plugin, need to cd up
    grunt.file['delete']('.build');
    if (failures) {
      return false;
    } else {
      return true;
    }
  });

  grunt.registerTask('jasmine_firefoxaddon', ['build-test-addon',
                                              'browserify:jasmine',
                                              'jpm:run',
                                              'report-tests:fromBuild']);

  function getFiles(specs) {
    var out = [];
    if (specs instanceof Array) {
      specs.forEach(function(spec) {
        out = out.concat(getFiles(spec));
      });
    } else if (specs.path) {
      out = glob.sync(specs.path).map(function(path) {
        return {
          path: path,
          include: specs.include,
          name: specs.name || path
        };
      });
    } else {
      out = glob.sync(specs);
    }
    return out;
  }

  function buildSpec(ctx) {
    ctx.dir = fs.mkdirpSync(ctx.target) || fs.realpathSync(ctx.target);

    var scripts = getFiles(ctx.files);
    var toLink = "";

    fs.mkdirpSync(ctx.dir + '/data');
    for (var i = 0; i < scripts.length; i++) {
      var s = scripts[i];
      var spath = s.path || s;
      var sname = s.name || s;
      if (!s.path || s.include) {
        toLink += "underTest = self.data.url('" + sname + "');\n";
      }
      var parent = path.dirname(sname);
      fs.mkdirpSync(ctx.dir + '/data/' + parent);
      fs.copySync(spath, ctx.dir + '/data/' + sname);
    }
    toLink += "finishLoad();";
    if (ctx.stayOpen) {
      toLink += "\nstayOpen = true;";
    }
    var buffer = new Buffer(toLink);

    fs.copySync(ctx.template, ctx.dir);
    var fd = fs.openSync(ctx.dir + '/index.js', 'a');
    fs.writeSync(fd, buffer, 0, buffer.length, null);
    grunt.log.ok('Extension staged in ' + ctx.target);
  }
};
<commit_msg>actually import helpers into index scope<commit_after>/*jslint node:true, nomen:true */
"use strict";

module.exports = function (grunt) {
  var http = require('http');
  var path = require('path');
  var fs = require('fs-extra');
  var glob = require('glob');
  var pkg = require('../package.json');
  var activeReporters = {};

  grunt.registerMultiTask('build-test-addon', pkg.description, function() {
    var name = this.target;

    var ctx = this.options({
      template: __dirname + '/../tasks/jasmine-firefoxaddon/',
      resources: undefined,
      helpers: undefined,
      timeout : 10000,
      port: 9989,
      stayOpen: false
    });

    if (!this.data.files) {
      grunt.log.error('No app target provided.');
      return false;
    }

    for (var target in this.data.files) {
      if (this.data.files.hasOwnProperty(target)) {
        ctx.target = target;
        ctx.files = this.data.files[target];
        buildSpec(ctx);
      }
    }

    if (!activeReporters[ctx.port]) {
      var Web = function() {
        var my = this;
        this.messages = [];
        this.ip = "";
        this.server = http.createServer(function (req, res) {
          if (req.url === '/') {
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.end('' +
                    'Hi, App should also load.' +
                    '');
          } else if (req.url === '/put') {
            req.setEncoding('utf8');
            req.on('data', function(chunk) {
              my.ip += chunk;
            });
            req.on('end', function() {
              my.messages.push(JSON.parse(new Buffer(my.ip, 'base64').toString('ascii')));
              grunt.log.write('- ' + my.messages[my.messages.length - 1].fullName + '\n');
              my.ip = '';
              res.end('{}');
            });
          } else if (req.url === '/ready') {
            grunt.log.writeln('Done.');
            res.end('{}');
          }
        }).listen(ctx.port);
      };
      activeReporters[ctx.port] = new Web();
    }

    // Configure downstream jpm run to use the generated addon.
    if (!grunt.config.get('jpm')) {
      grunt.config.set('jpm', {
        options: {
          src: './.build/',
          xpi: './.build/'
        }
      });
    }
    var divider = new RegExp('^.*HERE$', 'm');
    var indexfile = grunt.file.read('.build/index.js');
    var indexout = indexfile.split(divider)[0];
    var specfile = grunt.file.read('.build/spec.js');
    var specout = specfile.split(divider)[0];
    // Add the helpers to the spec and index files and copy them into addon
    grunt.file.expand(grunt.config.get('jasmine_firefoxaddon').helpers)
      .forEach(function (helper) {
        grunt.file.copy(helper, '.build/data/' + helper);
        // NOTE - hardcoding the path because self.data.url not visible in spec
        // This path is set in tasks/jasmine-firefoxaddon/package.json
        indexout += 'Cu.import(self.data.url("' + helper + '"))';
        specout += '\nComponents.utils.import("' +
          'resource://jid1-mkagayemb0e5nq-at-jetpack/data/' + helper + '");';
      });
    // Add actual tests to spec file
    grunt.file.expand(grunt.config.get('jasmine_firefoxaddon').tests)
      .forEach(function (test) {
        specout += '\nrequire("../' + test + '");';
      });
    indexout += indexfile.split(divider)[1];
    specout += specfile.split(divider)[1];
    grunt.file.write('.build/index.js', indexout);
    grunt.file.write('.build/spec.js', specout);
    // Copy the source files
    grunt.file.expand(grunt.config.get('jasmine_firefoxaddon').resources)
      .forEach(function (resource) {
        grunt.file.copy(resource, '.build/data/' + resource);
      });
    return true;
  });

  grunt.config.set('build-test-addon', {
    main: {
      files: {
        '.build': [ 'spec.jsm' ]
      },
      options: {
        helper: [
          //{path: 'freedom-for-firefox.jsm', include: false},
          //{path: freedomPrefix + '/providers', name: 'providers', include: false},
          //{path: freedomPrefix + '/spec', name: 'spec', include: false}
        ]
      }
    }
  });

  grunt.loadNpmTasks('grunt-jpm');
  if (!grunt.config.get('jpm')) {
    grunt.config.set('jpm', {
      options: {
        src: './.build/',
        xpi: './.build/'
      }
    });
  }
  grunt.loadNpmTasks('grunt-browserify');
  grunt.config.merge({
    browserify: {
      jasmine: {
        files: {
          '.build/data/spec.jsm': ['.build/spec.js']
        }
      }
    }
  });

  grunt.registerTask('report-tests', pkg.description, function() {
    var failures = 0;
    for (var port in activeReporters) {
      if (activeReporters[port].messages.length) {
        activeReporters[port].messages.forEach(function(msg) {
          if (!msg.fullName) {
            return;
          }
          if (msg.status === 'passed') {
            grunt.log.ok(msg.fullName, msg.status);
            if (msg.failedExpectations) {
              msg.failedExpectations.forEach(function(exp) {
                grunt.log.warn(exp.message);
              });
            }
          } else if (msg.status === 'failed') {
            grunt.log.error(msg.fullName, msg.status);
            if (msg.failedExpectations) {
              msg.failedExpectations.forEach(function(exp) {
                grunt.log.warn(exp.message);
              });
            }
            failures += 1;
          } else if (msg.status === 'pending') {
            grunt.log.write('>> ' + msg.fullName).subhead(msg.status);
          } else {
            grunt.log.warn(msg.fullName);
            console.error(msg);
            failures += 1;
          }
        });
      }
    }
    grunt.file.setBase('../');  // to delete plugin, need to cd up
    grunt.file['delete']('.build');
    if (failures) {
      return false;
    } else {
      return true;
    }
  });

  grunt.registerTask('jasmine_firefoxaddon', ['build-test-addon',
                                              'browserify:jasmine',
                                              'jpm:run',
                                              'report-tests:fromBuild']);

  function getFiles(specs) {
    var out = [];
    if (specs instanceof Array) {
      specs.forEach(function(spec) {
        out = out.concat(getFiles(spec));
      });
    } else if (specs.path) {
      out = glob.sync(specs.path).map(function(path) {
        return {
          path: path,
          include: specs.include,
          name: specs.name || path
        };
      });
    } else {
      out = glob.sync(specs);
    }
    return out;
  }

  function buildSpec(ctx) {
    ctx.dir = fs.mkdirpSync(ctx.target) || fs.realpathSync(ctx.target);

    var scripts = getFiles(ctx.files);
    var toLink = "";

    fs.mkdirpSync(ctx.dir + '/data');
    for (var i = 0; i < scripts.length; i++) {
      var s = scripts[i];
      var spath = s.path || s;
      var sname = s.name || s;
      if (!s.path || s.include) {
        toLink += "underTest = self.data.url('" + sname + "');\n";
      }
      var parent = path.dirname(sname);
      fs.mkdirpSync(ctx.dir + '/data/' + parent);
      fs.copySync(spath, ctx.dir + '/data/' + sname);
    }
    toLink += "finishLoad();";
    if (ctx.stayOpen) {
      toLink += "\nstayOpen = true;";
    }
    var buffer = new Buffer(toLink);

    fs.copySync(ctx.template, ctx.dir);
    var fd = fs.openSync(ctx.dir + '/index.js', 'a');
    fs.writeSync(fd, buffer, 0, buffer.length, null);
    grunt.log.ok('Extension staged in ' + ctx.target);
  }
};
<|endoftext|>