<commit_before>var socketIo = require('socket.io');
var command = require('./command');
var wrapper = require('./wrapper');
var api = require('./api');

var DEFAULT_TIMEOUT = 10 * 1000;

function deepClone(base) {
  if (!base || typeof base != 'object')
    return base;

  var cloned = Object.create(null);
  Object.keys(base).forEach(function(key) {
    cloned[key] = deepClone(base[key]);
  });
  return cloned;
}

function sanitizeBackendMessage(envelope) {
  return {
    statusCode: envelope.statusCode,
    body: deepClone(envelope.body)
  };
}
exports.sanitizeBackendMessage = sanitizeBackendMessage;

exports.register = function(application, server, params) {
  params = params || {};
  var connection = params.connection;
  if (!connection)
    throw new Error('Connection to the backend is required!');

  if (!server)
    throw new Error('A server instance is required!');

  function createClientMessageHandler(commandName, commandDefinition, socket, handlerOptions) {
    handlerOptions = handlerOptions || {};

    var defaultParameters = {};

    var droongaMessageOptions = {
      dataset:   commandDefinition.dataset,
      timeout:   DEFAULT_TIMEOUT,
      sessionId: socket.id
    };

    var requestHandler  = 'onRequest';
    var responseHandler = 'onResponse';
    if (command.PublishSubscribe.isInstance(commandDefinition))  {
      if (/\.subscribe$/.test(commandName)) {
        requestHandler  = 'onSubscribe';
        responseHandler = 'onSubscribed';
      } else if (/\.unsubscribe$/.test(commandName)) {
        requestHandler  = 'onUnsubscribe';
        responseHandler = 'onUnsubscribed';
      }
    }

    return (function(data, clientOptions) {
      clientOptions = clientOptions || {};

      var callback = function(error, envelope) {
        if (error) {
          socket.emit('error', error);
          return;
        }

        var responseEvent = envelope.type;
        var responseData = envelope.body;
        var options = {};
        if (clientOptions.responseEvent) {
          responseEvent = clientOptions.responseEvent;
          options.event = responseEvent;
        }

        if (handlerOptions.onResponse && typeof handlerOptions.onResponse == 'function')
          handlerOptions.onResponse(data, responseData);

        var wrappedSocket = new wrapper.SocketIOClientSocketWrapper(socket, options);
        if (commandDefinition[responseHandler]) {
          try {
            commandDefinition[responseHandler](responseData, wrappedSocket);
          } catch(error) {
            wrappedSocket.emit('error', error.message || error);
          }
        } else {
          wrappedSocket.emit(responseEvent, responseData);
          wrappedSocket.destroy();
        }
      };

      var wrappedConnection = new wrapper.DroongaProtocolConnectionWrapper(connection, callback, droongaMessageOptions);
      if (commandDefinition[requestHandler]) {
        try {
          commandDefinition[requestHandler](data, wrappedConnection);
        } catch(error) {
          wrappedConnection.destroy();
          socket.emit('error', error.message || error);
        }
      } else {
        wrappedConnection.emit(commandName, data);
      }
    });
  }

  function createErrorHandler(socket) {
    return (function(error) {
      socket.emit('error', error);
    });
  }

  var allSubscribers = {};
  function createNotificationHandler(notificationEvent, commandDefinition) {
    return (function(envelope) {
      var subscriberIds = envelope.to;
      if (!Array.isArray(subscriberIds))
        subscriberIds = [subscriberIds];

      var subscribers = allSubscribers[notificationEvent] || {};
      subscriberIds.forEach(function(subscriberId) {
        subscriberSocket = subscribers[subscriberId];
        subscriberSocket = subscriberSocket && subscriberSocket.socket;
        if (!subscriberSocket)
          return;
        try {
          if (commandDefinition.onNotify) {
            try {
              commandDefinition.onNotify(envelope.body, subscriberSocket);
            } catch(error) {
              subscriberSocket.emit('error', error.message || error);
            }
          } else {
            subscriberSocket.emit(notificationEvent, envelope.body);
          }
        } catch(exception) {
          console.log(exception + '\n' + exception.stack);
        }
      });
    });
  }

  var commandSets = api.normalize(params.plugins);

  var unifiedCommandSet = {};
  commandSets.forEach(function(commandSet) {
    Object.keys(commandSet).forEach(function(commandName) {
      var definition = commandSet[commandName];
      if (!command.SocketCommand.isInstance(definition))
        return;
      unifiedCommandSet[commandName] = definition;
    });
  });

  var registeredCommands = [];
  Object.keys(unifiedCommandSet).forEach(function(commandName) {
    var definition = unifiedCommandSet[commandName];
    if (!command.SocketCommand.isInstance(definition))
      return;
    registeredCommands.push({ name:       commandName,
                              definition: definition });
    if (definition.notification)
      connection.on(definition.notification,
                    createNotificationHandler(definition.notification,
                                              definition));
  });

  var io = socketIo.listen(server);
  io.sockets.on('connection', function(socket) {
    application.emit('connection', socket);

    var subscriberId = connection.getRouteToSelf({ sessionId: socket.id });
    subscriberId = command.sha1sum(subscriberId);

    registeredCommands.forEach(function(registeredCommand) {
      if (command.PublishSubscribe.isInstance(registeredCommand.definition)) {
        var subscribeEvent = registeredCommand.name + '.subscribe';
        var unsubscribeEvent = registeredCommand.name + '.unsubscribe';

        unifiedCommandSet[subscribeEvent] =
          unifiedCommandSet[unsubscribeEvent] = registeredCommand.definition;

        var notificationEvent = registeredCommand.definition.notification;
        var subscribers = allSubscribers[notificationEvent] || {};
        allSubscribers[notificationEvent] = subscribers;

        var subscribeHandler = createClientMessageHandler(
            subscribeEvent,
            registeredCommand.definition,
            socket,
            {
              onResponse: function(subscribeMessageBody, responseBody) {
                if (!subscribers[subscriberId]) {
                  var subscriber = {
                    socket:      socket,
                    unsubscribe: function() {
                      unsubscribeHandler(unsubscribeEvent, subscribeMessageBody);
                    }
                  };
                  subscribers[subscriberId] = subscriber
                }
              }
            }
        );
        socket.on(subscribeEvent, subscribeHandler);

        var unsubscribeHandler = createClientMessageHandler(
            unsubscribeEvent,
            registeredCommand.definition,
            socket,
            {
              onResponse: function(unsubscribeMessageBody, responseBody) {
                if (subscribers[subscriberId])
                  delete subscribers[subscriberId];
              }
            }
        );
        socket.on(unsubscribeEvent, unsubscribeHandler);
      } else {
        socket.on(registeredCommand.name,
                  createClientMessageHandler(registeredCommand.name,
                                             registeredCommand.definition,
                                             socket));
      }
    });

    var errorHandler = createErrorHandler(socket);
    connection.on('error', errorHandler);

    socket.on('disconnect', function() {
      Object.keys(allSubscribers).forEach(function(notificationEvent) {
        var subscribers = allSubscribers[notificationEvent];
        if (subscribers && subscribers[subscriberId]) {
          subscribers[subscriberId].unsubscribe();
          delete subscribers[subscriberId];
        }
      });
      connection.removeListener('error', errorHandler);
      socket.removeAllListeners();
    });
  });

  return registeredCommands;
}
<commit_msg>Remove trailing spaces<commit_after>var socketIo = require('socket.io');
var command = require('./command');
var wrapper = require('./wrapper');
var api = require('./api');

var DEFAULT_TIMEOUT = 10 * 1000;

function deepClone(base) {
  if (!base || typeof base != 'object')
    return base;

  var cloned = Object.create(null);
  Object.keys(base).forEach(function(key) {
    cloned[key] = deepClone(base[key]);
  });
  return cloned;
}

function sanitizeBackendMessage(envelope) {
  return {
    statusCode: envelope.statusCode,
    body: deepClone(envelope.body)
  };
}
exports.sanitizeBackendMessage = sanitizeBackendMessage;

exports.register = function(application, server, params) {
  params = params || {};
  var connection = params.connection;
  if (!connection)
    throw new Error('Connection to the backend is required!');

  if (!server)
    throw new Error('A server instance is required!');

  function createClientMessageHandler(commandName, commandDefinition, socket, handlerOptions) {
    handlerOptions = handlerOptions || {};

    var defaultParameters = {};

    var droongaMessageOptions = {
      dataset:   commandDefinition.dataset,
      timeout:   DEFAULT_TIMEOUT,
      sessionId: socket.id
    };

    var requestHandler  = 'onRequest';
    var responseHandler = 'onResponse';
    if (command.PublishSubscribe.isInstance(commandDefinition))  {
      if (/\.subscribe$/.test(commandName)) {
        requestHandler  = 'onSubscribe';
        responseHandler = 'onSubscribed';
      } else if (/\.unsubscribe$/.test(commandName)) {
        requestHandler  = 'onUnsubscribe';
        responseHandler = 'onUnsubscribed';
      }
    }

    return (function(data, clientOptions) {
      clientOptions = clientOptions || {};

      var callback = function(error, envelope) {
        if (error) {
          socket.emit('error', error);
          return;
        }

        var responseEvent = envelope.type;
        var responseData = envelope.body;
        var options = {};
        if (clientOptions.responseEvent) {
          responseEvent = clientOptions.responseEvent;
          options.event = responseEvent;
        }

        if (handlerOptions.onResponse && typeof handlerOptions.onResponse == 'function')
          handlerOptions.onResponse(data, responseData);

        var wrappedSocket = new wrapper.SocketIOClientSocketWrapper(socket, options);
        if (commandDefinition[responseHandler]) {
          try {
            commandDefinition[responseHandler](responseData, wrappedSocket);
          } catch(error) {
            wrappedSocket.emit('error', error.message || error);
          }
        } else {
          wrappedSocket.emit(responseEvent, responseData);
          wrappedSocket.destroy();
        }
      };

      var wrappedConnection = new wrapper.DroongaProtocolConnectionWrapper(connection, callback, droongaMessageOptions);
      if (commandDefinition[requestHandler]) {
        try {
          commandDefinition[requestHandler](data, wrappedConnection);
        } catch(error) {
          wrappedConnection.destroy();
          socket.emit('error', error.message || error);
        }
      } else {
        wrappedConnection.emit(commandName, data);
      }
    });
  }

  function createErrorHandler(socket) {
    return (function(error) {
      socket.emit('error', error);
    });
  }

  var allSubscribers = {};
  function createNotificationHandler(notificationEvent, commandDefinition) {
    return (function(envelope) {
      var subscriberIds = envelope.to;
      if (!Array.isArray(subscriberIds))
        subscriberIds = [subscriberIds];

      var subscribers = allSubscribers[notificationEvent] || {};
      subscriberIds.forEach(function(subscriberId) {
        subscriberSocket = subscribers[subscriberId];
        subscriberSocket = subscriberSocket && subscriberSocket.socket;
        if (!subscriberSocket)
          return;
        try {
          if (commandDefinition.onNotify) {
            try {
              commandDefinition.onNotify(envelope.body, subscriberSocket);
            } catch(error) {
              subscriberSocket.emit('error', error.message || error);
            }
          } else {
            subscriberSocket.emit(notificationEvent, envelope.body);
          }
        } catch(exception) {
          console.log(exception + '\n' + exception.stack);
        }
      });
    });
  }

  var commandSets = api.normalize(params.plugins);

  var unifiedCommandSet = {};
  commandSets.forEach(function(commandSet) {
    Object.keys(commandSet).forEach(function(commandName) {
      var definition = commandSet[commandName];
      if (!command.SocketCommand.isInstance(definition))
        return;
      unifiedCommandSet[commandName] = definition;
    });
  });

  var registeredCommands = [];
  Object.keys(unifiedCommandSet).forEach(function(commandName) {
    var definition = unifiedCommandSet[commandName];
    if (!command.SocketCommand.isInstance(definition))
      return;
    registeredCommands.push({ name:       commandName,
                              definition: definition });
    if (definition.notification)
      connection.on(definition.notification,
                    createNotificationHandler(definition.notification,
                                              definition));
  });

  var io = socketIo.listen(server);
  io.sockets.on('connection', function(socket) {
    application.emit('connection', socket);

    var subscriberId = connection.getRouteToSelf({ sessionId: socket.id });
    subscriberId = command.sha1sum(subscriberId);

    registeredCommands.forEach(function(registeredCommand) {
      if (command.PublishSubscribe.isInstance(registeredCommand.definition)) {
        var subscribeEvent = registeredCommand.name + '.subscribe';
        var unsubscribeEvent = registeredCommand.name + '.unsubscribe';

        unifiedCommandSet[subscribeEvent] =
          unifiedCommandSet[unsubscribeEvent] = registeredCommand.definition;

        var notificationEvent = registeredCommand.definition.notification;
        var subscribers = allSubscribers[notificationEvent] || {};
        allSubscribers[notificationEvent] = subscribers;

        var subscribeHandler = createClientMessageHandler(
            subscribeEvent,
            registeredCommand.definition,
            socket,
            {
              onResponse: function(subscribeMessageBody, responseBody) {
                if (!subscribers[subscriberId]) {
                  var subscriber = {
                    socket:      socket,
                    unsubscribe: function() {
                      unsubscribeHandler(unsubscribeEvent, subscribeMessageBody);
                    }
                  };
                  subscribers[subscriberId] = subscriber
                }
              }
            }
        );
        socket.on(subscribeEvent, subscribeHandler);

        var unsubscribeHandler = createClientMessageHandler(
            unsubscribeEvent,
            registeredCommand.definition,
            socket,
            {
              onResponse: function(unsubscribeMessageBody, responseBody) {
                if (subscribers[subscriberId])
                  delete subscribers[subscriberId];
              }
            }
        );
        socket.on(unsubscribeEvent, unsubscribeHandler);
      } else {
        socket.on(registeredCommand.name,
                  createClientMessageHandler(registeredCommand.name,
                                             registeredCommand.definition,
                                             socket));
      }
    });

    var errorHandler = createErrorHandler(socket);
    connection.on('error', errorHandler);

    socket.on('disconnect', function() {
      Object.keys(allSubscribers).forEach(function(notificationEvent) {
        var subscribers = allSubscribers[notificationEvent];
        if (subscribers && subscribers[subscriberId]) {
          subscribers[subscriberId].unsubscribe();
          delete subscribers[subscriberId];
        }
      });
      connection.removeListener('error', errorHandler);
      socket.removeAllListeners();
    });
  });

  return registeredCommands;
}
<|endoftext|>


<commit_before>import app from "app";
import Utils from "libs/utils";
import ResizableBuffer from "libs/resizable_buffer";
import ErrorHandling from "libs/error_handling";
import * as THREE from "three";
import TWEEN from "tween.js";
import ParticleMaterialFactory from "./materials/particle_material_factory";

class Tree {

  constructor(treeId, treeColor, model) {
    // create skeletonTracing to show in TDView and pre-allocate buffers

    this.model = model;
    const edgeGeometry = new THREE.BufferGeometry();
    const nodeGeometry = new THREE.BufferGeometry();

    this.nodeIDs = new ResizableBuffer(1, 100, Int32Array);
    this.edgesBuffer = new ResizableBuffer(6);
    this.nodesBuffer = new ResizableBuffer(3);
    this.sizesBuffer = new ResizableBuffer(1);
    this.scalesBuffer = new ResizableBuffer(1);
    this.nodesColorBuffer = new ResizableBuffer(3);

    edgeGeometry.addAttribute("position", this.makeDynamicFloatAttribute(3, this.edgesBuffer));
    nodeGeometry.addAttribute("position", this.makeDynamicFloatAttribute(3, this.nodesBuffer));
    nodeGeometry.addAttribute("sizeNm", this.makeDynamicFloatAttribute(1, this.sizesBuffer));
    nodeGeometry.addAttribute("nodeScaleFactor", this.makeDynamicFloatAttribute(1, this.scalesBuffer));
    nodeGeometry.addAttribute("color", this.makeDynamicFloatAttribute(3, this.nodesColorBuffer));

    nodeGeometry.nodeIDs = this.nodeIDs;

    this.edges = new THREE.Line(
      edgeGeometry,
      new THREE.LineBasicMaterial({
        color: this.darkenHex(treeColor),
        linewidth: this.getLineWidth(),
      }),
      THREE.LineSegments,
    );

    this.particleMaterial = new ParticleMaterialFactory(this.model).getMaterial();

    this.nodes = new THREE.Points(nodeGeometry, this.particleMaterial);

    this.id = treeId;
  }

  makeDynamicFloatAttribute(itemSize, resizableBuffer) {
    const attr = new THREE.BufferAttribute(resizableBuffer.getBuffer(), itemSize);
    attr.setDynamic(true);
    return attr;
  }

  clear() {
    this.nodesBuffer.clear();
    this.edgesBuffer.clear();
    this.sizesBuffer.clear();
    this.scalesBuffer.clear();
    this.nodeIDs.clear();
    this.updateNodesColors();
  }

  isEmpty() {
    return this.nodesBuffer.getLength() === 0;
  }

  addNode(node) {
    this.nodesBuffer.push(node.pos);
    this.sizesBuffer.push([node.radius * 2]);
    this.scalesBuffer.push([1.0]);
    this.nodeIDs.push([node.id]);
    this.nodesColorBuffer.push(this.getColor(node.id));

    // Add any edge from smaller IDs to the node
    // ASSUMPTION: if this node is new, it should have a
    //             greater id as its neighbor
    for (const neighbor of node.neighbors) {
      if (neighbor.id < node.id) {
        this.edgesBuffer.push(neighbor.pos.concat(node.pos));
      }
    }

    this.updateGeometries();
  }

  addNodes(nodeList) {
    nodeList.forEach(node =>
      this.addNode(node));
  }

  deleteNode(node) {
    let edgesIndex;
    let found;
    const swapLast = (array, index) => {
      const lastElement = array.pop();
      Utils.__range__(0, array.elementLength, false).forEach((i) => {
        array.getAllElements()[(index * array.elementLength) + i] = lastElement[i];
      });
    };

    const nodesIndex = this.getNodeIndex(node.id);
    ErrorHandling.assert((nodesIndex != null), "No node found.", { id: node.id, nodeIDs: this.nodeIDs });

    // swap IDs and nodes
    swapLast(this.nodeIDs, nodesIndex);
    swapLast(this.nodesBuffer, nodesIndex);
    swapLast(this.sizesBuffer, nodesIndex);
    swapLast(this.scalesBuffer, nodesIndex);
    swapLast(this.nodesColorBuffer, nodesIndex);

    // Delete Edge by finding it in the array
    const edgeArray = this.getEdgeArray(node, node.neighbors[0]);

    for (const i of Utils.__range__(0, this.edgesBuffer.getLength(), false)) {
      found = true;
      for (let j = 0; j <= 5; j++) {
        found &= Math.abs(this.edges.geometry.attributes.position.array[(6 * i) + j] - edgeArray[j]) < 0.5;
      }
      if (found) {
        edgesIndex = i;
        break;
      }
    }

    ErrorHandling.assert(found, "No edge found.", { found, edgeArray, nodesIndex });

    swapLast(this.edgesBuffer, edgesIndex);

    this.updateGeometries();
  }

  mergeTree(otherTree, lastNode, activeNode) {
    const merge = property => this[property].pushSubarray(otherTree[property].getAllElements());

    // merge IDs, nodes and edges
    merge("nodeIDs");
    merge("nodesBuffer");
    merge("edgesBuffer");
    merge("sizesBuffer");
    merge("scalesBuffer");
    this.edgesBuffer.push(this.getEdgeArray(lastNode, activeNode));

    this.updateNodesColors();
    this.updateGeometries();
  }

  getEdgeArray(node1, node2) {
    // ASSUMPTION: edges always go from smaller ID to bigger ID

    if (node1.id < node2.id) {
      return node1.pos.concat(node2.pos);
    } else {
      return node2.pos.concat(node1.pos);
    }
  }

  setSizeAttenuation(sizeAttenuation) {
    this.nodes.material.sizeAttenuation = sizeAttenuation;
    this.updateGeometries();
  }

  updateTreeColor() {
    const newColor = this.model.skeletonTracing.getTree(this.id).color;
    this.edges.material.color = new THREE.Color(this.darkenHex(newColor));

    this.updateNodesColors();
    this.updateGeometries();
  }

  getMeshes() {
    return [this.nodes, this.edges];
  }

  dispose() {
    for (const geometry of this.getMeshes()) {
      geometry.geometry.dispose();
      geometry.material.dispose();
    }
  }

  updateNodesColors() {
    this.nodesColorBuffer.clear();
    for (const i of Utils.__range__(0, this.nodeIDs.length, false)) {
      this.nodesColorBuffer.push(this.getColor(this.nodeIDs.get(i)));
    }

    this.updateGeometries();
  }

  updateNodeColor(id, isActiveNode, isBranchPoint) {
    this.doWithNodeIndex(id, index => this.nodesColorBuffer.set(this.getColor(id, isActiveNode, isBranchPoint), index),
    );

    this.updateGeometries();
  }

  updateNodeRadius(id, radius) {
    this.doWithNodeIndex(id, index => this.sizesBuffer.set([radius * 2], index),
    );

    this.updateGeometries();
  }

  startNodeHighlightAnimation(nodeId) {
    const normal = 1.0;
    const highlighted = 2.0;

    this.doWithNodeIndex(nodeId, index => this.animateNodeScale(normal, highlighted, index, () => this.animateNodeScale(highlighted, normal, index),
      ),
    );
  }

  animateNodeScale(from, to, index, onComplete) {
    if (onComplete == null) { onComplete = function () {}; }
    const setScaleFactor = factor => this.scalesBuffer.set([factor], index);
    const redraw = () => {
      this.updateGeometries();
      app.vent.trigger("rerender");
    };
    const onUpdate = function () {
      setScaleFactor(this.scaleFactor);
      redraw();
    };

    (new TWEEN.Tween({ scaleFactor: from }))
      .to({ scaleFactor: to }, 100)
      .onUpdate(onUpdate)
      .onComplete(onComplete)
      .start();
  }

  getColor(id, isActiveNode, isBranchPoint) {
    const tree = this.model.skeletonTracing.getTree(this.id);
    let { color } = tree;
    if (id != null) {
      isActiveNode = isActiveNode || this.model.skeletonTracing.getActiveNodeId() === id;
      isBranchPoint = isBranchPoint || tree.isBranchPoint(id);

      if (isActiveNode) {
        color = this.shiftHex(color, 1 / 4);
      } else {
        color = this.darkenHex(color);
      }

      if (isBranchPoint) {
        color = this.invertHex(color);
      }
    }

    return this.hexToRGB(color);
  }

  showRadius(show) {
    this.edges.material.linewidth = this.getLineWidth();
    this.particleMaterial.setShowRadius(show);
  }

  updateGeometries() {
    this.updateGeometry(this.nodes, {
      position: [3, this.nodesBuffer],
      sizeNm: [1, this.sizesBuffer],
      nodeScaleFactor: [1, this.scalesBuffer],
      color: [3, this.nodesColorBuffer],
    });

    this.updateGeometry(this.edges, {
      position: [3, this.edgesBuffer],
    }, 2);
  }

  updateGeometry(mesh, attribute2buffer, itemsPerElement = 1) {
    let length = -1;
    let needsToRebuildGeometry = false;
    for (const attribute of Object.keys(attribute2buffer)) {
      const rBuffer = attribute2buffer[attribute][1];

      if (length === -1) {
        length = rBuffer.getLength();
      } else {
        ErrorHandling.assertEquals(rBuffer.getLength(), length,
          "All attribute lengths should be equal.");
      }

      if (mesh.geometry.attributes[attribute].array !== rBuffer.getBuffer()) {
        // The reference of the underlying buffer has changed. Unfortunately,
        // this means that we have to re-create all of the attributes.
        needsToRebuildGeometry = true;
      }
      mesh.geometry.attributes[attribute].needsUpdate = true;
    }

    if (needsToRebuildGeometry) {
      // Free any memory allocated on the GPU
      mesh.geometry.dispose();
      for (const attribute of Object.keys(attribute2buffer)) {
        // Recreate attribute
        const [itemSize, rBuffer] = attribute2buffer[attribute];
        mesh.geometry.addAttribute(attribute, this.makeDynamicFloatAttribute(itemSize, rBuffer));
      }
    }
    mesh.geometry.computeBoundingSphere();
    mesh.geometry.setDrawRange(0, length * itemsPerElement);
  }

  logState(title) {
    console.log(` +++ ${title} +++ `);
    console.log("nodeIDs", this.nodeIDs.toString());
    console.log("nodesBuffer", this.nodesBuffer.toString());
    console.log("edgesBuffer", this.edgesBuffer.toString());
    console.log("sizesBuffer", this.sizesBuffer.toString());
  }

  getNodeIndex(nodeId) {
    for (let i = 0; i < this.nodeIDs.length; i++) {
      if (this.nodeIDs.get(i) === nodeId) {
        return i;
      }
    }
    return null;
  }

  doWithNodeIndex(nodeId, f) {
    const index = this.getNodeIndex(nodeId);
    if (index == null) { return; }
    f(index);
  }

  getLineWidth() {
    return this.model.user.get("particleSize") / 4;
  }

  // ### Color utility methods

  hexToRGB(hexColor) {
    const rgbColor = new THREE.Color().setHex(hexColor);
    return [rgbColor.r, rgbColor.g, rgbColor.b];
  }

  darkenHex(hexColor) {
    const hslColor = new THREE.Color(hexColor).getHSL();
    hslColor.l = 0.25;
    return new THREE.Color().setHSL(hslColor.h, hslColor.s, hslColor.l).getHex();
  }

  shiftHex(hexColor, shiftValue) {
    const hslColor = new THREE.Color(hexColor).getHSL();
    hslColor.h = (hslColor.h + shiftValue) % 1;
    return new THREE.Color().setHSL(hslColor.h, hslColor.s, hslColor.l).getHex();
  }

  invertHex(hexColor) {
    return this.shiftHex(hexColor, 0.5);
  }
}

export default Tree;
<commit_msg>1590 Fix edge bug<commit_after>import app from "app";
import Utils from "libs/utils";
import ResizableBuffer from "libs/resizable_buffer";
import ErrorHandling from "libs/error_handling";
import * as THREE from "three";
import TWEEN from "tween.js";
import ParticleMaterialFactory from "./materials/particle_material_factory";

class Tree {

  constructor(treeId, treeColor, model) {
    // create skeletonTracing to show in TDView and pre-allocate buffers

    this.model = model;
    const edgeGeometry = new THREE.BufferGeometry();
    const nodeGeometry = new THREE.BufferGeometry();

    this.nodeIDs = new ResizableBuffer(1, 100, Int32Array);
    this.edgesBuffer = new ResizableBuffer(6);
    this.nodesBuffer = new ResizableBuffer(3);
    this.sizesBuffer = new ResizableBuffer(1);
    this.scalesBuffer = new ResizableBuffer(1);
    this.nodesColorBuffer = new ResizableBuffer(3);

    edgeGeometry.addAttribute("position", this.makeDynamicFloatAttribute(3, this.edgesBuffer));
    nodeGeometry.addAttribute("position", this.makeDynamicFloatAttribute(3, this.nodesBuffer));
    nodeGeometry.addAttribute("sizeNm", this.makeDynamicFloatAttribute(1, this.sizesBuffer));
    nodeGeometry.addAttribute("nodeScaleFactor", this.makeDynamicFloatAttribute(1, this.scalesBuffer));
    nodeGeometry.addAttribute("color", this.makeDynamicFloatAttribute(3, this.nodesColorBuffer));

    nodeGeometry.nodeIDs = this.nodeIDs;

    this.edges = new THREE.LineSegments(
      edgeGeometry,
      new THREE.LineBasicMaterial({
        color: this.darkenHex(treeColor),
        linewidth: this.getLineWidth(),
      }),
    );

    this.particleMaterial = new ParticleMaterialFactory(this.model).getMaterial();

    this.nodes = new THREE.Points(nodeGeometry, this.particleMaterial);

    this.id = treeId;
  }

  makeDynamicFloatAttribute(itemSize, resizableBuffer) {
    const attr = new THREE.BufferAttribute(resizableBuffer.getBuffer(), itemSize);
    attr.setDynamic(true);
    return attr;
  }

  clear() {
    this.nodesBuffer.clear();
    this.edgesBuffer.clear();
    this.sizesBuffer.clear();
    this.scalesBuffer.clear();
    this.nodeIDs.clear();
    this.updateNodesColors();
  }

  isEmpty() {
    return this.nodesBuffer.getLength() === 0;
  }

  addNode(node) {
    this.nodesBuffer.push(node.pos);
    this.sizesBuffer.push([node.radius * 2]);
    this.scalesBuffer.push([1.0]);
    this.nodeIDs.push([node.id]);
    this.nodesColorBuffer.push(this.getColor(node.id));

    // Add any edge from smaller IDs to the node
    // ASSUMPTION: if this node is new, it should have a
    //             greater id as its neighbor
    for (const neighbor of node.neighbors) {
      if (neighbor.id < node.id) {
        this.edgesBuffer.push(neighbor.pos.concat(node.pos));
      }
    }

    this.updateGeometries();
  }

  addNodes(nodeList) {
    nodeList.forEach(node =>
      this.addNode(node));
  }

  deleteNode(node) {
    let edgesIndex;
    let found;
    const swapLast = (array, index) => {
      const lastElement = array.pop();
      Utils.__range__(0, array.elementLength, false).forEach((i) => {
        array.getAllElements()[(index * array.elementLength) + i] = lastElement[i];
      });
    };

    const nodesIndex = this.getNodeIndex(node.id);
    ErrorHandling.assert((nodesIndex != null), "No node found.", { id: node.id, nodeIDs: this.nodeIDs });

    // swap IDs and nodes
    swapLast(this.nodeIDs, nodesIndex);
    swapLast(this.nodesBuffer, nodesIndex);
    swapLast(this.sizesBuffer, nodesIndex);
    swapLast(this.scalesBuffer, nodesIndex);
    swapLast(this.nodesColorBuffer, nodesIndex);

    // Delete Edge by finding it in the array
    const edgeArray = this.getEdgeArray(node, node.neighbors[0]);

    for (const i of Utils.__range__(0, this.edgesBuffer.getLength(), false)) {
      found = true;
      for (let j = 0; j <= 5; j++) {
        found &= Math.abs(this.edges.geometry.attributes.position.array[(6 * i) + j] - edgeArray[j]) < 0.5;
      }
      if (found) {
        edgesIndex = i;
        break;
      }
    }

    ErrorHandling.assert(found, "No edge found.", { found, edgeArray, nodesIndex });

    swapLast(this.edgesBuffer, edgesIndex);

    this.updateGeometries();
  }

  mergeTree(otherTree, lastNode, activeNode) {
    const merge = property => this[property].pushSubarray(otherTree[property].getAllElements());

    // merge IDs, nodes and edges
    merge("nodeIDs");
    merge("nodesBuffer");
    merge("edgesBuffer");
    merge("sizesBuffer");
    merge("scalesBuffer");
    this.edgesBuffer.push(this.getEdgeArray(lastNode, activeNode));

    this.updateNodesColors();
    this.updateGeometries();
  }

  getEdgeArray(node1, node2) {
    // ASSUMPTION: edges always go from smaller ID to bigger ID

    if (node1.id < node2.id) {
      return node1.pos.concat(node2.pos);
    } else {
      return node2.pos.concat(node1.pos);
    }
  }

  setSizeAttenuation(sizeAttenuation) {
    this.nodes.material.sizeAttenuation = sizeAttenuation;
    this.updateGeometries();
  }

  updateTreeColor() {
    const newColor = this.model.skeletonTracing.getTree(this.id).color;
    this.edges.material.color = new THREE.Color(this.darkenHex(newColor));

    this.updateNodesColors();
    this.updateGeometries();
  }

  getMeshes() {
    return [this.nodes, this.edges];
  }

  dispose() {
    for (const geometry of this.getMeshes()) {
      geometry.geometry.dispose();
      geometry.material.dispose();
    }
  }

  updateNodesColors() {
    this.nodesColorBuffer.clear();
    for (const i of Utils.__range__(0, this.nodeIDs.length, false)) {
      this.nodesColorBuffer.push(this.getColor(this.nodeIDs.get(i)));
    }

    this.updateGeometries();
  }

  updateNodeColor(id, isActiveNode, isBranchPoint) {
    this.doWithNodeIndex(id, index => this.nodesColorBuffer.set(this.getColor(id, isActiveNode, isBranchPoint), index),
    );

    this.updateGeometries();
  }

  updateNodeRadius(id, radius) {
    this.doWithNodeIndex(id, index => this.sizesBuffer.set([radius * 2], index),
    );

    this.updateGeometries();
  }

  startNodeHighlightAnimation(nodeId) {
    const normal = 1.0;
    const highlighted = 2.0;

    this.doWithNodeIndex(nodeId, index => this.animateNodeScale(normal, highlighted, index, () => this.animateNodeScale(highlighted, normal, index),
      ),
    );
  }

  animateNodeScale(from, to, index, onComplete) {
    if (onComplete == null) { onComplete = function () {}; }
    const setScaleFactor = factor => this.scalesBuffer.set([factor], index);
    const redraw = () => {
      this.updateGeometries();
      app.vent.trigger("rerender");
    };
    const onUpdate = function () {
      setScaleFactor(this.scaleFactor);
      redraw();
    };

    (new TWEEN.Tween({ scaleFactor: from }))
      .to({ scaleFactor: to }, 100)
      .onUpdate(onUpdate)
      .onComplete(onComplete)
      .start();
  }

  getColor(id, isActiveNode, isBranchPoint) {
    const tree = this.model.skeletonTracing.getTree(this.id);
    let { color } = tree;
    if (id != null) {
      isActiveNode = isActiveNode || this.model.skeletonTracing.getActiveNodeId() === id;
      isBranchPoint = isBranchPoint || tree.isBranchPoint(id);

      if (isActiveNode) {
        color = this.shiftHex(color, 1 / 4);
      } else {
        color = this.darkenHex(color);
      }

      if (isBranchPoint) {
        color = this.invertHex(color);
      }
    }

    return this.hexToRGB(color);
  }

  showRadius(show) {
    this.edges.material.linewidth = this.getLineWidth();
    this.particleMaterial.setShowRadius(show);
  }

  updateGeometries() {
    this.updateGeometry(this.nodes, {
      position: [3, this.nodesBuffer],
      sizeNm: [1, this.sizesBuffer],
      nodeScaleFactor: [1, this.scalesBuffer],
      color: [3, this.nodesColorBuffer],
    });

    this.updateGeometry(this.edges, {
      position: [3, this.edgesBuffer],
    }, 2);
  }

  updateGeometry(mesh, attribute2buffer, itemsPerElement = 1) {
    let length = -1;
    let needsToRebuildGeometry = false;
    for (const attribute of Object.keys(attribute2buffer)) {
      const rBuffer = attribute2buffer[attribute][1];

      if (length === -1) {
        length = rBuffer.getLength();
      } else {
        ErrorHandling.assertEquals(rBuffer.getLength(), length,
          "All attribute lengths should be equal.");
      }

      if (mesh.geometry.attributes[attribute].array !== rBuffer.getBuffer()) {
        // The reference of the underlying buffer has changed. Unfortunately,
        // this means that we have to re-create all of the attributes.
        needsToRebuildGeometry = true;
      }
      mesh.geometry.attributes[attribute].needsUpdate = true;
    }

    if (needsToRebuildGeometry) {
      // Free any memory allocated on the GPU
      mesh.geometry.dispose();
      for (const attribute of Object.keys(attribute2buffer)) {
        // Recreate attribute
        const [itemSize, rBuffer] = attribute2buffer[attribute];
        mesh.geometry.addAttribute(attribute, this.makeDynamicFloatAttribute(itemSize, rBuffer));
      }
    }
    mesh.geometry.computeBoundingSphere();
    mesh.geometry.setDrawRange(0, length * itemsPerElement);
  }

  logState(title) {
    console.log(` +++ ${title} +++ `);
    console.log("nodeIDs", this.nodeIDs.toString());
    console.log("nodesBuffer", this.nodesBuffer.toString());
    console.log("edgesBuffer", this.edgesBuffer.toString());
    console.log("sizesBuffer", this.sizesBuffer.toString());
  }

  getNodeIndex(nodeId) {
    for (let i = 0; i < this.nodeIDs.length; i++) {
      if (this.nodeIDs.get(i) === nodeId) {
        return i;
      }
    }
    return null;
  }

  doWithNodeIndex(nodeId, f) {
    const index = this.getNodeIndex(nodeId);
    if (index == null) { return; }
    f(index);
  }

  getLineWidth() {
    return this.model.user.get("particleSize") / 4;
  }

  // ### Color utility methods

  hexToRGB(hexColor) {
    const rgbColor = new THREE.Color().setHex(hexColor);
    return [rgbColor.r, rgbColor.g, rgbColor.b];
  }

  darkenHex(hexColor) {
    const hslColor = new THREE.Color(hexColor).getHSL();
    hslColor.l = 0.25;
    return new THREE.Color().setHSL(hslColor.h, hslColor.s, hslColor.l).getHex();
  }

  shiftHex(hexColor, shiftValue) {
    const hslColor = new THREE.Color(hexColor).getHSL();
    hslColor.h = (hslColor.h + shiftValue) % 1;
    return new THREE.Color().setHSL(hslColor.h, hslColor.s, hslColor.l).getHex();
  }

  invertHex(hexColor) {
    return this.shiftHex(hexColor, 0.5);
  }
}

export default Tree;
<|endoftext|>


<commit_before>var Wapper = {
  split: function(range) {
    var startParent = range.startContainer
    var endParent = range.endContainer

    if (startParent == endParent) {
      // Achtung! Zero-length selection.
      if (range.startOffset == range.endOffset) return

      endParent.splitText(range.endOffset)
      var middle = startParent.splitText(range.startOffset)
      return [middle]
    } else {
      var commonAncestor = range.commonAncestorContainer
      var between = []

      if (commonAncestor == startParent.parentNode) {
        // Common scenario where selection contains a few text and element
        // nodes within a block-level element - a <p> for instance.

        var current = startParent.splitText(range.startOffset)
        var after = endParent.splitText(range.endOffset)

        for (current; current != after; current = current.nextSibling) {
          between.push(current)
        }
      } else {
        // More complex scenario where the selection spans multiple elements.

        endParent.splitText(range.endOffset)
        var start = startParent.splitText(range.startOffset)
        var end = endParent

        function nextViaParent(node) {
          if (node.nextSibling) {
            return node.nextSibling
          } else {
            return nextViaParent(node.parentNode)
          }
        }

        function previousViaParent(node) {
          if (node.previousSibling) {
            return node.previousSibling
          } else {
            return previousViaParent(node.parentNode)
          }
        }

        // Ascend DOM until parent is a child of the common ancestor.
        function rootNode(node) {
          while (node.parentNode.parentNode != commonAncestor) {
            node = node.parentNode
          }
          return node
        }

        var startRoot = rootNode(start)
        var endRoot = rootNode(end)
        var current = start

        while (current.parentNode != commonAncestor) {
          between.push(current)
          current = nextViaParent(current)
        }

        var tail = []
        current = endParent

        while (current.parentNode != commonAncestor) {
          tail.unshift(current)
          current = previousViaParent(current)
        }

        current = startRoot.parentNode

        while (current.nextSibling != endRoot.parentNode) {
          between.push(current = current.nextSibling)
        }

        between = between.concat(tail)
      }

      return between
    }
  },

  wrap: function(range) {
    var elems = this.split(range)
    var currentParent
    var groups = []
    var spans = []

    function createWrapper() {
      return document.createElement("span")
    }

    function display(elem) {
      if (elem.nodeType != 1) return
      var style = window.getComputedStyle(elem, null)
      return style && style.getPropertyValue("display")
    }

    // Group nodes by shared parent.
    for (var i = 0; i < elems.length; i++) {
      var elem = elems[i]

      if (elem.parentNode == currentParent) {
        groups[groups.length - 1].push(elem)
      } else {
        groups.push([elem])
      }

      currentParent = elem.parentNode
    }

    for (var g = 0; g < groups.length; g++) {
      var children = groups[g]

      if (display(children[0]) == "block") {
        // Wrap a block-element's children not the block element itself.
        for (var c = 0; c < children.length; c++) {
          var wrapper = createWrapper()
          spans.push(wrapper)

          while (children[0].childNodes.length) {
            wrapper.appendChild(children[0].firstChild)
          }

          children[0].appendChild(wrapper)
        }
      } else {
        var wrapper = createWrapper()
        spans.push(wrapper)

        // Insert the wrapper before the first element.
        children[0].parentNode.insertBefore(wrapper, children[0])

        // Move the contained elements into the wrapper.
        for (var c = 0; c < children.length; c++) {
          wrapper.appendChild(children[c])
        }
      }
    }

    return spans
  }
}
<commit_msg>Simpler.<commit_after>var Wapper = {
  split: function(range) {
    var startParent = range.startContainer
    var endParent = range.endContainer

    if (startParent == endParent) {
      // Achtung! Zero-length selection.
      if (range.startOffset == range.endOffset) return

      endParent.splitText(range.endOffset)
      var middle = startParent.splitText(range.startOffset)
      return [middle]
    } else {
      var commonAncestor = range.commonAncestorContainer
      var between = []

      if (commonAncestor == startParent.parentNode) {
        // Common scenario where selection contains a few text and element
        // nodes within a block-level element - a <p> for instance.

        var current = startParent.splitText(range.startOffset)
        var after = endParent.splitText(range.endOffset)

        for (current; current != after; current = current.nextSibling) {
          between.push(current)
        }
      } else {
        // More complex scenario where the selection spans multiple elements.

        endParent.splitText(range.endOffset)
        var start = startParent.splitText(range.startOffset)
        var end = endParent

        function nextViaParent(node) {
          if (node.nextSibling) {
            return node.nextSibling
          } else {
            return nextViaParent(node.parentNode)
          }
        }

        function previousViaParent(node) {
          if (node.previousSibling) {
            return node.previousSibling
          } else {
            return previousViaParent(node.parentNode)
          }
        }

        // Ascend the DOM until finding a direct child of the common ancestor.
        function rootNode(node) {
          while (node.parentNode != commonAncestor) {
            node = node.parentNode
          }
          return node
        }

        var startRoot = rootNode(start)
        var endRoot = rootNode(end)
        var current = start

        // Start.
        while (current.parentNode != commonAncestor) {
          between.push(current)
          current = nextViaParent(current)
        }

        var tail = []
        current = endParent

        // End.
        while (current.parentNode != commonAncestor) {
          tail.unshift(current)
          current = previousViaParent(current)
        }

        current = startRoot

        // Middle.
        while (current.nextSibling != endRoot) {
          between.push(current = current.nextSibling)
        }

        between = between.concat(tail)
      }

      return between
    }
  },

  wrap: function(range) {
    var elems = this.split(range)
    var currentParent
    var groups = []
    var spans = []

    function createWrapper() {
      return document.createElement("span")
    }

    function display(elem) {
      if (elem.nodeType != 1) return
      var style = window.getComputedStyle(elem, null)
      return style && style.getPropertyValue("display")
    }

    // Group nodes by shared parent.
    for (var i = 0; i < elems.length; i++) {
      var elem = elems[i]

      if (elem.parentNode == currentParent) {
        groups[groups.length - 1].push(elem)
      } else {
        groups.push([elem])
      }

      currentParent = elem.parentNode
    }

    for (var g = 0; g < groups.length; g++) {
      var children = groups[g]

      if (display(children[0]) == "block") {
        // Wrap a block-element's children not the block element itself.
        for (var c = 0; c < children.length; c++) {
          var wrapper = createWrapper()
          spans.push(wrapper)

          while (children[0].childNodes.length) {
            wrapper.appendChild(children[0].firstChild)
          }

          children[0].appendChild(wrapper)
        }
      } else {
        var wrapper = createWrapper()
        spans.push(wrapper)

        // Insert the wrapper before the first element.
        children[0].parentNode.insertBefore(wrapper, children[0])

        // Move the contained elements into the wrapper.
        for (var c = 0; c < children.length; c++) {
          wrapper.appendChild(children[c])
        }
      }
    }

    return spans
  }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  modify(defaultConfig, { target, dev }, webpack) {
    const config = defaultConfig; // stay immutable here

    // Change the name of the server output file in production
    if (target === 'web') {
      // modify filenaming to account for multiple entry files
      config.output.filename = dev
        ? 'static/js/[name].js'
        : 'static/js/[name].[hash:8].js';

      // add another entry point called vendor
      config.entry.vendor = [
        require.resolve('react'),
        require.resolve('react-dom'),
        // ... add any other vendor packages with require.resolve('xxx')
      ];

      // Use Twitter Lite's vendor & manifest bundle approach
      // See https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3
      config.plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
          names: ['vendor', 'manifest'],
          minChunks: Infinity,
        })
      );

      // Extract common modules from all the chunks (requires no 'name' property)
      config.plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
          async: true,
          children: true,
          minChunks: 4,
        })
      );
    }

    return config;
  },
};
<commit_msg>Fix vendor bundle example with polyfills<commit_after>'use strict';

module.exports = {
  modify(defaultConfig, { target, dev }, webpack) {
    const config = defaultConfig; // stay immutable here

    // Change the name of the server output file in production
    if (target === 'web') {
      // modify filenaming to account for multiple entry files
      config.output.filename = dev
        ? 'static/js/[name].js'
        : 'static/js/[name].[hash:8].js';

      // add another entry point called vendor
      config.entry.vendor = [
        // now that React has moved, we need to Razzle's polyfills because
        // vendor.js will be loaded before our other entry. Razzle looks for
        // process.env.REACT_BUNDLE_PATH and will exclude the polyfill from our normal entry,
        // so we don't need to worry about including it twice.
        require.resolve('razzle/polyfills'),
        require.resolve('react'),
        require.resolve('react-dom'),
        // ... add any other vendor packages with require.resolve('xxx')
      ];

      // Use Twitter Lite's vendor & manifest bundle approach
      // See https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3
      config.plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
          names: ['vendor', 'manifest'],
          minChunks: Infinity,
        })
      );

      // Extract common modules from all the chunks (requires no 'name' property)
      config.plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
          async: true,
          children: true,
          minChunks: 4,
        })
      );
    }

    return config;
  },
};
<|endoftext|>


<commit_before>/**
@fileOverview Template manager UI.
*/
$.extend($.boom, {

	/**
	* @class
	* @name $.boom.templates
	*/
	templates :
		/** @lends $.boom.templates */
		{

		/** @function */
		init : function(config) {

			var self = this;

			this.bind();

			$.boom.log('Template manager init');
		},

		/** @function */
		bind : function(){

			var self = this;

			$('.b-templates-delete').click(function(event){
				var item = $(this).closest( "tr" );
				$.boom.dialog.confirm(
					"Please confirm",
					"Are you sure you want to delete this template?"
				)
				.done( function(){
					item.fadeOut(600, function(){
						item.remove();
					});
				});
			});

			$('#b-templates-save').click(function(){

				var data = $('#b-templates form').serialize();

				$.post('/cms/templates/save', data, function(){
					$.boom.growl.show('Templates successfully saved.');
				});
			});
		}
	}
});<commit_msg>Updated boom confirmations<commit_after>/**
@fileOverview Template manager UI.
*/
$.extend($.boom, {

	/**
	* @class
	* @name $.boom.templates
	*/
	templates :
		/** @lends $.boom.templates */
		{

		/** @function */
		init : function(config) {

			var self = this;

			this.bind();

			$.boom.log('Template manager init');
		},

		/** @function */
		bind : function(){

			var self = this;

			$('.b-templates-delete').click(function(event){
				var item = $(this).closest( "tr" ),
					confirmation = new boomConfirmation("Please confirm", "Are you sure you want to delete this template?");

				confirmation
					.done( function(){
						item.fadeOut(600, function(){
							item.remove();
						});
					});
			});

			$('#b-templates-save').click(function(){

				var data = $('#b-templates form').serialize();

				$.post('/cms/templates/save', data, function(){
					$.boom.growl.show('Templates successfully saved.');
				});
			});
		}
	}
});<|endoftext|>


<commit_before>var LorViewDiffHistory = function() {
  return {
    htmlHash: {},
    currentHash: 44032,
    dmp: new diff_match_patch(),

    pushHash: function (tag) {
       if (typeof(this.htmlHash[tag]) == 'undefined') {
        this.htmlHash[tag] = eval('"\\u'+this.currentHash.toString(16)+'"');
        this.currentHash++;
      }
      return this.htmlHash[tag];
    },

    clearHash: function () {
      this.htmlHash = {};
      this.currentHash = 44032;
    },

    html2plain: function (html) {
      html = html.replace(/(<(\S*?)[^>]*>|<.*?\/>)/g, function(tag){
          return document.lorViewDiffHistory.pushHash(tag);
      });

      return html;
    },

    plain2html: function (plain) {
      for(var tag in this.htmlHash){
        plain = plain.replace(RegExp(this.htmlHash[tag], 'g'), tag);
      }
      return plain;
    },

    makeHtml: function(diffs) {
      var html = [];
      var pattern_amp = /&/g;
      var pattern_lt = /</g;
      var pattern_gt = />/g;
      var pattern_para = /\n/g;
      for (var x = 0; x < diffs.length; x++) {
        var op = diffs[x][0];    // Operation (insert, delete, equal)
        var data = diffs[x][1];  // Text of change.
        var text = data
            .replace(pattern_amp, '&')
            .replace(pattern_lt, '<')
            .replace(pattern_gt, '>');

        switch (op) {
          case DIFF_INSERT:
            html[x] = '' + text + '';
            break;
          case DIFF_DELETE:
            html[x] = '' + text + '';
            break;
          case DIFF_EQUAL:
            html[x] = text;
            break;
        }
      }
      return html.join('');
    },

    highlightContentDiff: function (object1, object2) {
      this.clearHash();
      var content1 = this.html2plain ($(object1).html());
      var content2 = this.html2plain ($(object2).html());

      var diffs = this.dmp.diff_main(content1, content2);
      this.dmp.diff_cleanupSemantic(diffs);
      var html = this.makeHtml(diffs);

      return this.plain2html(html);
    }
  };
}
document.lorViewDiffHistory = new LorViewDiffHistory();

function diffHighlightOn(button) {
  document.prev_object = null;
  $("div.messages div.msg div.msg_body").each(
    function(ind, obj){
      $(obj).attr("oldHtml", $(obj).html());
      if (document.prev_object == null) {
        document.prev_object = obj;
        return;
      }
      var html = document.lorViewDiffHistory.highlightContentDiff(obj, document.prev_object);
      $(document.prev_object).html(html);
      document.prev_object = obj;
    }
  );
}

function diffHighlightOff(button) {
  document.prev_object = null;
  $("div.messages div.msg div.msg_body").each(
    function(ind, obj){
      $(obj).html($(obj).attr("oldHtml"));
    }
  );
}

function toggleDiffHighlight(obj) {
  var button = obj.currentTarget;
  var attr = $(button).attr("highlighted");
  if (attr == "on") {
    $(button)
      .val("Подсветить различия")
      .attr("highlighted", "off");
    diffHighlightOff(button);
  } else {
    $(button)
      .val("Убрать подсветку")
      .attr("highlighted", "on");
    diffHighlightOn(button);
  }
}

$(document).ready(function() {
  var button = $('<input type="button">')
    .val("Подсветить различия")
    .attr("highlighted", "off")
    .addClass("toggleDiffHighlight")
    .appendTo($("#historyButtonBar"))
    .click(function(obj){
      toggleDiffHighlight(obj)
    });
});
<commit_msg>Исправлена проблема:<commit_after>var LorViewDiffHistory = function() {
  return {
    htmlHash: {},
    currentHash: 44032,
    dmp: new diff_match_patch(),

    pushHash: function (tag) {
       if (typeof(this.htmlHash[tag]) == 'undefined') {
        this.htmlHash[tag] = eval('"\\u'+this.currentHash.toString(16)+'"');
        this.currentHash++;
      }
      return this.htmlHash[tag];
    },

    clearHash: function () {
      this.htmlHash = {};
      this.currentHash = 44032;
    },

    html2plain: function (html) {
      html = html.replace(/(<(\S*?)[^>]*>|<.*?\/>)/g, function(tag){
          return document.lorViewDiffHistory.pushHash(tag);
      });

      return html;
    },

    plain2html: function (plain) {
      for(var tag in this.htmlHash){
        plain = plain.replace(RegExp(this.htmlHash[tag], 'g'), tag);
      }
      return plain;
    },

    makeHtml: function(diffs) {
      var html = [];
      var pattern_amp = /&/g;
      var pattern_lt = /</g;
      var pattern_gt = />/g;
      var pattern_para = /\n/g;
      for (var x = 0; x < diffs.length; x++) {
        var op = diffs[x][0];    // Operation (insert, delete, equal)
        var data = diffs[x][1];  // Text of change.
        var text = data
            .replace(pattern_amp, '&')
            .replace(pattern_lt, '<')
            .replace(pattern_gt, '>');

        switch (op) {
          case DIFF_INSERT:
            html[x] = '' + text + '';
            break;
          case DIFF_DELETE:
            html[x] = '' + text + '';
            break;
          case DIFF_EQUAL:
            html[x] = text;
            break;
        }
      }
      return html.join('');
    },

    highlightContentDiff: function (object1, object2) {
      this.clearHash();
      var content1 = this.html2plain ($(object1).html());
      var content2 = this.html2plain ($(object2).html());

      var diffs = this.dmp.diff_main(content1, content2);
      this.dmp.diff_cleanupSemantic(diffs);
      var html = this.makeHtml(diffs);

      return this.plain2html(html);
    }
  };
}
document.lorViewDiffHistory = new LorViewDiffHistory();

function diffHighlightOn(button) {
  document.prev_object = null;
  $("div.messages div.msg div.msg_body").each(
    function(ind, obj){
      var htmlContent = $(obj).html();
      $(obj).attr("oldHtml", htmlContent);
      htmlContent = htmlContent.replace(/\n/,'').trim();
      if (htmlContent == '') {
        return;
      }
      if (document.prev_object == null) {
        document.prev_object = obj;
        return;
      }
      var html = document.lorViewDiffHistory.highlightContentDiff(obj, document.prev_object);
      $(document.prev_object).html(html);
      document.prev_object = obj;
    }
  );
}

function diffHighlightOff(button) {
  document.prev_object = null;
  $("div.messages div.msg div.msg_body").each(
    function(ind, obj){
      $(obj).html($(obj).attr("oldHtml"));
    }
  );
}

function toggleDiffHighlight(obj) {
  var button = obj.currentTarget;
  var attr = $(button).attr("highlighted");
  if (attr == "on") {
    $(button)
      .val("Подсветить различия")
      .attr("highlighted", "off");
    diffHighlightOff(button);
  } else {
    $(button)
      .val("Убрать подсветку")
      .attr("highlighted", "on");
    diffHighlightOn(button);
  }
}

$(document).ready(function() {
  var button = $('<input type="button">')
    .val("Подсветить различия")
    .attr("highlighted", "off")
    .addClass("toggleDiffHighlight")
    .appendTo($("#historyButtonBar"))
    .click(function(obj){
      toggleDiffHighlight(obj)
    });
});
<|endoftext|>


<commit_before><commit_msg>better ordering<commit_after><|endoftext|>


<commit_before>//
// Strider Webapp extension for Sauce Labs tests
//

module.exports = function(ctx, cb) {
  /*
   * GET /api/sauce/
   *
   * Get the current Strider config for specified project. This will be a JSON-encoded
   * object with the keys: sauce_username, sauce_access_key and sauce_browsers
   *
   * @param url Github html_url of the project.
   */
  function getIndex(req, res) {
    var url = req.param("url")

    function error(err_msg) {
      console.error("Strider-Sauce: getIndex() - %s", err_msg)
      var r = {
        errors: [err_msg],
        status: "error"
      }
      res.statusCode = 400
      return res.end(JSON.stringify(r, null, '\t'))
    }

    req.user.get_repo_config(url, function(err, repo, access_level, owner_user_obj) {
      if (err) {
        return error("Error fetching Repo Config for url " + url + ": " + err)
      }
      repo = repo.toJSON()
      var r = {
        status: "ok",
        errors: [],
        results: {
          sauce_username: repo.sauce_username,
          sauce_access_key: repo.sauce_access_key,
          sauce_browsers: repo.sauce_browsers,
        }
      }
      return res.end(JSON.stringify(r, null, '\t'))
    })
  }

  /*
   * POST /api/sauce/
   *
   * Set the current Strider config for specified project.
   *
   * @param url Github html_url of the project.
   * @param sauce_username Sauce Labs username.
   * @param sauce_access_key Sauce Labs access key.
   * @param sauce_browsers JSON-encoded list of object tuples.
   *
   */
  function postIndex(req, res) {
    console.log("post index")
    var url = req.param("url")
    var sauce_username = req.param("sauce_username")
    var sauce_access_key = req.param("sauce_access_key")
    var sauce_browsers = req.param("sauce_browsers")

    function error(err_msg) {
      console.error("Strider-Sauce: postIndex() - %s", err_msg)
      var r = {
        errors: [err_msg],
        status: "error"
      }
      res.statusCode = 400
      return res.end(JSON.stringify(r, null, '\t'))
    }

    req.user.get_repo_config(url, function(err, repo, access_level, owner_user_obj) {
      if (err) {
        return error("Error fetching Repo Config for url " + url + ": " + err)
      }
      // must have access_level > 0 to be able to continue;
      if (access_level < 1) {
        console.debug(
          "User %s tried to change sauce config but doesn't have admin privileges on %s (access level: %s)",
          req.user.email, url, access_level);
        return error("You must have access level greater than 0 in order to be able to configure sauce.");
      }
      if (sauce_username) {
        repo.sauce_username = sauce_username
      }
      if (sauce_access_key) {
        repo.sauce_access_key = sauce_access_key
      }
      if (sauce_browsers) {
        try {
          sauce_browsers = JSON.parse(sauce_browsers)
        } catch(e) {
          return error("Error decoding `sauce_browsers` parameter - must be JSON-encoded array")
        }
        repo.sauce_browsers = sauce_browsers
      }
      ctx.models.User.update({"github_config.url":repo.url},
        {
          $set:{
            "github_config.$.sauce_username":repo.sauce_username,
            "github_config.$.sauce_access_key":repo.sauce_access_key,
            "github_config.$.sauce_browsers":repo.sauce_browsers,
          }
        }, function(err) {
          if (err) {
            var errmsg = "Error saving sauce config " + email + ": " + err;
            return error(errmsg)
          }
          return res.end(JSON.stringify(r, null, '\t'))
        })

    })

  }

  // Extend RepoConfig model with 'Sauce' properties
  function saucePlugin(schema, opts) {
    schema.add({
      sauce_access_key: String,
      sauce_username: String,
      sauce_browsers: [],
    })
  }
  ctx.models.RepoConfig.plugin(saucePlugin)

  // Add webserver routes
  ctx.route.get("/api/sauce",
    ctx.middleware.require_auth,
    ctx.middleware.require_params(["url"]),
    getIndex)
  ctx.route.post("/api/sauce",
    ctx.middleware.require_auth,
    ctx.middleware.require_params(["url"]),
    postIndex)

  console.log("strider-sauce webapp extension loaded")

  cb(null, null)
}
<commit_msg>spacing;<commit_after>//
// Strider Webapp extension for Sauce Labs tests
//

module.exports = function(ctx, cb) {
  /*
   * GET /api/sauce/
   *
   * Get the current Strider config for specified project. This will be a JSON-encoded
   * object with the keys: sauce_username, sauce_access_key and sauce_browsers
   *
   * @param url Github html_url of the project.
   */
  function getIndex(req, res) {
    var url = req.param("url")

    function error(err_msg) {
      console.error("Strider-Sauce: getIndex() - %s", err_msg)
      var r = {
        errors: [err_msg],
        status: "error"
      }
      res.statusCode = 400
      return res.end(JSON.stringify(r, null, '\t'))
    }

    req.user.get_repo_config(url, function(err, repo, access_level, owner_user_obj) {
      if (err) {
        return error("Error fetching Repo Config for url " + url + ": " + err)
      }
      repo = repo.toJSON()
      var r = {
        status: "ok",
        errors: [],
        results: {
          sauce_username: repo.sauce_username,
          sauce_access_key: repo.sauce_access_key,
          sauce_browsers: repo.sauce_browsers,
        }
      }
      return res.end(JSON.stringify(r, null, '\t'))
    })
  }

  /*
   * POST /api/sauce/
   *
   * Set the current Strider config for specified project.
   *
   * @param url Github html_url of the project.
   * @param sauce_username Sauce Labs username.
   * @param sauce_access_key Sauce Labs access key.
   * @param sauce_browsers JSON-encoded list of object tuples.
   *
   */
  function postIndex(req, res) {
    console.log("post index")
    var url = req.param("url")
    var sauce_username = req.param("sauce_username")
    var sauce_access_key = req.param("sauce_access_key")
    var sauce_browsers = req.param("sauce_browsers")

    function error(err_msg) {
      console.error("Strider-Sauce: postIndex() - %s", err_msg)
      var r = {
        errors: [err_msg],
        status: "error"
      }
      res.statusCode = 400
      return res.end(JSON.stringify(r, null, '\t'))
    }

    req.user.get_repo_config(url, function(err, repo, access_level, owner_user_obj) {
      if (err) {
        return error("Error fetching Repo Config for url " + url + ": " + err)
      }
      // must have access_level > 0 to be able to continue;
      if (access_level < 1) {
        console.debug(
          "User %s tried to change sauce config but doesn't have admin privileges on %s (access level: %s)",
          req.user.email, url, access_level);
        return error("You must have access level greater than 0 in order to be able to configure sauce.");
      }
      if (sauce_username) {
        repo.sauce_username = sauce_username
      }
      if (sauce_access_key) {
        repo.sauce_access_key = sauce_access_key
      }
      if (sauce_browsers) {
        try {
          sauce_browsers = JSON.parse(sauce_browsers)
        } catch(e) {
          return error("Error decoding `sauce_browsers` parameter - must be JSON-encoded array")
        }
        repo.sauce_browsers = sauce_browsers
      }
      ctx.models.User.update({"github_config.url":repo.url},
        {
          $set:{
            "github_config.$.sauce_username":repo.sauce_username,
            "github_config.$.sauce_access_key":repo.sauce_access_key,
            "github_config.$.sauce_browsers":repo.sauce_browsers,
          }
        }, function(err) {
          if (err) {
            var errmsg = "Error saving sauce config " + email + ": " + err;
            return error(errmsg)
          }
          return res.end(JSON.stringify(r, null, '\t'))
      })

    })

  }

  // Extend RepoConfig model with 'Sauce' properties
  function saucePlugin(schema, opts) {
    schema.add({
      sauce_access_key: String,
      sauce_username: String,
      sauce_browsers: [],
    })
  }
  ctx.models.RepoConfig.plugin(saucePlugin)

  // Add webserver routes
  ctx.route.get("/api/sauce",
    ctx.middleware.require_auth,
    ctx.middleware.require_params(["url"]),
    getIndex)
  ctx.route.post("/api/sauce",
    ctx.middleware.require_auth,
    ctx.middleware.require_params(["url"]),
    postIndex)

  console.log("strider-sauce webapp extension loaded")

  cb(null, null)
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import axios from 'axios';
import querystring from 'querystring';

import EditorActors from './EditorActors';

/* ----- COMPONENT ----- */

class Editor extends Component {
  constructor(props) {
    super(props);
    this.state = {
      textBody: '',
      nouns: [],
    }
    this.onSubmit = this.onSubmit.bind(this);
    this.onSceneTextChange = this.onSceneTextChange.bind(this);
    this.onGenerateActors = this.onGenerateActors.bind(this);
    this.handleActorsChange = this.handleActorsChange.bind(this);
  }
  onSubmit(event) {
    event.preventDefault();
    axios.post('/api/stories', {
      title: event.target.storyTitle.value,
      sceneText: this.state.textBody,
      actors: this.props.nouns
    })
      .then(newStory => {
        browserHistory.push(`/stories/${newStory.data.id}`)
      })
  }
  onSceneTextChange(event) {
    this.setState({ textBody: event.target.value });
  }
  onGenerateActors(event) {
    event.preventDefault();
    axios.post('/api/compromise/nouns', { text: this.state.textBody })
      .then(nouns => {
        this.setState({
          nouns: nouns.data
        })
      })
  }
  handleActorsChange(event) {
    const eventNameArray = event.target.name.split('-')
      , noun = eventNameArray[0]
      , type = eventNameArray[1]
      , actors = this.state.nouns;
    let index
      , actor;
    actors.forEach((a, i) => {
      if (a.title === noun) {
        index = i;
        actor = a;
      }
    })
    actor[type] = event.target.value;
    const newActors = actors.slice(0, index).concat(actor).concat(actors.slice(index + 1));
    this.setState({ nouns: newActors });
  }
  render() {
    console.log("this.state on Editor", this.state);
    return (

        <form onSubmit={this.onSubmit}>

              <input
                name="storyTitle"
                type="text"
                placeholder="Story Title"
                className="titleInput"
              />

                <button
                  className="btn btn-success"
                  type="submit"
                >
                  Publish My Story
                </button>

              <textarea
                rows="10"
                cols="78"
                type="text"
                className="form-control"
                placeholder="Scene"
                name="fullStory"
                onChange={this.onSceneTextChange}
              />

                <button
                  className="btn btn-default"
                  onClick={this.onGenerateActors}
                >
                  Generate Actors
                </button>

              <EditorActors actors={this.state.nouns} handleFormChange={this.handleActorsChange} />

        </form>

    )
  }
}

/* ----- CONTAINER ----- */

import { connect } from 'react-redux';
import { setCurrScene } from '../reducers/allState';
import { fetchNouns, setNouns } from '../reducers/analyze';
import { addStory } from '../reducers/setText';

const mapStateToProps = (store, ownProps) => {
  return {
    nouns: store.analyze.nouns
  };
};

function mapDispatchToProps(dispatch) {
  return {
    parseNouns: (input) => {
      dispatch(fetchNouns(input));
    },
    setStory: (input) => {
      dispatch(addStory(input));
    },
    setCurrScene: (scene) => {
      dispatch(setCurrScene(scene))
    }
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Editor);
<commit_msg>fixes publish to account for new actor forms<commit_after>import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import axios from 'axios';
import querystring from 'querystring';

import EditorActors from './EditorActors';

/* ----- COMPONENT ----- */

class Editor extends Component {
  constructor(props) {
    super(props);
    this.state = {
      textBody: '',
      nouns: [],
    }
    this.onSubmit = this.onSubmit.bind(this);
    this.onSceneTextChange = this.onSceneTextChange.bind(this);
    this.onGenerateActors = this.onGenerateActors.bind(this);
    this.handleActorsChange = this.handleActorsChange.bind(this);
  }
  onSubmit(event) {
    event.preventDefault();
    axios.post('/api/stories', {
      title: event.target.storyTitle.value,
      sceneText: this.state.textBody,
      actors: this.state.nouns
    })
      .then(newStory => {
        browserHistory.push(`/stories/${newStory.data.id}`)
      })
  }
  onSceneTextChange(event) {
    this.setState({ textBody: event.target.value });
  }
  onGenerateActors(event) {
    event.preventDefault();
    axios.post('/api/compromise/nouns', { text: this.state.textBody })
      .then(nouns => {
        this.setState({
          nouns: nouns.data
        })
      })
  }
  handleActorsChange(event) {
    const eventNameArray = event.target.name.split('-')
      , noun = eventNameArray[0]
      , type = eventNameArray[1]
      , actors = this.state.nouns;
    let index
      , actor;
    actors.forEach((a, i) => {
      if (a.title === noun) {
        index = i;
        actor = a;
      }
    })
    actor[type] = event.target.value;
    const newActors = actors.slice(0, index).concat(actor).concat(actors.slice(index + 1));
    this.setState({ nouns: newActors });
  }
  render() {
    console.log("this.state on Editor", this.state);
    return (

        <form onSubmit={this.onSubmit}>

              <input
                name="storyTitle"
                type="text"
                placeholder="Story Title"
                className="titleInput"
              />

                <button
                  className="btn btn-success"
                  type="submit"
                >
                  Publish My Story
                </button>

              <textarea
                rows="10"
                cols="78"
                type="text"
                className="form-control"
                placeholder="Scene"
                name="fullStory"
                onChange={this.onSceneTextChange}
              />

                <button
                  className="btn btn-default"
                  onClick={this.onGenerateActors}
                >
                  Generate Actors
                </button>

              <EditorActors actors={this.state.nouns} handleFormChange={this.handleActorsChange} />

        </form>

    )
  }
}

/* ----- CONTAINER ----- */

import { connect } from 'react-redux';
import { setCurrScene } from '../reducers/allState';
import { fetchNouns, setNouns } from '../reducers/analyze';
import { addStory } from '../reducers/setText';

const mapStateToProps = (store, ownProps) => {
  return {
    nouns: store.analyze.nouns
  };
};

function mapDispatchToProps(dispatch) {
  return {
    parseNouns: (input) => {
      dispatch(fetchNouns(input));
    },
    setStory: (input) => {
      dispatch(addStory(input));
    },
    setCurrScene: (scene) => {
      dispatch(setCurrScene(scene))
    }
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Editor);
<|endoftext|>


<commit_before>'use strict';
var fs = require('fs');
var _path = require('path');

var mkdirp = require('mkdirp');
var ncp = require('ncp');
var webpack = require('webpack');
var webpackConfig = require('./webpack.coffee').build;

exports.buildDevIndex = function(config) {
  process.env.NODE_ENV = 'dev';

  webpack(webpackConfig(config), function(err) {
    if(err) {
      return console.error(err);
    }

    var cwd = process.cwd();
    var page = require(_path.join(cwd, './.antwar/build/bundleStaticPage.js'));

    fs.writeFileSync(
      _path.join(cwd, './.antwar/build/index.html'),
      page,
      '/antwar_devindex',
      null
    );

    ncp('./assets', _path.join(cwd, './.antwar/build/assets'));
  });
};

exports.build = function(config) {
  process.env.NODE_ENV = 'production';

  webpack(webpackConfig(config), function(err) {
    if(err) {
      return console.error(err);
    }

    var cwd = process.cwd();

    var assets = _path.join(cwd, './public/assets');
    var renderPage = require(_path.join(cwd, './.antwar/build/bundleStaticPage.js'));
    var paths = require(_path.join(cwd, './.antwar/build/paths.js'));

    mkdirp.sync(assets);
    ncp(_path.join(cwd, './assets'), assets);

    fs.writeFileSync(
      _path.join(assets, '/main.css'),
      fs.readFileSync('./.antwar/build/main.css')
    );

    mkdirp.sync(_path.join(cwd, './public/blog'));

    var allPaths = paths();
    writePages(cwd, renderPage, allPaths);
    writeIndex(cwd, renderPage);
    writePosts(cwd, renderPage, allPaths);

    // TODO: this should be pushed to a plugin
    //var rss = require(_path.join(cwd, './.antwar/build/bundleStaticRss.js'));
    //fs.writeFileSync(_path.join(process.cwd(), './public/atom.xml'), rss(renderPage));
  });
};

function writePages(root, renderPage, allPaths) {
  Object.keys(allPaths).forEach(function(path) {
    if(path !== 'posts') {
      var publicPath;

      if(path === '/') {
        path = '';

        publicPath = _path.join(root, './public');
      }
      else {
        publicPath = _path.join(root, './public/#' + path);

        mkdirp.sync(publicPath);
      }

      fs.writeFileSync(
        _path.join(publicPath, 'index.html'),
        renderPage('/#' + path, null)
      );
    }
  });
}

function writeIndex(root, renderPage) {
  fs.writeFileSync(
    _path.join(root, './public/blog/index.html'),
    renderPage('/blog', null)
  );
}

function writePosts(root, renderPage, allPaths) {
  Object.keys(allPaths.posts).forEach(function(post) {
    fs.writeFileSync(
      _path.join(root, './public/blog/' + post + '.html'),
      renderPage('/blog' + post)
    );
  });
}
<commit_msg>Tidy up<commit_after>'use strict';
var fs = require('fs');
var _path = require('path');

var mkdirp = require('mkdirp');
var ncp = require('ncp');
var webpack = require('webpack');
var webpackConfig = require('./webpack.coffee').build;

exports.buildDevIndex = function(config) {
  process.env.NODE_ENV = 'dev';

  webpack(webpackConfig(config), function(err) {
    if(err) {
      return console.error(err);
    }

    var cwd = process.cwd();
    var page = require(_path.join(cwd, './.antwar/build/bundleStaticPage.js'));

    fs.writeFileSync(
      _path.join(cwd, './.antwar/build/index.html'),
      page,
      '/antwar_devindex',
      null
    );

    ncp('./assets', _path.join(cwd, './.antwar/build/assets'));
  });
};

exports.build = function(config) {
  process.env.NODE_ENV = 'production';

  webpack(webpackConfig(config), function(err) {
    if(err) {
      return console.error(err);
    }

    var cwd = process.cwd();

    var assets = _path.join(cwd, './public/assets');
    var renderPage = require(_path.join(cwd, './.antwar/build/bundleStaticPage.js'));
    var paths = require(_path.join(cwd, './.antwar/build/paths.js'));

    mkdirp.sync(_path.join(cwd, './public/blog'));
    mkdirp.sync(assets);

    ncp(_path.join(cwd, './assets'), assets);

    fs.writeFileSync(
      _path.join(assets, '/main.css'),
      fs.readFileSync('./.antwar/build/main.css')
    );

    var allPaths = paths();
    writePages(cwd, renderPage, allPaths);
    writeIndex(cwd, renderPage);
    writePosts(cwd, renderPage, allPaths);

    // TODO: this should be pushed to a plugin
    //var rss = require(_path.join(cwd, './.antwar/build/bundleStaticRss.js'));
    //fs.writeFileSync(_path.join(process.cwd(), './public/atom.xml'), rss(renderPage));
  });
};

function writePages(root, renderPage, allPaths) {
  Object.keys(allPaths).forEach(function(path) {
    if(path !== 'posts') {
      var publicPath;

      if(path === '/') {
        path = '';

        publicPath = _path.join(root, './public');
      }
      else {
        publicPath = _path.join(root, './public/#' + path);

        mkdirp.sync(publicPath);
      }

      fs.writeFileSync(
        _path.join(publicPath, 'index.html'),
        renderPage('/#' + path, null)
      );
    }
  });
}

function writeIndex(root, renderPage) {
  fs.writeFileSync(
    _path.join(root, './public/blog/index.html'),
    renderPage('/blog', null)
  );
}

function writePosts(root, renderPage, allPaths) {
  Object.keys(allPaths.posts).forEach(function(post) {
    fs.writeFileSync(
      _path.join(root, './public/blog/' + post + '.html'),
      renderPage('/blog' + post)
    );
  });
}
<|endoftext|>


<commit_before>require(`dotenv`).config({
  path: `.env.${process.env.NODE_ENV}`,
})

const GA = {
  identifier: `UA-93349937-5`,
  viewId: `176383508`,
}

const dynamicPlugins = []
if (process.env.ANALYTICS_SERVICE_ACCOUNT) {
  // pick data from 3 months ago
  const startDate = new Date()
  // temporary lower guess to use 2 days of data to lower guess data
  // real fix is to move gatsby-plugin-guess to aot mode
  // startDate.setMonth(startDate.getMonth() - 3)
  startDate.setDate(startDate.getDate() - 2)
  dynamicPlugins.push({
    resolve: `gatsby-plugin-guess-js`,
    options: {
      GAViewID: GA.viewId,
      jwt: {
        client_email: process.env.ANALYTICS_SERVICE_ACCOUNT,
        // replace \n characters in real new lines for circleci deploys
        private_key: process.env.ANALYTICS_SERVICE_ACCOUNT_KEY.replace(
          /\\n/g,
          `\n`
        ),
      },
      period: {
        startDate,
        endDate: new Date(),
      },
    },
  })
}

if (process.env.AIRTABLE_API_KEY) {
  dynamicPlugins.push({
    resolve: `gatsby-source-airtable`,
    options: {
      apiKey: process.env.AIRTABLE_API_KEY,
      tables: [
        {
          baseId: `app0q5U0xkEwZaT9c`,
          tableName: `Community Events Submitted`,
          queryName: `CommunityEvents`,
        },
      ],
    },
  })
}

if (!process.env.DISABLE_SOURCE_DOCS) {
  dynamicPlugins.push({
    resolve: `gatsby-source-filesystem`,
    options: {
      name: `docs`,
      path: `${__dirname}/../docs/`,
    },
  })
  // The `packages` directory is only used for API definitions,
  // which are part of the docs.
  dynamicPlugins.push({
    resolve: `gatsby-source-filesystem`,
    options: {
      name: `gatsby-core`,
      path: `${__dirname}/../packages/gatsby/`,
      ignore: [`**/dist/**`],
    },
  })
}

module.exports = {
  siteMetadata: {
    title: `GatsbyJS`,
    siteUrl: `https://www.gatsbyjs.org`,
    description: `Blazing fast modern site generator for React`,
    twitter: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-theme-ui`,
    {
      resolve: `gatsby-alias-imports`,
      options: {
        aliases: {
          // Relative paths when importing components from MDX break translations of the docs,
          // so use an alias instead inside MDX:
          // https://www.gatsbyjs.org/contributing/docs-and-blog-components/#importing-other-components
          "@components": `${__dirname}/src/components`,
        },
      },
    },
    {
      resolve: `gatsby-source-npm-package-search`,
      options: {
        // If DISABLE_NPM_SEARCH is true, search for a placeholder keyword
        // that returns a lot fewer packages
        // (In this case, stuff that Lennart has published)
        keywords: process.env.DISABLE_NPM_SEARCH
          ? [`lekoarts`]
          : [`gatsby-plugin`, `gatsby-component`],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `guidelines`,
        path: `${__dirname}/src/data/guidelines/`,
      },
    },
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `${__dirname}/src/utils/typography`,
      },
    },
    `gatsby-transformer-documentationjs`,
    `gatsby-transformer-yaml`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data/diagram`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/assets`,
      },
    },
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.md`, `.mdx`],
        shouldBlockNodeFromTransformation(node) {
          return (
            [`NPMPackage`, `NPMPackageReadme`].includes(node.internal.type) ||
            (node.internal.type === `File` &&
              node.sourceInstanceName === `gatsby-core`)
          )
        },
        gatsbyRemarkPlugins: [
          `gatsby-remark-embedder`,
          `gatsby-remark-graphviz`,
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 786,
              backgroundColor: `#ffffff`,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.5rem`,
            },
          },
          {
            resolve: `gatsby-remark-autolink-headers`,
            options: {
              offsetY: 104,
            },
          },
          `gatsby-remark-copy-linked-files`,
          `gatsby-remark-smartypants`,
        ],
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          `gatsby-remark-embedder`,
          `gatsby-remark-graphviz`,
          `gatsby-remark-code-titles`,
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 786,
              backgroundColor: `#ffffff`,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.5rem`,
            },
          },
          {
            resolve: `gatsby-remark-autolink-headers`,
            options: {
              offsetY: 104,
            },
          },
          {
            resolve: `gatsby-remark-prismjs`,
            options: {
              aliases: {
                dosini: `ini`,
                env: `bash`,
                es6: `js`,
                flowchart: `none`,
                gitignore: `none`,
                gql: `graphql`,
                htaccess: `apacheconf`,
                mdx: `markdown`,
                ml: `fsharp`,
                styl: `stylus`,
              },
            },
          },
          `gatsby-remark-copy-linked-files`,
          `gatsby-remark-smartypants`,
          // convert images using http to https in plugin library READMEs
          `gatsby-remark-http-to-https`,
        ],
      },
    },
    {
      resolve: `gatsby-plugin-nprogress`,
      options: {
        color: `#9D7CBF`,
        showSpinner: false,
      },
    },
    `gatsby-plugin-emotion`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    `gatsby-plugin-catch-links`,
    `gatsby-plugin-layout`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `GatsbyJS`,
        short_name: `GatsbyJS`,
        start_url: `/`,
        background_color: `#ffffff`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `${__dirname}/src/assets/gatsby-icon.png`,
      },
    },
    `gatsby-plugin-offline`,
    `gatsby-transformer-csv`,
    `gatsby-plugin-twitter`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-sitemap`,
    {
      resolve: `gatsby-plugin-react-svg`,
      options: {
        rule: {
          include: /assets\/(guidelines|icons|ornaments)\/.*.svg$/,
        },
      },
    },
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: GA.identifier,
        anonymize: true,
        allowLinker: true,
      },
    },
    {
      resolve: `gatsby-plugin-feed`,
      options: {
        feeds: [
          {
            title: `GatsbyJS`,
            query: `
              {
                allMdx(
                  sort: { order: DESC, fields: [frontmatter___date] }
                  limit: 10,
                  filter: {
                    fields: { section: { eq: "blog" }, released: { eq: true } }
                  }
                ) {
                  nodes {
                    html
                    frontmatter {
                      title
                      date
                      author {
                        id
                      }
                    }
                    fields {
                      excerpt
                      slug
                    }
                  }
                }
              }
            `,
            output: `/blog/rss.xml`,
            setup: ({
              query: {
                site: { siteMetadata },
              },
            }) => {
              return {
                title: siteMetadata.title,
                description: siteMetadata.description,
                feed_url: siteMetadata.siteUrl + `/blog/rss.xml`,
                site_url: siteMetadata.siteUrl,
                generator: `GatsbyJS`,
              }
            },
            serialize: ({ query: { site, allMdx } }) =>
              allMdx.nodes.map(node => {
                return {
                  title: node.frontmatter.title,
                  description: node.fields.excerpt,
                  url: site.siteMetadata.siteUrl + node.fields.slug,
                  guid: site.siteMetadata.siteUrl + node.fields.slug,
                  custom_elements: [{ "content:encoded": node.html }],
                  author: node.frontmatter.author.id,
                  date: node.frontmatter.date,
                }
              }),
          },
        ],
      },
    },
    {
      resolve: `gatsby-plugin-netlify`,
      options: {
        headers: {
          "/*": [`Referrer-Policy: strict-origin-when-cross-origin`],
        },
      },
    },
    `gatsby-plugin-netlify-cache`,
    {
      resolve: `gatsby-plugin-mailchimp`,
      options: {
        endpoint: `https://gatsbyjs.us17.list-manage.com/subscribe/post?u=1dc33f19eb115f7ebe4afe5ee&id=f366064ba7`,
      },
    },
    {
      resolve: `gatsby-transformer-screenshot`,
      options: {
        nodeTypes: [`StartersYaml`],
      },
    },
    // `gatsby-plugin-subfont`,
  ].concat(dynamicPlugins),
}
<commit_msg>chore(www): fix regex in gatsby-config (#25765)<commit_after>require(`dotenv`).config({
  path: `.env.${process.env.NODE_ENV}`,
})

const GA = {
  identifier: `UA-93349937-5`,
  viewId: `176383508`,
}

const dynamicPlugins = []
if (process.env.ANALYTICS_SERVICE_ACCOUNT) {
  // pick data from 3 months ago
  const startDate = new Date()
  // temporary lower guess to use 2 days of data to lower guess data
  // real fix is to move gatsby-plugin-guess to aot mode
  // startDate.setMonth(startDate.getMonth() - 3)
  startDate.setDate(startDate.getDate() - 2)
  dynamicPlugins.push({
    resolve: `gatsby-plugin-guess-js`,
    options: {
      GAViewID: GA.viewId,
      jwt: {
        client_email: process.env.ANALYTICS_SERVICE_ACCOUNT,
        // replace \n characters in real new lines for circleci deploys
        private_key: process.env.ANALYTICS_SERVICE_ACCOUNT_KEY.replace(
          /\\n/g,
          `\n`
        ),
      },
      period: {
        startDate,
        endDate: new Date(),
      },
    },
  })
}

if (process.env.AIRTABLE_API_KEY) {
  dynamicPlugins.push({
    resolve: `gatsby-source-airtable`,
    options: {
      apiKey: process.env.AIRTABLE_API_KEY,
      tables: [
        {
          baseId: `app0q5U0xkEwZaT9c`,
          tableName: `Community Events Submitted`,
          queryName: `CommunityEvents`,
        },
      ],
    },
  })
}

if (!process.env.DISABLE_SOURCE_DOCS) {
  dynamicPlugins.push({
    resolve: `gatsby-source-filesystem`,
    options: {
      name: `docs`,
      path: `${__dirname}/../docs/`,
    },
  })
  // The `packages` directory is only used for API definitions,
  // which are part of the docs.
  dynamicPlugins.push({
    resolve: `gatsby-source-filesystem`,
    options: {
      name: `gatsby-core`,
      path: `${__dirname}/../packages/gatsby/`,
      ignore: [`**/dist/**`],
    },
  })
}

module.exports = {
  siteMetadata: {
    title: `GatsbyJS`,
    siteUrl: `https://www.gatsbyjs.org`,
    description: `Blazing fast modern site generator for React`,
    twitter: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-theme-ui`,
    {
      resolve: `gatsby-alias-imports`,
      options: {
        aliases: {
          // Relative paths when importing components from MDX break translations of the docs,
          // so use an alias instead inside MDX:
          // https://www.gatsbyjs.org/contributing/docs-and-blog-components/#importing-other-components
          "@components": `${__dirname}/src/components`,
        },
      },
    },
    {
      resolve: `gatsby-source-npm-package-search`,
      options: {
        // If DISABLE_NPM_SEARCH is true, search for a placeholder keyword
        // that returns a lot fewer packages
        // (In this case, stuff that Lennart has published)
        keywords: process.env.DISABLE_NPM_SEARCH
          ? [`lekoarts`]
          : [`gatsby-plugin`, `gatsby-component`],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `guidelines`,
        path: `${__dirname}/src/data/guidelines/`,
      },
    },
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `${__dirname}/src/utils/typography`,
      },
    },
    `gatsby-transformer-documentationjs`,
    `gatsby-transformer-yaml`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data/diagram`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/assets`,
      },
    },
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.md`, `.mdx`],
        shouldBlockNodeFromTransformation(node) {
          return (
            [`NPMPackage`, `NPMPackageReadme`].includes(node.internal.type) ||
            (node.internal.type === `File` &&
              node.sourceInstanceName === `gatsby-core`)
          )
        },
        gatsbyRemarkPlugins: [
          `gatsby-remark-embedder`,
          `gatsby-remark-graphviz`,
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 786,
              backgroundColor: `#ffffff`,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.5rem`,
            },
          },
          {
            resolve: `gatsby-remark-autolink-headers`,
            options: {
              offsetY: 104,
            },
          },
          `gatsby-remark-copy-linked-files`,
          `gatsby-remark-smartypants`,
        ],
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          `gatsby-remark-embedder`,
          `gatsby-remark-graphviz`,
          `gatsby-remark-code-titles`,
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 786,
              backgroundColor: `#ffffff`,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.5rem`,
            },
          },
          {
            resolve: `gatsby-remark-autolink-headers`,
            options: {
              offsetY: 104,
            },
          },
          {
            resolve: `gatsby-remark-prismjs`,
            options: {
              aliases: {
                dosini: `ini`,
                env: `bash`,
                es6: `js`,
                flowchart: `none`,
                gitignore: `none`,
                gql: `graphql`,
                htaccess: `apacheconf`,
                mdx: `markdown`,
                ml: `fsharp`,
                styl: `stylus`,
              },
            },
          },
          `gatsby-remark-copy-linked-files`,
          `gatsby-remark-smartypants`,
          // convert images using http to https in plugin library READMEs
          `gatsby-remark-http-to-https`,
        ],
      },
    },
    {
      resolve: `gatsby-plugin-nprogress`,
      options: {
        color: `#9D7CBF`,
        showSpinner: false,
      },
    },
    `gatsby-plugin-emotion`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    `gatsby-plugin-catch-links`,
    `gatsby-plugin-layout`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `GatsbyJS`,
        short_name: `GatsbyJS`,
        start_url: `/`,
        background_color: `#ffffff`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `${__dirname}/src/assets/gatsby-icon.png`,
      },
    },
    `gatsby-plugin-offline`,
    `gatsby-transformer-csv`,
    `gatsby-plugin-twitter`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-sitemap`,
    {
      resolve: `gatsby-plugin-react-svg`,
      options: {
        rule: {
          include: /assets\/(guidelines|icons|ornaments)\/.*\.svg$/,
        },
      },
    },
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: GA.identifier,
        anonymize: true,
        allowLinker: true,
      },
    },
    {
      resolve: `gatsby-plugin-feed`,
      options: {
        feeds: [
          {
            title: `GatsbyJS`,
            query: `
              {
                allMdx(
                  sort: { order: DESC, fields: [frontmatter___date] }
                  limit: 10,
                  filter: {
                    fields: { section: { eq: "blog" }, released: { eq: true } }
                  }
                ) {
                  nodes {
                    html
                    frontmatter {
                      title
                      date
                      author {
                        id
                      }
                    }
                    fields {
                      excerpt
                      slug
                    }
                  }
                }
              }
            `,
            output: `/blog/rss.xml`,
            setup: ({
              query: {
                site: { siteMetadata },
              },
            }) => {
              return {
                title: siteMetadata.title,
                description: siteMetadata.description,
                feed_url: siteMetadata.siteUrl + `/blog/rss.xml`,
                site_url: siteMetadata.siteUrl,
                generator: `GatsbyJS`,
              }
            },
            serialize: ({ query: { site, allMdx } }) =>
              allMdx.nodes.map(node => {
                return {
                  title: node.frontmatter.title,
                  description: node.fields.excerpt,
                  url: site.siteMetadata.siteUrl + node.fields.slug,
                  guid: site.siteMetadata.siteUrl + node.fields.slug,
                  custom_elements: [{ "content:encoded": node.html }],
                  author: node.frontmatter.author.id,
                  date: node.frontmatter.date,
                }
              }),
          },
        ],
      },
    },
    {
      resolve: `gatsby-plugin-netlify`,
      options: {
        headers: {
          "/*": [`Referrer-Policy: strict-origin-when-cross-origin`],
        },
      },
    },
    `gatsby-plugin-netlify-cache`,
    {
      resolve: `gatsby-plugin-mailchimp`,
      options: {
        endpoint: `https://gatsbyjs.us17.list-manage.com/subscribe/post?u=1dc33f19eb115f7ebe4afe5ee&id=f366064ba7`,
      },
    },
    {
      resolve: `gatsby-transformer-screenshot`,
      options: {
        nodeTypes: [`StartersYaml`],
      },
    },
    // `gatsby-plugin-subfont`,
  ].concat(dynamicPlugins),
}
<|endoftext|>